From 0b8ee4991f2e519594be52f8528809eb3cdd7322 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Fri, 27 Sep 2024 17:21:18 +0000 Subject: [PATCH] ARM64EC: Dynamically determine syscall numbers under wine. Takes advantage of the consistent alphabetical syscall ordering that wine follows. --- Source/Windows/ARM64EC/Module.cpp | 36 ++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index f2d8ed10a4..38589e9716 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -69,10 +69,9 @@ uint32_t NtDllRedirectionLUTSize; // Wine doesn't support issuing direct system calls with SVC, and unlike Windows it doesn't have a 'stable' syscall number for NtContinue void* WineSyscallDispatcher; -// TODO: this really shouldn't be hardcoded, once wine gains proper syscall thunks this can be dropped. -uint64_t WineNtContinueSyscallId = 0x1a; -uint64_t WineNtAllocateVirtualMemorySyscallId = 0xb; -uint64_t WineNtProtectVirtualMemorySyscallId = 0x76; +uint64_t WineNtContinueSyscallId; +uint64_t WineNtAllocateVirtualMemorySyscallId; +uint64_t WineNtProtectVirtualMemorySyscallId; NTSTATUS NtContinueNative(ARM64_NT_CONTEXT* NativeContext, BOOLEAN Alert); NTSTATUS NtAllocateVirtualMemoryNative(HANDLE, PVOID*, ULONG_PTR, SIZE_T*, ULONG, ULONG); @@ -151,7 +150,6 @@ bool IsDispatcherAddress(uint64_t Address) { void FillNtDllLUTs(HMODULE NtDll) { - NtDllBase = reinterpret_cast(NtDll); ULONG Size; const auto* LoadConfig = reinterpret_cast<_IMAGE_LOAD_CONFIG_DIRECTORY64*>(RtlImageDirectoryEntryToData(NtDll, true, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &Size)); @@ -196,6 +194,31 @@ void PatchCallChecker() { WriteModuleRVA(Module, CHPEMetadata->__os_arm64x_dispatch_icall_cfg, &CheckCall); } +// Fills in the syscall numbers necessary to call *Native variants of syscalls from FEX under wine. +void ParseWineSyscallNumbers(HMODULE NtDll) { + ULONG Size; + const auto* Exports = reinterpret_cast(RtlImageDirectoryEntryToData(NtDll, true, IMAGE_DIRECTORY_ENTRY_EXPORT, &Size)); + const auto* NameTable = reinterpret_cast(NtDllBase + Exports->AddressOfNames); + + // Wine orders syscalls alphabetically, take advantage of that to find the syscall indices for those which we need to + // manually issue. Note that all functions starting with Nt besides NtGetTickCount are syscalls. + uint32_t CurSyscallId = 0; + for (uint32_t Idx = 0; Idx < Exports->NumberOfNames; Idx++) { + const char* Name = reinterpret_cast(NtDllBase + NameTable[Idx]); + if (Name[0] == 'N' && Name[1] == 't' && strcmp(Name, "NtGetTickCount")) { + if (!strcmp(Name, "NtContinue")) { + WineNtContinueSyscallId = CurSyscallId; + } else if (!strcmp(Name, "NtAllocateVirtualMemory")) { + WineNtAllocateVirtualMemorySyscallId = CurSyscallId; + } else if (!strcmp(Name, "NtProtectVirtualMemory")) { + WineNtProtectVirtualMemorySyscallId = CurSyscallId; + } + + CurSyscallId++; + } + } +} + // Syscall thunks may have been patched before FEX has loaded, the default call checker installed by ntdll into FEX will // try to invoke the JIT when calling such patched syscalls but this obviously doesn't work before FEX is initalised. // This function parses ntdll and sets up a custom call checker to prevent this, as such it must avoid using any syscall @@ -204,9 +227,12 @@ void InitSyscalls() { // The ntdll exports called by GetModuleHandle/GetProcAddress aren't known to be patched before JIT init by any current // software so are safe to call, but if that changes the loader structures in the PEB could be parsed manually. const auto NtDll = GetModuleHandle("ntdll.dll"); + NtDllBase = reinterpret_cast(NtDll); + const auto WineSyscallDispatcherPtr = reinterpret_cast(GetProcAddress(NtDll, "__wine_syscall_dispatcher")); if (WineSyscallDispatcherPtr) { WineSyscallDispatcher = *WineSyscallDispatcherPtr; + ParseWineSyscallNumbers(NtDll); } FillNtDllLUTs(NtDll);