diff --git a/Assets/dll/libbizabiadapter_msabi_sysv.dll b/Assets/dll/libbizabiadapter_msabi_sysv.dll new file mode 100644 index 0000000000..58cc49c81f Binary files /dev/null and b/Assets/dll/libbizabiadapter_msabi_sysv.dll differ diff --git a/ExternalProjects/LibBizAbiAdapter/Makefile b/ExternalProjects/LibBizAbiAdapter/Makefile new file mode 100644 index 0000000000..58058d59bb --- /dev/null +++ b/ExternalProjects/LibBizAbiAdapter/Makefile @@ -0,0 +1,38 @@ +ROOT_DIR := $(realpath .) +OUTPUTDLL_DIR := $(realpath $(ROOT_DIR)/../../Assets/dll) +OUTPUTDLLCOPY_DIR := $(realpath $(ROOT_DIR)/../../output/dll) + +OBJ_DIR := $(ROOT_DIR)/obj + +TARGET := msabi_sysv +TARGET_DLL := libbizabiadapter_$(TARGET).dll + +OUT_OBJ := $(OBJ_DIR)/$(TARGET).o +OUT_DLL := $(OBJ_DIR)/$(TARGET_DLL) + +LDFLAGS := -shared -s + +$(OBJ_DIR)/%.o: %.s + @echo nasm $< + @mkdir -p $(@D) + @nasm -fwin64 -o $@ $< + +.DEFAULT_GOAL := install + +.PHONY: release install + +release: $(OUT_DLL) + +$(OUT_DLL): $(OUT_OBJ) + @echo ld $@ + @ld -o $@ $(LDFLAGS) $(OUT_OBJ) + +install: $(OUT_DLL) + @cp -f $< $(OUTPUTDLL_DIR) + @cp $(OUTPUTDLL_DIR)/$(TARGET_DLL) $(OUTPUTDLLCOPY_DIR)/$(TARGET_DLL) || true + @echo Release build of $(TARGET_DLL) installed. + +.PHONY: clean + +clean: + rm -rf $(OBJ_DIR) diff --git a/ExternalProjects/LibBizAbiAdapter/msabi_sysv.s b/ExternalProjects/LibBizAbiAdapter/msabi_sysv.s new file mode 100644 index 0000000000..045bcd4711 --- /dev/null +++ b/ExternalProjects/LibBizAbiAdapter/msabi_sysv.s @@ -0,0 +1,285 @@ +global __start + +%macro EXPORT_FUNCS 1 + global depart%1 + global arrive%1 + export depart%1 + export arrive%1 +%endmacro + +EXPORT_FUNCS 0 +EXPORT_FUNCS 1 +EXPORT_FUNCS 2 +EXPORT_FUNCS 3 +EXPORT_FUNCS 4 +EXPORT_FUNCS 5 +EXPORT_FUNCS 6 + +; save msabi nonvolatiles which are volatile under sysv +%macro START_DEPART 0 + push rsi + push rdi + sub rsp, 0xA8 + movaps [rsp + 0x90], xmm15 + movaps [rsp + 0x80], xmm14 + movaps [rsp + 0x70], xmm13 + movaps [rsp + 0x60], xmm12 + movaps [rsp + 0x50], xmm11 + movaps [rsp + 0x40], xmm10 + movaps [rsp + 0x30], xmm9 + movaps [rsp + 0x20], xmm8 + movaps [rsp + 0x10], xmm7 + movaps [rsp], xmm6 +%endmacro + +; restore the saved msabi nonvolatiles which were volatile under sysv, then return +%macro END_DEPART 0 + movaps xmm6, [rsp] + movaps xmm7, [rsp + 0x10] + movaps xmm8, [rsp + 0x20] + movaps xmm9, [rsp + 0x30] + movaps xmm10, [rsp + 0x40] + movaps xmm11, [rsp + 0x50] + movaps xmm12, [rsp + 0x60] + movaps xmm13, [rsp + 0x70] + movaps xmm14, [rsp + 0x80] + movaps xmm15, [rsp + 0x90] + add rsp, 0xA8 + pop rdi + pop rsi + ret +%endmacro + +; https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-runtime_function + +%define RVA(sym) sym wrt ..imagebase + +%macro DEPART_UNWIND_ENTRY 1 + dd RVA(depart%1) + dd RVA(depart%1_end) + dd RVA(xdepart) +%endmacro + +%macro ARRIVE_UNWIND_ENTRY 1 + dd RVA(arrive%1) + dd RVA(arrive%1_end) + dd RVA(xarrive) +%endmacro + +; https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64 + +%define UWOP_PUSH_NONVOL 0 +%define UWOP_ALLOC_LARGE 1 +%define UWOP_ALLOC_SMALL 2 +%define UWOP_SAVE_XMM128 8 + +section .pdata rdata align=4 + + DEPART_UNWIND_ENTRY 0 + DEPART_UNWIND_ENTRY 1 + DEPART_UNWIND_ENTRY 2 + DEPART_UNWIND_ENTRY 3 + DEPART_UNWIND_ENTRY 4 + DEPART_UNWIND_ENTRY 5 + DEPART_UNWIND_ENTRY 6 + + ARRIVE_UNWIND_ENTRY 0 + ARRIVE_UNWIND_ENTRY 1 + ARRIVE_UNWIND_ENTRY 2 + ARRIVE_UNWIND_ENTRY 3 + ARRIVE_UNWIND_ENTRY 4 + ARRIVE_UNWIND_ENTRY 5 + + dd RVA(arrive6) + dd RVA(arrive6_end) + dd RVA(xarrive6) + +section .xdata rdata align=8 + + xdepart: + db 1, 72, 24, 0 + db 68, (0x6 << 4) | UWOP_SAVE_XMM128 + dw 0 + db 63, (0x7 << 4) | UWOP_SAVE_XMM128 + dw 1 + db 57, (0x8 << 4) | UWOP_SAVE_XMM128 + dw 2 + db 51, (0x9 << 4) | UWOP_SAVE_XMM128 + dw 3 + db 45, (0xA << 4) | UWOP_SAVE_XMM128 + dw 4 + db 39, (0xB << 4) | UWOP_SAVE_XMM128 + dw 5 + db 33, (0xC << 4) | UWOP_SAVE_XMM128 + dw 6 + db 27, (0xD << 4) | UWOP_SAVE_XMM128 + dw 7 + db 18, (0xE << 4) | UWOP_SAVE_XMM128 + dw 8 + db 9, (0xF << 4) | UWOP_SAVE_XMM128 + dw 9 + db 2, (0x0 << 4) | UWOP_ALLOC_LARGE + dw 0x15 + db 1, (0x7 << 4) | UWOP_PUSH_NONVOL + db 0, (0x6 << 4) | UWOP_PUSH_NONVOL + + xarrive: + db 1, 4, 1, 0 + db 0, (0x4 << 4) | UWOP_ALLOC_SMALL + dw 0 + + xarrive6: + db 1, 4, 1, 0 + db 0, (0x6 << 4) | UWOP_ALLOC_SMALL + dw 0 + +section .text + + ; DllMain, just a stub here + __start: + mov eax, 1 + ret + + ; departX are msabi functions that call a sysv function and returns its result. + ; arriveX are sysv functions that call a msabi function and returns its result. + ; The function is passed as a hidden parameter in rax, and should take X pointer or integer type arguments. + ; The function passed may also have at most 4 floating point arguments. + + depart0: + START_DEPART + call rax + END_DEPART + depart0_end: + + depart1: + START_DEPART + mov rdi, rcx + call rax + END_DEPART + depart1_end: + + depart2: + START_DEPART + mov rdi, rcx + mov rsi, rdx + call rax + END_DEPART + depart2_end: + + depart3: + START_DEPART + mov rdi, rcx + mov rsi, rdx + mov rdx, r8 + call rax + END_DEPART + depart3_end: + + depart4: + START_DEPART + mov rdi, rcx + mov rsi, rdx + mov rdx, r8 + mov rcx, r9 + call rax + END_DEPART + depart4_end: + + depart5: + START_DEPART + mov r10, r8 + mov r8, qword [rsp + 0xE0] + mov rdi, rcx + mov rsi, rdx + mov rdx, r10 + mov rcx, r9 + call rax + END_DEPART + depart5_end: + + depart6: + START_DEPART + mov r11, r9 + mov r10, r8 + mov r8, qword [rsp + 0xE0] + mov r9, qword [rsp + 0xE8] + mov rdi, rcx + mov rsi, rdx + mov rdx, r10 + mov rcx, r11 + call rax + END_DEPART + depart6_end: + + arrive0: + sub rsp, 0x28 + call rax + add rsp, 0x28 + ret + arrive0_end: + + arrive1: + sub rsp, 0x28 + mov rcx, rdi + call rax + add rsp, 0x28 + ret + arrive1_end: + + arrive2: + sub rsp, 0x28 + mov rdx, rsi + mov rcx, rdi + call rax + add rsp, 0x28 + ret + arrive2_end: + + arrive3: + sub rsp, 0x28 + mov r8, rdx + mov rdx, rsi + mov rcx, rdi + call rax + add rsp, 0x28 + ret + arrive3_end: + + arrive4: + sub rsp, 0x28 + mov r9, rcx + mov r8, rdx + mov rdx, rsi + mov rcx, rdi + call rax + add rsp, 0x28 + ret + arrive4_end: + + arrive5: + sub rsp, 0x28 + mov r9, rcx + mov r10, rdx + mov rdx, rsi + mov rcx, rdi + mov qword [rsp + 0x20], r8 + mov r8, r10 + call rax + add rsp, 0x28 + ret + arrive5_end: + + arrive6: + sub rsp, 0x38 + mov r10, rcx + mov r11, rdx + mov rdx, rsi + mov rcx, rdi + mov qword [rsp + 0x28], r9 + mov qword [rsp + 0x20], r8 + mov r8, r11 + mov r9, r10 + call rax + add rsp, 0x38 + ret + arrive6_end: diff --git a/src/BizHawk.BizInvoke/CallingConventionAdapter.cs b/src/BizHawk.BizInvoke/CallingConventionAdapter.cs index 8c4610950f..bca7e9934f 100644 --- a/src/BizHawk.BizInvoke/CallingConventionAdapter.cs +++ b/src/BizHawk.BizInvoke/CallingConventionAdapter.cs @@ -229,26 +229,25 @@ namespace BizHawk.BizInvoke /// private class MsHostSysVGuest : ICallingConventionAdapter { - // This is implemented by using thunks defined in the waterbox native code, and putting stubs on top of them that close over the - // function pointer parameter. + // This is implemented by using thunks defined in a small dll, and putting stubs on top of them that close over the + // function pointer parameter. A dll is used here to easily set unwind information (allowing SEH exceptions to work). private const int BlockSize = 32; private static readonly IImportResolver ThunkDll; static MsHostSysVGuest() { - // If needed, these can be split out from waterboxhost.dll; they're not directly related to anything waterbox does. - ThunkDll = new DynamicLibraryImportResolver(OSTailoredCode.IsUnixHost ? "libwaterboxhost.so" : "waterboxhost.dll", hasLimitedLifetime: false); + ThunkDll = new DynamicLibraryImportResolver("libbizabiadapter_msabi_sysv.dll", hasLimitedLifetime: false); } private readonly MemoryBlock _memory; - private readonly object _sync = new object(); + private readonly object _sync = new(); private readonly WeakReference?[] _refs; public MsHostSysVGuest() { - int size = 4 * 1024 * 1024; - _memory = new MemoryBlock((ulong)size); + const int size = 4 * 1024 * 1024; + _memory = new((ulong)size); _refs = new WeakReference[size / BlockSize]; } @@ -274,7 +273,7 @@ namespace BizHawk.BizInvoke private static void VerifyParameter(Type type) { if (type == typeof(float) || type == typeof(double)) - throw new NotSupportedException("floating point not supported"); + return; // note only 4 floating point args can be used at once, this is checked later if (type == typeof(void) || type.IsPrimitive || type.IsEnum) return; if (type.IsPointer || typeof(Delegate).IsAssignableFrom(type)) @@ -289,8 +288,9 @@ namespace BizHawk.BizInvoke VerifyParameter(pp.ReturnType); foreach (var ppp in pp.ParameterTypes) VerifyParameter(ppp); - var ret = pp.ParameterTypes.Count; - if (ret >= 7) + var ret = pp.ParameterTypes.Count(t => t != typeof(float) && t != typeof(double)); + var fargs = pp.ParameterTypes.Count - ret; + if (ret >= 7 || fargs >= 5) throw new InvalidOperationException("Too many parameters to marshal!"); return ret; } @@ -345,7 +345,7 @@ namespace BizHawk.BizInvoke else { return GetArrivalFunctionPointer( - Marshal.GetFunctionPointerForDelegate(d), new ParameterInfo(d.GetType()), d); + Marshal.GetFunctionPointerForDelegate(d), new(d.GetType()), d); } } } @@ -367,7 +367,7 @@ namespace BizHawk.BizInvoke lock (_sync) { var index = FindFreeIndex(); - var count = VerifyDelegateSignature(new ParameterInfo(delegateType)); + var count = VerifyDelegateSignature(new(delegateType)); WriteThunk(ThunkDll.GetProcAddrOrThrow($"depart{count}"), p, index); var ret = Marshal.GetDelegateForFunctionPointer(GetThunkAddress(index), delegateType); SetLifetime(index, ret);