Use a small dll for handling the msabi<->sysv adapter (#3451)

make a small dll for handling the msabi<->sysv adapter, using only assembly (taken from generated optimized rustc output) and handcrafted unwind information (c# exceptions in a callback seem to work fine in testing)
additionally, allow floating point arguments. this really only needs to occur on the c# side. msabi and sysv agree on the first 4 floating point args and for returns, so no work actually has to be done adapting these
with assembly being used, we can guarantee rax will not be stomped by compiler whims (and avoid potential floating point args from being trashed)
This commit is contained in:
CasualPokePlayer 2022-11-16 09:02:13 -08:00 committed by GitHub
parent a0de42b1b3
commit 62c3b4b8e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 335 additions and 12 deletions

Binary file not shown.

View File

@ -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)

View File

@ -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:

View File

@ -229,26 +229,25 @@ namespace BizHawk.BizInvoke
/// </summary>
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);