diff --git a/FEXCore/Source/Interface/Core/Frontend.cpp b/FEXCore/Source/Interface/Core/Frontend.cpp index 02cfdb1008..c38ec7363c 100644 --- a/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/FEXCore/Source/Interface/Core/Frontend.cpp @@ -96,6 +96,9 @@ bool Decoder::CheckRangeExecutable(uint64_t Address, uint64_t Size) { ExecutableRangeWritable = RangeInfo.Writable; if (RangeInfo.Size == 0) { + if (NonExecutableAddress == 0) { + NonExecutableAddress = Address; + } return false; } @@ -1086,12 +1089,14 @@ Decoder::DecodedBlockStatus Decoder::DecodeInstruction(uint64_t PC) { // Will be set if DecodeInstructionImpl tries to read non-executable memory HitNonExecutableRange = false; HitBadRelocation = false; + NonExecutableAddress = 0; auto ErrorDuringDecoding = DecodeInstructionImpl(PC); if (ErrorDuringDecoding != DecodedBlockStatus::SUCCESS || HitNonExecutableRange || HitBadRelocation) [[unlikely]] { // Put an invalid instruction in the stream so the core can raise SIGILL if hit // Error while decoding instruction. We don't know the table or instruction size DecodeInst->TableInfo = nullptr; + DecodeInst->FaultAddress = NonExecutableAddress; auto Result = ErrorDuringDecoding != DecodedBlockStatus::SUCCESS ? ErrorDuringDecoding : DecodeInst->InstSize ? DecodedBlockStatus::PARTIAL_DECODE_INST : HitNonExecutableRange ? DecodedBlockStatus::NOEXEC_INST : diff --git a/FEXCore/Source/Interface/Core/Frontend.h b/FEXCore/Source/Interface/Core/Frontend.h index f668d2b9c7..5a034fd79c 100644 --- a/FEXCore/Source/Interface/Core/Frontend.h +++ b/FEXCore/Source/Interface/Core/Frontend.h @@ -126,6 +126,7 @@ class Decoder final { uint64_t ExecutableRangeEnd {}; bool ExecutableRangeWritable {}; bool HitNonExecutableRange {}; + uint64_t NonExecutableAddress {}; bool HitBadRelocation {}; struct DecodeStream { diff --git a/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp b/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp index 6c2b3499a6..1d98635a37 100644 --- a/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp @@ -76,6 +76,9 @@ DEF_OP(Break) { LoadConstant(ARMEmitter::Size::i64Bit, TMP1, Constant); str(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData)); + LoadConstant(ARMEmitter::Size::i64Bit, TMP1, Op->Reason.FaultAddress); + str(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultAddress)); + switch (Op->Reason.Signal) { case Core::FAULT_SIGILL: ldr(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.GuestSignal_SIGILL)); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 31f9eddc09..208af63559 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -5158,6 +5158,7 @@ void OpDispatchBuilder::NoExecOp(OpcodeArgs) { .Signal = Core::FAULT_SIGSEGV, .TrapNumber = X86State::X86_TRAPNO_PF, .si_code = 2, // SEGV_ACCERR + .FaultAddress = Op->FaultAddress, }); } diff --git a/FEXCore/Source/Interface/Core/X86Tables/X86Tables.h b/FEXCore/Source/Interface/Core/X86Tables/X86Tables.h index 8bf90e6fdd..29541bf2ff 100644 --- a/FEXCore/Source/Interface/Core/X86Tables/X86Tables.h +++ b/FEXCore/Source/Interface/Core/X86Tables/X86Tables.h @@ -210,6 +210,7 @@ struct DecodedOperand { struct DecodedInst { uint64_t PC; + uint64_t FaultAddress; DecodedOperand Dest; DecodedOperand Src[3]; diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index 5d46bdf572..ab44c91a7d 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -124,6 +124,7 @@ " uint8_t Signal;", " uint8_t TrapNumber;", " uint8_t si_code;", + " uint64_t FaultAddress;", "};" ], "IRTypes" : { diff --git a/FEXCore/Source/Interface/IR/IRDumper.cpp b/FEXCore/Source/Interface/IR/IRDumper.cpp index d8a7a0d9e7..af0b9c3a9c 100644 --- a/FEXCore/Source/Interface/IR/IRDumper.cpp +++ b/FEXCore/Source/Interface/IR/IRDumper.cpp @@ -321,7 +321,8 @@ static void PrintArg(fextl::ostringstream* out, const IRListView*, FEXCore::IR:: *out << "{" << Arg.ErrorRegister << "."; *out << static_cast(Arg.Signal) << "."; *out << static_cast(Arg.TrapNumber) << "."; - *out << static_cast(Arg.si_code) << "}"; + *out << static_cast(Arg.si_code) << "."; + *out << fmt::format("0x{:x}", Arg.FaultAddress) << "}"; } static void PrintArg(fextl::ostringstream* out, const IRListView*, ShiftType Arg) { diff --git a/FEXCore/include/FEXCore/Core/CoreState.h b/FEXCore/include/FEXCore/Core/CoreState.h index 7c64dd83c1..beefc3b4bb 100644 --- a/FEXCore/include/FEXCore/Core/CoreState.h +++ b/FEXCore/include/FEXCore/Core/CoreState.h @@ -424,6 +424,7 @@ struct CpuStateFrame { uint16_t err_code; uint16_t _pad : 16; } SynchronousFaultData; + uint64_t SynchronousFaultAddress {}; InternalThreadState* Thread; @@ -444,4 +445,5 @@ static_assert(std::is_standard_layout::value, "This needs to be s static_assert(sizeof(CpuStateFrame::SynchronousFaultData) == 8, "This needs to be 8 bytes"); static_assert(alignof(CpuStateFrame::SynchronousFaultDataStruct) == 8, "This needs to be 8 bytes"); static_assert(offsetof(CpuStateFrame, SynchronousFaultData) % 8 == 0, "This needs to be aligned"); +static_assert(offsetof(CpuStateFrame, SynchronousFaultAddress) % 8 == 0, "This needs to be aligned"); } // namespace FEXCore::Core diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator/GuestFramesManagement.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator/GuestFramesManagement.cpp index 2401fe496d..8ce49f666e 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator/GuestFramesManagement.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SignalDelegator/GuestFramesManagement.cpp @@ -438,7 +438,11 @@ uint64_t SignalDelegator::SetupFrame_x64(FEXCore::Core::InternalThreadState* Thr // Overwrite si_code and si_addr guest_siginfo->si_code = Thread->CurrentFrame->SynchronousFaultData.si_code; - guest_siginfo->si_addr = reinterpret_cast(ContextBackup->OriginalRIP); + const bool IsGeneratedPageFault = Frame->SynchronousFaultData.Signal == FEXCore::Core::FAULT_SIGSEGV && + Frame->SynchronousFaultData.TrapNo == FEXCore::X86State::X86_TRAPNO_PF; + const uint64_t FaultAddress = + IsGeneratedPageFault && Frame->SynchronousFaultAddress ? Frame->SynchronousFaultAddress : ContextBackup->OriginalRIP; + guest_siginfo->si_addr = reinterpret_cast(FaultAddress); Signal = Frame->SynchronousFaultData.Signal; } else { guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_TRAPNO] = ConvertSignalToTrapNo(Signal, HostSigInfo); @@ -790,7 +794,12 @@ uint64_t SignalDelegator::SetupRTFrame_ia32(FEXCore::Core::InternalThreadState* case SigInfoLayout::LAYOUT_FAULT: // Macro expansion to get the si_addr // This is the address trying to be accessed, not the RIP - guest_uctx->info._sifields._sigfault.addr = static_cast(reinterpret_cast(HostSigInfo->si_addr)); + if (ContextBackup->FaultToTopAndGeneratedException && Frame->SynchronousFaultData.Signal == FEXCore::Core::FAULT_SIGSEGV && + Frame->SynchronousFaultData.TrapNo == FEXCore::X86State::X86_TRAPNO_PF && Frame->SynchronousFaultAddress) { + guest_uctx->info._sifields._sigfault.addr = static_cast(Frame->SynchronousFaultAddress); + } else { + guest_uctx->info._sifields._sigfault.addr = static_cast(reinterpret_cast(HostSigInfo->si_addr)); + } break; case SigInfoLayout::LAYOUT_FAULT_RIP: // Macro expansion to get the si_addr diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index ab3e7a35e0..65e637470c 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -497,7 +497,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); + Args->Rec = FEX::Windows::HandleGuestException(Fault, Thread->CurrentFrame->SynchronousFaultAddress, 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) { diff --git a/Source/Windows/Common/Exception.h b/Source/Windows/Common/Exception.h index 73bf5a9f30..9a7e00ac0e 100644 --- a/Source/Windows/Common/Exception.h +++ b/Source/Windows/Common/Exception.h @@ -10,7 +10,7 @@ namespace FEX::Windows { template -static inline EXCEPTION_RECORD HandleGuestException(FEXCore::Core::CpuStateFrame::SynchronousFaultDataStruct& Fault, +static inline EXCEPTION_RECORD HandleGuestException(FEXCore::Core::CpuStateFrame::SynchronousFaultDataStruct& Fault, uint64_t FaultAddress, const EXCEPTION_RECORD& Src, TReg& Rip, TReg Rax, TReg Cx) { EXCEPTION_RECORD Dst = Src; Dst.ExceptionAddress = reinterpret_cast(Rip); @@ -68,7 +68,7 @@ static inline EXCEPTION_RECORD HandleGuestException(FEXCore::Core::CpuStateFrame // A page-fault raised by an explicit break in JIT code is always an execute fault Dst.NumberParameters = 2; Dst.ExceptionInformation[0] = EXCEPTION_EXECUTE_FAULT; - Dst.ExceptionInformation[1] = Rip; + Dst.ExceptionInformation[1] = FaultAddress ? FaultAddress : Rip; return Dst; default: LogMan::Msg::EFmt("Unknown SIGSEGV trap: {}", Fault.TrapNo); break; } diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index 515a553b70..d2d1d92df2 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -926,7 +926,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); + *Exception = FEX::Windows::HandleGuestException(Fault, Thread->CurrentFrame->SynchronousFaultAddress, *Exception, WowContext.Eip, + WowContext.Eax, WowContext.Ecx); if (Exception->ExceptionCode == EXCEPTION_SINGLE_STEP) { WowContext.EFlags &= ~(1 << FEXCore::X86State::RFLAG_TF_RAW_LOC); } diff --git a/unittests/FEXLinuxTests/Known_Failures b/unittests/FEXLinuxTests/Known_Failures index 689a15a91a..9fcd3dd23c 100644 --- a/unittests/FEXLinuxTests/Known_Failures +++ b/unittests/FEXLinuxTests/Known_Failures @@ -23,6 +23,3 @@ sigtest_sigmask.64 # Disabled since FEX's FaultSafeMemcpy is intentionally stub-implemented syscalls_efault.32 syscalls_efault.64 - -# partial instruction decode is known to fail -noexec_protect.64