Make all project targets compile on Intel MacOS (#2780)

This commit is contained in:
Fabian Bergström 2023-07-01 19:30:11 +02:00 committed by GitHub
parent 98bb40d618
commit cf295952b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 184 additions and 82 deletions

View File

@ -45,18 +45,8 @@ jobs:
-DCMAKE_C_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache \
-DCMAKE_CXX_COMPILER_LAUNCHER=${{ github.workspace }}/buildcache/bin/buildcache
# Disabled for now, not all build targets are valid
# - name: Build Project
# run: cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
# Temporary, selectively build those that work
- name: Build Working Targets
run: |
cmake --build build --target extractor --parallel $((`sysctl -n hw.logicalcpu`)) && \
cmake --build build --target offline-test --parallel $((`sysctl -n hw.logicalcpu`)) && \
cmake --build build --target decompiler --parallel $((`sysctl -n hw.logicalcpu`)) && \
cmake --build build --target lsp --parallel $((`sysctl -n hw.logicalcpu`)) && \
cmake --build build --target goalc --parallel $((`sysctl -n hw.logicalcpu`))
- name: Build Project
run: cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
- name: Run Tests
continue-on-error: true # until macOS is stable

View File

@ -26,6 +26,9 @@
- [Windows](#windows)
- [Required Software](#required-software)
- [Using Visual Studio](#using-visual-studio)
- [MacOS](#macos)
- [Intel Based](#intel-based)
- [Apple Silicon](#apple-silicon)
- [Building and Running the Game](#building-and-running-the-game)
- [Extract Assets](#extract-assets)
- [Build the Game](#build-the-game)
@ -234,6 +237,29 @@ Then build the entire project as `Windows Release (clang)`. You can also press C
![](./docs/img/windows/release-build.png)
![](./docs/img/windows/build-all.png)
### MacOS
> NOTE: At this time you still cannot run the game on macOS due to OpenGL version limitations. But you can build the project and use most of the tooling.
Ensure that you have Xcode command line tools installed (this installs things like Apple Clang). If you don't, you can run the following command:
```bash
xcode-select install
```
#### Intel Based
```bash
brew install go-task/tap/go-task
brew install cmake nasm ninja go-task
cmake -B build --preset=Release-macos-clang
cmake --build build --parallel $((`sysctl -n hw.logicalcpu`))
```
#### Apple Silicon
**Not Supported at This Time**
### Building and Running the Game
Getting a running game involves 4 steps:

View File

@ -689,9 +689,16 @@ std::string ThreadID::to_string() const {
return "invalid";
}
ThreadID get_current_thread_id();
ThreadID get_current_thread_id() {
return ThreadID("not implemented on macOS");
}
bool attach_and_break(const ThreadID& tid);
void allow_debugging();
void allow_debugging() {
printf("allow_debugging not implemented on macOS\n");
}
bool detach_and_resume(const ThreadID& tid) {
return false;
}

View File

@ -42,19 +42,26 @@ bool XSocketServer::init_server() {
return false;
}
#ifdef OS_POSIX
int server_socket_opt = SO_REUSEADDR | SO_REUSEPORT;
#elif _WIN32
int server_socket_opt = SO_EXCLUSIVEADDRUSE;
#endif
int yes = 1;
int opt = 1;
if (set_socket_option(listening_socket, SOL_SOCKET, server_socket_opt, &opt, sizeof(opt)) < 0) {
#ifdef OS_POSIX
if (set_socket_option(listening_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
close_server_socket();
return false;
}
// macOS doesn't support setting multiple options at once, so we have to do this separately.
if (set_socket_option(listening_socket, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) {
close_server_socket();
return false;
}
#elif _WIN32
if (set_socket_option(listening_socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, &yes, sizeof(yes)) < 0) {
close_server_socket();
return false;
};
#endif
if (set_socket_option(listening_socket, TCP_SOCKET_LEVEL, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
if (set_socket_option(listening_socket, TCP_SOCKET_LEVEL, TCP_NODELAY, &yes, sizeof(yes)) < 0) {
close_server_socket();
return false;
}

View File

@ -38,6 +38,13 @@
#include "common/log/log.h"
#include "common/util/Assert.h"
#ifdef __APPLE__
#include <crt_externs.h>
#include <limits.h>
#include <mach-o/dyld.h>
#endif
namespace file_util {
fs::path get_user_home_dir() {
#ifdef _WIN32
@ -64,6 +71,9 @@ fs::path get_user_config_dir() {
} else {
config_base_path = fs::path(config_base_dir);
}
#elif __APPLE__
auto config_base_dir = get_env("HOME");
config_base_path = fs::path(config_base_dir) / "Library" / "Application Support";
#endif
return config_base_path / "OpenGOAL";
}
@ -101,14 +111,22 @@ std::string get_current_executable_path() {
return file_path.substr(4);
}
return file_path;
#else
// do Linux stuff
#elif __linux
char buffer[FILENAME_MAX + 1];
auto len = readlink("/proc/self/exe", buffer,
FILENAME_MAX); // /proc/self acts like a "virtual folder" containing
// information about the current process
buffer[len] = '\0';
return std::string(buffer);
#elif __APPLE__
char buffer[PATH_MAX];
uint32_t bufsize = sizeof(buffer);
if (_NSGetExecutablePath(buffer, &bufsize) != 0) {
lg::warn("Could not get executable path, trying with _NSGetArgv()[0] instead.");
auto argv = *_NSGetArgv();
return std::string(argv[0]);
}
return std::string(buffer);
#endif
}

View File

@ -28,6 +28,8 @@ enum class Mask {
#ifdef __linux__
#define REALLY_INLINE __attribute__((always_inline))
#elif __APPLE__
#define REALLY_INLINE __attribute__((always_inline))
#else
#define REALLY_INLINE __forceinline
#endif

View File

@ -6,9 +6,9 @@
SECTION .text
;; Call C++ code on linux, from GOAL, using Linux calling convention.
global _arg_call_linux
_arg_call_linux:
;; Call C++ code on unix systems, from GOAL, using System V calling convention.
global _arg_call_systemv
_arg_call_systemv:
pop rax
push r10
push r11
@ -43,12 +43,12 @@ _arg_call_linux:
ret
;; Call C++ code on linux, from GOAL. Pug arguments on the stack and put a pointer to this array in the first arg.
;; Call C++ code on unix systems, from GOAL. Pug arguments on the stack and put a pointer to this array in the first arg.
;; this function pushes all 8 OpenGOAL registers into a stack array.
;; then it calls the function pointed to by rax with a pointer to this array.
;; it returns the return value of the called function.
global _stack_call_linux
_stack_call_linux:
global _stack_call_systemv
_stack_call_systemv:
pop rax
; align stack
sub rsp, 8
@ -105,8 +105,8 @@ _stack_call_linux:
;; Call c++ code through mips2c.
;; GOAL will call a dynamically generated trampoline.
;; The trampoline will have pushed the exec function and stack offset onto the stack
global _mips2c_call_linux
_mips2c_call_linux:
global _mips2c_call_systemv
_mips2c_call_systemv:
;; grab the address to call and put it in xmm0
sub rsp, 8
movaps xmm0, [rsp + 16]
@ -323,9 +323,9 @@ _stack_call_win32:
;; - address of the symbol table
;; - GOAL memory space offset
global _call_goal_asm_linux
global _call_goal_asm_systemv
_call_goal_asm_linux:
_call_goal_asm_systemv:
;; x86 saved registers we need to modify for GOAL should be saved
push r13
push r14
@ -353,9 +353,9 @@ _call_goal_asm_linux:
pop r13
ret
global _call_goal8_asm_linux
global _call_goal8_asm_systemv
_call_goal8_asm_linux:
_call_goal8_asm_systemv:
;; x86 saved registers we need to modify for GOAL should be saved
push r13
push r14
@ -395,9 +395,9 @@ _call_goal8_asm_linux:
ret
;; Call goal, but switch stacks.
global _call_goal_on_stack_asm_linux
global _call_goal_on_stack_asm_systemv
_call_goal_on_stack_asm_linux:
_call_goal_on_stack_asm_systemv:
;; RDI - stack pointer
;; RSI - unused
;; RDX - unused
@ -436,7 +436,6 @@ _call_goal_on_stack_asm_linux:
pop r13
ret
;; The _call_goal_asm function is used to call a GOAL function from C.
;; It supports up to 3 arguments and a return value.
;; This should be called with the arguments:

View File

@ -85,13 +85,22 @@ u64 goal_malloc(u32 heap, u32 size, u32 flags, u32 name) {
extern "C" {
// defined in asm_funcs.asm
#ifdef __linux__
uint64_t _call_goal_asm_linux(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset);
uint64_t _call_goal_on_stack_asm_linux(u64 rsp,
u64 u0,
u64 u1,
void* fptr,
void* st_ptr,
void* offset);
uint64_t _call_goal_asm_systemv(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset);
uint64_t _call_goal_on_stack_asm_systemv(u64 rsp,
u64 u0,
u64 u1,
void* fptr,
void* st_ptr,
void* offset);
#elif defined __APPLE__ && defined __x86_64__
uint64_t _call_goal_asm_systemv(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset) asm(
"_call_goal_asm_systemv");
uint64_t _call_goal_on_stack_asm_systemv(u64 rsp,
u64 u0,
u64 u1,
void* fptr,
void* st_ptr,
void* offset) asm("_call_goal_on_stack_asm_systemv");
#elif _WIN32
uint64_t _call_goal_asm_win32(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset);
uint64_t _call_goal_on_stack_asm_win32(u64 rsp, void* fptr, void* st_ptr, void* offset);
@ -108,7 +117,9 @@ u64 call_goal(Ptr<Function> f, u64 a, u64 b, u64 c, u64 st, void* offset) {
void* fptr = f.c();
#ifdef __linux__
return _call_goal_asm_linux(a, b, c, fptr, st_ptr, offset);
return _call_goal_asm_systemv(a, b, c, fptr, st_ptr, offset);
#elif defined __APPLE__ && defined __x86_64__
return _call_goal_asm_systemv(a, b, c, fptr, st_ptr, offset);
#elif _WIN32
return _call_goal_asm_win32(a, b, c, fptr, st_ptr, offset);
#endif
@ -122,7 +133,9 @@ u64 call_goal_on_stack(Ptr<Function> f, u64 rsp, u64 st, void* offset) {
void* fptr = f.c();
#ifdef __linux__
return _call_goal_on_stack_asm_linux(rsp, 0, 0, fptr, st_ptr, offset);
return _call_goal_on_stack_asm_systemv(rsp, 0, 0, fptr, st_ptr, offset);
#elif defined __APPLE__ && defined __x86_64__
return _call_goal_on_stack_asm_systemv(rsp, 0, 0, fptr, st_ptr, offset);
#elif _WIN32
return _call_goal_on_stack_asm_win32(rsp, fptr, st_ptr, offset);
#endif
@ -286,4 +299,4 @@ u64 inspect_vu_function(u32 obj) {
u64 inspect_kheap(u32 obj) {
kheapstatus(Ptr<kheapinfo>(obj));
return obj;
}
}

View File

@ -264,19 +264,23 @@ u64 make_string_from_c(const char* c_str) {
}
extern "C" {
void _arg_call_linux();
#ifdef __APPLE__
void _arg_call_systemv() asm("_arg_call_systemv");
#else
void _arg_call_systemv();
#endif
}
/*!
* This creates an OpenGOAL function from a C++ function. Only 6 arguments can be accepted.
* But calling this function is fast. It used to be really fast but wrong.
*/
Ptr<Function> make_function_from_c_linux(void* func, bool arg3_is_pp) {
Ptr<Function> make_function_from_c_systemv(void* func, bool arg3_is_pp) {
auto mem = Ptr<u8>(alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP,
*(s7 + FIX_SYM_FUNCTION_TYPE), 0x40, UNKNOWN_PP));
auto f = (uint64_t)func;
auto target_function = (u8*)&f;
auto trampoline_function_addr = _arg_call_linux;
auto trampoline_function_addr = _arg_call_systemv;
auto trampoline = (u8*)&trampoline_function_addr;
// movabs rax, target_function
@ -378,17 +382,22 @@ Ptr<Function> make_function_from_c_win32(void* func, bool arg3_is_pp) {
}
extern "C" {
void _stack_call_linux();
#ifdef __APPLE__
void _stack_call_systemv() asm("_stack_call_systemv");
void _stack_call_win32() asm("_stack_call_win32");
#else
void _stack_call_systemv();
void _stack_call_win32();
#endif
}
Ptr<Function> make_stack_arg_function_from_c_linux(void* func) {
Ptr<Function> make_stack_arg_function_from_c_systemv(void* func) {
// allocate a function object on the global heap
auto mem = Ptr<u8>(alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP,
*(s7 + FIX_SYM_FUNCTION_TYPE), 0x40, UNKNOWN_PP));
auto f = (uint64_t)func;
auto target_function = (u8*)&f;
auto trampoline_function_addr = _stack_call_linux;
auto trampoline_function_addr = _stack_call_systemv;
auto trampoline = (u8*)&trampoline_function_addr;
// movabs rax, target_function
@ -467,7 +476,9 @@ Ptr<Function> make_stack_arg_function_from_c_win32(void* func) {
*/
Ptr<Function> make_function_from_c(void* func, bool arg3_is_pp = false) {
#ifdef __linux__
return make_function_from_c_linux(func, arg3_is_pp);
return make_function_from_c_systemv(func, arg3_is_pp);
#elif __APPLE__
return make_function_from_c_systemv(func, arg3_is_pp);
#elif _WIN32
return make_function_from_c_win32(func, arg3_is_pp);
#endif
@ -475,7 +486,9 @@ Ptr<Function> make_function_from_c(void* func, bool arg3_is_pp = false) {
Ptr<Function> make_stack_arg_function_from_c(void* func) {
#ifdef __linux__
return make_stack_arg_function_from_c_linux(func);
return make_stack_arg_function_from_c_systemv(func);
#elif __APPLE__
return make_stack_arg_function_from_c_systemv(func);
#elif _WIN32
return make_stack_arg_function_from_c_win32(func);
#endif
@ -1712,10 +1725,9 @@ s32 InitHeapAndSymbol() {
// check the kernel version!
auto kernel_version = intern_from_c("*kernel-version*")->value;
if (!kernel_version || ((kernel_version >> 0x13) != KERNEL_VERSION_MAJOR)) {
MsgErr("\n");
MsgErr(
"dkernel: compiled C kernel version is %d.%d but the goal kernel is %d.%d\n\tfrom the "
"goal> prompt (:mch) then mkee your kernel in linux.\n",
lg::error(
"Kernel version mismatch! Compiled C kernel version is {}.{} but"
"the goal kernel is {}.{}",
KERNEL_VERSION_MAJOR, KERNEL_VERSION_MINOR, kernel_version >> 0x13,
(kernel_version >> 3) & 0xffff);
return -1;

View File

@ -276,19 +276,23 @@ u64 make_debug_string_from_c(const char* c_str) {
}
extern "C" {
void _arg_call_linux();
#ifdef __APPLE__
void _arg_call_systemv() asm("_arg_call_systemv");
#else
void _arg_call_systemv();
#endif
}
/*!
* This creates an OpenGOAL function from a C++ function. Only 6 arguments can be accepted.
* But calling this function is fast. It used to be really fast but wrong.
*/
Ptr<Function> make_function_from_c_linux(void* func, bool arg3_is_pp) {
Ptr<Function> make_function_from_c_systemv(void* func, bool arg3_is_pp) {
auto mem = Ptr<u8>(alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP,
u32_in_fixed_sym(FIX_SYM_FUNCTION_TYPE), 0x40, UNKNOWN_PP));
auto f = (uint64_t)func;
auto target_function = (u8*)&f;
auto trampoline_function_addr = _arg_call_linux;
auto trampoline_function_addr = _arg_call_systemv;
auto trampoline = (u8*)&trampoline_function_addr;
// movabs rax, target_function
@ -390,17 +394,22 @@ Ptr<Function> make_function_from_c_win32(void* func, bool arg3_is_pp) {
}
extern "C" {
void _stack_call_linux();
#ifdef __APPLE__
void _stack_call_systemv() asm("_stack_call_systemv");
void _stack_call_win32() asm("_stack_call_win32");
#else
void _stack_call_systemv();
void _stack_call_win32();
#endif
}
Ptr<Function> make_stack_arg_function_from_c_linux(void* func) {
Ptr<Function> make_stack_arg_function_from_c_systemv(void* func) {
// allocate a function object on the global heap
auto mem = Ptr<u8>(alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP,
u32_in_fixed_sym(FIX_SYM_FUNCTION_TYPE), 0x40, UNKNOWN_PP));
auto f = (uint64_t)func;
auto target_function = (u8*)&f;
auto trampoline_function_addr = _stack_call_linux;
auto trampoline_function_addr = _stack_call_systemv;
auto trampoline = (u8*)&trampoline_function_addr;
// movabs rax, target_function
@ -479,7 +488,9 @@ Ptr<Function> make_stack_arg_function_from_c_win32(void* func) {
*/
Ptr<Function> make_function_from_c(void* func, bool arg3_is_pp = false) {
#ifdef __linux__
return make_function_from_c_linux(func, arg3_is_pp);
return make_function_from_c_systemv(func, arg3_is_pp);
#elif __APPLE__
return make_function_from_c_systemv(func, arg3_is_pp);
#elif _WIN32
return make_function_from_c_win32(func, arg3_is_pp);
#endif
@ -487,7 +498,9 @@ Ptr<Function> make_function_from_c(void* func, bool arg3_is_pp = false) {
Ptr<Function> make_stack_arg_function_from_c(void* func) {
#ifdef __linux__
return make_stack_arg_function_from_c_linux(func);
return make_stack_arg_function_from_c_systemv(func);
#elif __APPLE__
return make_stack_arg_function_from_c_systemv(func);
#elif _WIN32
return make_stack_arg_function_from_c_win32(func);
#endif
@ -1728,10 +1741,9 @@ int InitHeapAndSymbol() {
auto kernel_version = intern_from_c("*kernel-version*")->value();
if (!kernel_version || ((kernel_version >> 0x13) != KERNEL_VERSION_MAJOR)) {
MsgErr("\n");
MsgErr(
"dkernel: compiled C kernel version is %d.%d but the goal kernel is %d.%d\n\tfrom the "
"goal> prompt (:mch) then mkee your kernel in linux.\n",
lg::error(
"Kernel version mismatch! Compiled C kernel version is {}.{} but"
"the goal kernel is {}.{}",
KERNEL_VERSION_MAJOR, KERNEL_VERSION_MINOR, kernel_version >> 0x13,
(kernel_version >> 3) & 0xffff);
return -1;

View File

@ -20,8 +20,14 @@
extern u8* g_ee_main_mem;
extern "C" {
u64 _call_goal8_asm_linux(void* func, u64* arg_array, u64 zero, u64 pp, u64 st, void* off);
#ifdef __linux__
u64 _call_goal8_asm_systemv(void* func, u64* arg_array, u64 zero, u64 pp, u64 st, void* off);
#elif defined __APPLE__ && defined __x86_64__
u64 _call_goal8_asm_systemv(void* func, u64* arg_array, u64 zero, u64 pp, u64 st, void* off) asm(
"_call_goal8_asm_systemv");
#elif _WIN32
u64 _call_goal8_asm_win32(void* func, u64* arg_array, u64 zero, u64 pp, u64 st, void* off);
#endif
}
namespace Mips2C {
@ -344,13 +350,15 @@ struct ExecutionContext {
}
void jalr(u32 addr) {
// u64 _call_goal8_asm_linux(u64 func, u64* arg_array, u64 zero, u64 pp, u64 st, u64 off);
u64 args[8] = {gprs[a0].du64[0], gprs[a1].du64[0], gprs[a2].du64[0], gprs[a3].du64[0],
gprs[t0].du64[0], gprs[t1].du64[0], gprs[t2].du64[0], gprs[t3].du64[0]};
#ifdef __linux__
ASSERT(addr);
gprs[v0].du64[0] = _call_goal8_asm_linux(g_ee_main_mem + addr, args, 0, gprs[s6].du64[0],
gprs[s7].du64[0], g_ee_main_mem);
#ifdef __linux__
gprs[v0].du64[0] = _call_goal8_asm_systemv(g_ee_main_mem + addr, args, 0, gprs[s6].du64[0],
gprs[s7].du64[0], g_ee_main_mem);
#elif defined __APPLE__ && defined __x86_64__
gprs[v0].du64[0] = _call_goal8_asm_systemv(g_ee_main_mem + addr, args, 0, gprs[s6].du64[0],
gprs[s7].du64[0], g_ee_main_mem);
#elif _WIN32
gprs[v0].du64[0] = _call_goal8_asm_win32(g_ee_main_mem + addr, args, 0, gprs[s6].du64[0],
gprs[s7].du64[0], g_ee_main_mem);

View File

@ -10,7 +10,11 @@
#include "game/runtime.h"
extern "C" {
void _mips2c_call_linux();
#ifdef __linux__
void _mips2c_call_systemv();
#elif defined __APPLE__ && defined __x86_64__
void _mips2c_call_systemv() asm("_mips2c_call_systemv");
#endif
void _mips2c_call_windows();
}
@ -478,7 +482,9 @@ void LinkedFunctionTable::reg(const std::string& name, u64 (*exec)(void*), u32 s
// call the other function
#ifdef __linux__
addr = (u64)_mips2c_call_linux;
addr = (u64)_mips2c_call_systemv;
#elif defined __APPLE__ && defined __x86_64__
addr = (u64)_mips2c_call_systemv;
#elif _WIN32
addr = (u64)_mips2c_call_windows;
#endif

View File

@ -76,7 +76,7 @@ Replace the `defun` with:
You can use the same idea for methods with `method-set!`. The method name will be the decompiler name.
## Running Mips2C code
When Mips2C code is linked (for the first time), a small dynamically generated function object is created. This is a very short stub that jumps to a common implementation in `mips2c_call_linux`, that actually sets up the call.
When Mips2C code is linked (for the first time), a small dynamically generated function object is created. This is a very short stub that jumps to a common implementation in `mips2c_call_systemv`, that actually sets up the call.
The setup code saves the appropriate registers for the OS, allocates an `ExecutionContext` on the stack, initializes the argument registers, allocates a "fake stack array" on the stack with the requested size, and calls the C++ function.
@ -86,4 +86,4 @@ Unfortunately, throwing all the registers on the stack takes a huge amount of st
With some clever tricks it might be possible to do better, but it doesn't seem worth it at this time.
On exit, the assembly function will grab the return value from `v0` and put it in `rax`.
On exit, the assembly function will grab the return value from `v0` and put it in `rax`.

View File

@ -26,6 +26,8 @@ target_link_libraries(sound PRIVATE fmt cubeb common)
add_executable(sndplay 989snd/sndplay.cpp)
if(WIN32)
target_link_libraries(sndplay PRIVATE sound cubeb)
elseif(APPLE)
target_link_libraries(sndplay PRIVATE sound cubeb)
else()
target_link_libraries(sndplay PRIVATE sound cubeb stdc++fs)
endif()