Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARM64EC: Dynamically determine syscall numbers under wine. #4088

Merged
merged 1 commit into from
Oct 1, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions Source/Windows/ARM64EC/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -151,7 +150,6 @@ bool IsDispatcherAddress(uint64_t Address) {


void FillNtDllLUTs(HMODULE NtDll) {
NtDllBase = reinterpret_cast<uintptr_t>(NtDll);
ULONG Size;
const auto* LoadConfig =
reinterpret_cast<_IMAGE_LOAD_CONFIG_DIRECTORY64*>(RtlImageDirectoryEntryToData(NtDll, true, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &Size));
Expand Down Expand Up @@ -196,6 +194,32 @@ 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<IMAGE_EXPORT_DIRECTORY*>(RtlImageDirectoryEntryToData(NtDll, true, IMAGE_DIRECTORY_ENTRY_EXPORT, &Size));
const auto* NameTable = reinterpret_cast<uint32_t*>(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 and the syscall ID must
// be incremented by 1 for each.
uint32_t CurSyscallId = 0;
for (uint32_t Idx = 0; Idx < Exports->NumberOfNames; Idx++) {
const char* Name = reinterpret_cast<const char*>(NtDllBase + NameTable[Idx]);
if (Name[0] == 'N' && Name[1] == 't' && strcmp(Name, "NtGetTickCount") != 0) {
if (strcmp(Name, "NtContinue") == 0) {
WineNtContinueSyscallId = CurSyscallId;
} else if (strcmp(Name, "NtAllocateVirtualMemory") == 0) {
WineNtAllocateVirtualMemorySyscallId = CurSyscallId;
} else if (strcmp(Name, "NtProtectVirtualMemory") == 0) {
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
Expand All @@ -204,9 +228,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<uintptr_t>(NtDll);

const auto WineSyscallDispatcherPtr = reinterpret_cast<void**>(GetProcAddress(NtDll, "__wine_syscall_dispatcher"));
if (WineSyscallDispatcherPtr) {
WineSyscallDispatcher = *WineSyscallDispatcherPtr;
ParseWineSyscallNumbers(NtDll);
}

FillNtDllLUTs(NtDll);
Expand Down
Loading