mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 14:00:16 +02:00
Merge pull request #5930 from cjacek/raise-exception
Windows: Use NtRaiseException to raise guest exception
This commit is contained in:
5 files changed
+24
-21
No files matched your search
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,18 +484,15 @@ static ARM64_NT_CONTEXT StoreStateToPackedECContext(FEXCore::Core::InternalThrea
|
||||
}
|
||||
|
||||
static void RethrowGuestException(const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT& Context) {
|
||||
const auto& Config = SignalDelegator->GetConfig();
|
||||
auto* Thread = GetCPUArea().ThreadState();
|
||||
auto& Fault = Thread->CurrentFrame->SynchronousFaultData;
|
||||
uint64_t GuestSp = Context.X[Config.SRAGPRMapping[static_cast<size_t>(FEXCore::X86State::REG_RSP)]];
|
||||
auto* Args = reinterpret_cast<KiUserExceptionDispatcherStackLayout*>(FEXCore::AlignDown(GuestSp, 64)) - 1;
|
||||
|
||||
LogMan::Msg::DFmt("Reconstructing context");
|
||||
if (!IsDispatcherAddress(Context.Pc)) {
|
||||
ReconstructThreadState(Thread, Context);
|
||||
}
|
||||
Args->Context = StoreStateToPackedECContext(Thread, Context.Fpcr, Context.Fpsr);
|
||||
LogMan::Msg::DFmt("pc: {:X} rip: {:X}", Context.Pc, Args->Context.Pc);
|
||||
ARM64_NT_CONTEXT GuestContext = StoreStateToPackedECContext(Thread, Context.Fpcr, Context.Fpsr);
|
||||
LogMan::Msg::DFmt("pc: {:X} rip: {:X}", Context.Pc, GuestContext.Pc);
|
||||
|
||||
// X64 Windows always clears TF, DF and AF when handling an exception, restoring after.
|
||||
// Current ARM64EC windows can only restore NZCV+SS when returning from an exception and other flags are left untouched from the handler context.
|
||||
@@ -500,16 +501,16 @@ 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);
|
||||
if (Args->Rec.ExceptionCode == EXCEPTION_SINGLE_STEP) {
|
||||
Args->Context.Cpsr &= ~(1 << 21); // PSTATE.SS
|
||||
} else if (Args->Rec.ExceptionCode == EXCEPTION_BREAKPOINT) {
|
||||
BOOL FirstChance = TRUE;
|
||||
EXCEPTION_RECORD GuestRec = FEX::Windows::HandleGuestException(Fault, Rec, GuestContext.Pc, GuestContext.X8, GuestContext.X0, FirstChance);
|
||||
if (GuestRec.ExceptionCode == EXCEPTION_SINGLE_STEP) {
|
||||
GuestContext.Cpsr &= ~(1 << 21); // PSTATE.SS
|
||||
} else if (GuestRec.ExceptionCode == EXCEPTION_BREAKPOINT) {
|
||||
// INT3 will set RIP to the instruction following it, undo this (any edge cases with multibyte instructions that trigger breakpoints are bugs present in Windows also)
|
||||
Args->Context.Pc -= 1;
|
||||
GuestContext.Pc -= 1;
|
||||
}
|
||||
|
||||
Context.Sp = reinterpret_cast<uint64_t>(Args);
|
||||
Context.Pc = KiUserExceptionDispatcher;
|
||||
NtRaiseExceptionNative(&GuestRec, &GuestContext, FirstChance);
|
||||
}
|
||||
|
||||
class ECSyscallHandler : public FEXCore::HLE::SyscallHandler, public FEXCore::Allocator::FEXAllocOperators {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace FEX::Windows {
|
||||
template<typename TReg>
|
||||
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<void*>(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<void*>(Rip);
|
||||
Dst.NumberParameters = 1;
|
||||
Dst.ExceptionInformation[0] = Cx;
|
||||
FirstChance = FALSE;
|
||||
return Dst;
|
||||
case 0x2c: Dst.ExceptionCode = STATUS_ASSERTION_FAILURE; return Dst;
|
||||
case 0x2d:
|
||||
|
||||
@@ -489,8 +489,7 @@ public:
|
||||
// Stash the the context pointer on the stack, as Simulate can be called from this syscall handler which would overwrite it
|
||||
CONTEXT* EntryContext = TLS.EntryContext();
|
||||
// Call the syscall handler with unwind information pointing to Simulate as its caller
|
||||
uint64_t Ret =
|
||||
SEHFrameTrampoline1Args(reinterpret_cast<void*>(Frame), reinterpret_cast<void*>(&HandleSyscallImpl), EntryContext->Sp, EntryContext->Pc);
|
||||
SEHFrameTrampoline1Args(reinterpret_cast<void*>(Frame), reinterpret_cast<void*>(&HandleSyscallImpl), EntryContext->Sp, EntryContext->Pc);
|
||||
TLS.EntryContext() = EntryContext;
|
||||
}
|
||||
|
||||
@@ -943,18 +942,17 @@ 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);
|
||||
if (Exception->ExceptionCode == EXCEPTION_SINGLE_STEP) {
|
||||
BOOL FirstChance = TRUE;
|
||||
EXCEPTION_RECORD GuestException =
|
||||
FEX::Windows::HandleGuestException(Fault, *Exception, WowContext.Eip, WowContext.Eax, WowContext.Ecx, FirstChance);
|
||||
if (GuestException.ExceptionCode == EXCEPTION_SINGLE_STEP) {
|
||||
WowContext.EFlags &= ~(1 << FEXCore::X86State::RFLAG_TF_RAW_LOC);
|
||||
}
|
||||
// wow64.dll will handle adjusting PC in the dispatched context after a breakpoint
|
||||
|
||||
BTCpuSetContext(GetCurrentThread(), GetCurrentProcess(), nullptr, &WowContext);
|
||||
Context::UnlockJITContext(TLS);
|
||||
|
||||
// Replace the host context with one captured before JIT entry so host code can unwind
|
||||
memcpy(Context, TLS.EntryContext(), sizeof(*Context));
|
||||
|
||||
NtRaiseException(&GuestException, TLS.EntryContext(), FirstChance);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user