From 07abec317c255924445ced39d150b17844ae34c5 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Tue, 13 Oct 2026 18:10:16 -0700 Subject: [PATCH] Arm64ec: Wire up non-faulting synchronous faults --- Source/Windows/ARM64EC/Module.cpp | 47 ++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index 633f252c3..80752f1b8 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -542,17 +542,9 @@ static ARM64_NT_CONTEXT StoreStateToPackedECContext(FEXCore::Core::InternalThrea return ECContext; } -static void RethrowGuestException(const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT& Context) { - auto* Thread = GetCPUArea().ThreadState(); +static void RaiseException(FEXCore::Core::InternalThreadState* Thread, const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT& GuestContext) { auto& Fault = Thread->CurrentFrame->SynchronousFaultData; - LogMan::Msg::DFmt("Reconstructing context"); - if (!IsDispatcherAddress(Context.Pc)) { - ReconstructThreadState(Thread, Context); - } - 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. // TODO: Can extend wine to support this by mapping the remaining EFlags into reserved cpsr members. @@ -572,6 +564,40 @@ static void RethrowGuestException(const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT& NtRaiseExceptionNative(&GuestRec, &GuestContext, FirstChance); } +static void RethrowGuestException(FEXCore::Core::InternalThreadState* Thread, const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT& HostContext) { + LogMan::Msg::DFmt("Reconstructing context"); + if (!IsDispatcherAddress(HostContext.Pc)) { + ReconstructThreadState(Thread, HostContext); + } + auto GuestContext = StoreStateToPackedECContext(Thread, HostContext.Fpcr, HostContext.Fpsr); + LogMan::Msg::DFmt("pc: {:X} rip: {:X}", HostContext.Pc, GuestContext.Pc); + + RaiseException(Thread, Rec, GuestContext); +} + +[[noreturn]] +static void SynchronousExceptionHandler(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame) { + // Coming in to this function, the guest state is synchronized and the host thread is in a safe location. + auto Thread = Frame->Thread; + auto& Fault = Thread->CurrentFrame->SynchronousFaultData; + + uint64_t FPCR, FPSR; + __asm volatile("mrs %[fpcr], fpcr" : [fpcr] "=r"(FPCR)); + __asm volatile("mrs %[fpsr], fpsr" : [fpsr] "=r"(FPSR)); + + auto Context = StoreStateToPackedECContext(Thread, static_cast(FPCR), static_cast(FPSR)); + EXCEPTION_RECORD Record = {}; + switch (Fault.Signal) { + case FEXCore::Core::FAULT_SIGILL: Record.ExceptionCode = EXCEPTION_ILLEGAL_INSTRUCTION; break; + case FEXCore::Core::FAULT_SIGTRAP: Record.ExceptionCode = EXCEPTION_BREAKPOINT; break; + case FEXCore::Core::FAULT_SIGSEGV: Record.ExceptionCode = EXCEPTION_ACCESS_VIOLATION; break; + default: ERROR_AND_DIE_FMT("Unknown fault signal"); break; + } + + RaiseException(Thread, Record, Context); + ERROR_AND_DIE_FMT("This should always noreturn"); +} + class ECSyscallHandler : public FEXCore::HLE::SyscallHandler, public FEXCore::Allocator::FEXAllocOperators { public: ECSyscallHandler() = default; @@ -805,7 +831,7 @@ bool ResetToConsistentStateImpl(const ThreadCPUArea CPUArea, EXCEPTION_RECORD* E } if (IsEmulatorStackAddress(CPUArea, reinterpret_cast(__builtin_frame_address(0)))) { - Exception::RethrowGuestException(*Exception, *NativeContext); + Exception::RethrowGuestException(Thread, *Exception, *NativeContext); LogMan::Msg::DFmt("Rethrowing onto guest stack: {:X}", NativeContext->Sp); return true; } else { @@ -1038,6 +1064,7 @@ NTSTATUS ThreadInit() { FEX::Windows::CallRetStack::InitializeThread(Thread); Thread->CurrentFrame->Pointers.ExitFunctionEC = reinterpret_cast(&ExitFunctionEC); + Thread->CurrentFrame->Pointers.SynchronousExceptionHandler = reinterpret_cast(&Exception::SynchronousExceptionHandler); CPUArea.StateFrame() = Thread->CurrentFrame; uint64_t EnterEC = Thread->CurrentFrame->Pointers.DispatcherLoopTopEnterEC;