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:
parent
a0de42b1b3
commit
62c3b4b8e3
Binary file not shown.
|
@ -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)
|
|
@ -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:
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue