diff --git a/Source/Windows/ARM64EC/Module.S b/Source/Windows/ARM64EC/Module.S index 4c18172ea..4a59cf650 100644 --- a/Source/Windows/ARM64EC/Module.S +++ b/Source/Windows/ARM64EC/Module.S @@ -121,6 +121,7 @@ ExitFunctionSuspendPoint: // Allows for continuing from a full native context, as the NTDLL NtContinue export takes in an x64 context with EC and // the conversion to that loses the ARM64EC ABI-disallowed registers that FEX uses. DIRECT_SYSCALL_WRAPPER("#NtContinueNative", WineNtContinueSyscallId, 0x43) +DIRECT_SYSCALL_WRAPPER("#NtRaiseExceptionNative", WineNtRaiseExceptionSyscallId, 0x174) // Both of these are wrapped as FEX needs them to setup its call checker at startup time and their NTDLL thunks could // already be patched by then (and because the call checker isn't installed, their patched x86 versions would be invoked diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index 5e35f565c..fa3a994bc 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -87,10 +87,12 @@ void* WineSyscallDispatcher; uint64_t WineNtContinueSyscallId; uint64_t WineNtAllocateVirtualMemorySyscallId; uint64_t WineNtProtectVirtualMemorySyscallId; +uint64_t WineNtRaiseExceptionSyscallId; NTSTATUS NtContinueNative(ARM64_NT_CONTEXT* NativeContext, BOOLEAN Alert); NTSTATUS NtAllocateVirtualMemoryNative(HANDLE, PVOID*, ULONG_PTR, SIZE_T*, ULONG, ULONG); NTSTATUS NtProtectVirtualMemoryNative(HANDLE, PVOID*, SIZE_T*, ULONG, ULONG*); +NTSTATUS NtRaiseExceptionNative(EXCEPTION_RECORD*, ARM64_NT_CONTEXT*, BOOL); static fextl::string AppConfigName {}; [[noreturn]] @@ -260,6 +262,8 @@ void ParseWineSyscallNumbers(HMODULE NtDll) { WineNtAllocateVirtualMemorySyscallId = CurSyscallId; } else if (strcmp(it->Name, "NtProtectVirtualMemory") == 0) { WineNtProtectVirtualMemorySyscallId = CurSyscallId; + } else if (strcmp(it->Name, "NtRaiseException") == 0) { + WineNtRaiseExceptionSyscallId = CurSyscallId; } } } @@ -500,7 +504,8 @@ static void RethrowGuestException(const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT& EFlags &= ~(1 << FEXCore::X86State::RFLAG_TF_RAW_LOC); CTX->SetFlagsFromCompactedEFLAGS(Thread, EFlags); - Args->Rec = FEX::Windows::HandleGuestException(Fault, Rec, Args->Context.Pc, Args->Context.X8, Args->Context.X0); + BOOL FirstChance = TRUE; + Args->Rec = FEX::Windows::HandleGuestException(Fault, Rec, Args->Context.Pc, Args->Context.X8, Args->Context.X0, FirstChance); if (Args->Rec.ExceptionCode == EXCEPTION_SINGLE_STEP) { Args->Context.Cpsr &= ~(1 << 21); // PSTATE.SS } else if (Args->Rec.ExceptionCode == EXCEPTION_BREAKPOINT) { @@ -508,6 +513,9 @@ static void RethrowGuestException(const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT& Args->Context.Pc -= 1; } + if (!FirstChance) { + NtRaiseExceptionNative(&Args->Rec, &Args->Context, FirstChance); + } Context.Sp = reinterpret_cast(Args); Context.Pc = KiUserExceptionDispatcher; } diff --git a/Source/Windows/Common/Exception.h b/Source/Windows/Common/Exception.h index a02f899b4..12162233c 100644 --- a/Source/Windows/Common/Exception.h +++ b/Source/Windows/Common/Exception.h @@ -11,7 +11,7 @@ namespace FEX::Windows { template static inline EXCEPTION_RECORD HandleGuestException(FEXCore::Core::CpuStateFrame::SynchronousFaultDataStruct& Fault, - const EXCEPTION_RECORD& Src, TReg& Rip, TReg Rax, TReg Cx) { + const EXCEPTION_RECORD& Src, TReg& Rip, TReg Rax, TReg Cx, BOOL& FirstChance) { EXCEPTION_RECORD Dst = Src; Dst.ExceptionAddress = reinterpret_cast(Rip); @@ -51,9 +51,11 @@ static inline EXCEPTION_RECORD HandleGuestException(FEXCore::Core::CpuStateFrame return Dst; case 0x29: Dst.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; + Dst.ExceptionFlags = EXCEPTION_NONCONTINUABLE; Dst.ExceptionAddress = reinterpret_cast(Rip); Dst.NumberParameters = 1; Dst.ExceptionInformation[0] = Cx; + FirstChance = FALSE; return Dst; case 0x2c: Dst.ExceptionCode = STATUS_ASSERTION_FAILURE; return Dst; case 0x2d: diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index ed1603592..50555548d 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -942,7 +942,8 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) { LogMan::Msg::DFmt("pc: {:X} eip: {:X}", Context->Pc, WowContext.Eip); auto& Fault = Thread->CurrentFrame->SynchronousFaultData; - *Exception = FEX::Windows::HandleGuestException(Fault, *Exception, WowContext.Eip, WowContext.Eax, WowContext.Ecx); + BOOL FirstChance = TRUE; + *Exception = FEX::Windows::HandleGuestException(Fault, *Exception, WowContext.Eip, WowContext.Eax, WowContext.Ecx, FirstChance); if (Exception->ExceptionCode == EXCEPTION_SINGLE_STEP) { WowContext.EFlags &= ~(1 << FEXCore::X86State::RFLAG_TF_RAW_LOC); } @@ -951,6 +952,10 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) { BTCpuSetContext(GetCurrentThread(), GetCurrentProcess(), nullptr, &WowContext); Context::UnlockJITContext(TLS); + if (!FirstChance) { + NtRaiseException(Exception, TLS.EntryContext(), FirstChance); + } + // Replace the host context with one captured before JIT entry so host code can unwind memcpy(Context, TLS.EntryContext(), sizeof(*Context)); diff --git a/Source/Windows/include/winternl.h b/Source/Windows/include/winternl.h index 0244cb6b7..a987c3a1d 100644 --- a/Source/Windows/include/winternl.h +++ b/Source/Windows/include/winternl.h @@ -557,6 +557,7 @@ NTSTATUS WINAPI NtQueryDirectoryFile(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO BOOLEAN, PUNICODE_STRING, BOOLEAN); NTSTATUS WINAPI NtQueryValueKey(HANDLE, const UNICODE_STRING*, KEY_VALUE_INFORMATION_CLASS, void*, DWORD, DWORD*); NTSTATUS WINAPI NtQueryVirtualMemory(HANDLE, LPCVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, SIZE_T*); +NTSTATUS WINAPI NtRaiseException(EXCEPTION_RECORD*, CONTEXT*, BOOL); NTSTATUS WINAPI NtReadFile(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER, PULONG); NTSTATUS WINAPI NtSetContextThread(HANDLE, const CONTEXT*); NTSTATUS WINAPI NtSuspendThread(HANDLE, PULONG);