Merge pull request #5401 from Sonicadvance1/126

Linux/GuestFrames: Ensure MXCSR is saved and restored on signal
This commit is contained in:
LC authored and GitHub committed 2026-03-28 22:25:59 -04:00
commit 5b4a5969cc
5 files changed
+89 -6

No files matched your search

@@ -2756,7 +2756,7 @@ void OpDispatchBuilder::SaveSSEState(Ref MemBase) {
void OpDispatchBuilder::SaveMXCSRState(Ref MemBase) {
// Store MXCSR and the mask for all bits.
_StoreMemPairGPR(OpSize::i32Bit, GetMXCSR(), Constant(0xFFFF), MemBase, 24);
_StoreMemPairGPR(OpSize::i32Bit, GetMXCSR(), Constant(0xFFC0), MemBase, 24);
}
void OpDispatchBuilder::SaveAVXState(Ref MemBase) {
@@ -180,6 +180,10 @@ void SignalDelegator::RestoreFrame_x64(FEXCore::Core::InternalThreadState* Threa
CTX->SetXMMRegistersFromState(Thread, fpstate->_xmm, nullptr);
}
// Technically if mxcsr contains invalid bits then rt_sigreturn should return -EINVAL.
// TODO: FEX doesn't support this today.
Frame->State.mxcsr = fpstate->mxcsr & 0xFFC0;
// FCW store default
Frame->State.FCW = fpstate->fcw;
Frame->State.AbridgedFTW = fpstate->ftw;
@@ -254,6 +258,9 @@ void SignalDelegator::RestoreFrame_ia32(FEXCore::Core::InternalThreadState* Thre
CTX->SetXMMRegistersFromState(Thread, fpstate->_xmm, nullptr);
}
// Invalid bits are silently masked off in 32-bit.
Frame->State.mxcsr = fpstate->mxcsr & 0xFFC0;
// FCW store default
Frame->State.FCW = fpstate->fcw;
Frame->State.AbridgedFTW = FEXCore::FPState::ConvertToAbridgedFTW(fpstate->ftw);
@@ -330,6 +337,9 @@ void SignalDelegator::RestoreRTFrame_ia32(FEXCore::Core::InternalThreadState* Th
CTX->SetXMMRegistersFromState(Thread, fpstate->_xmm, nullptr);
}
// Invalid bits are silently masked off in 32-bit.
Frame->State.mxcsr = fpstate->mxcsr & 0xFFC0;
// FCW store default
Frame->State.FCW = fpstate->fcw;
Frame->State.AbridgedFTW = FEXCore::FPState::ConvertToAbridgedFTW(fpstate->ftw);
@@ -471,6 +481,10 @@ uint64_t SignalDelegator::SetupFrame_x64(FEXCore::Core::InternalThreadState* Thr
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, nullptr);
}
// Save mxcsr and the default mask.
fpstate->mxcsr = Frame->State.mxcsr;
fpstate->mxcsr_mask = 0xFFC0;
// FCW store default
fpstate->fcw = Frame->State.FCW;
fpstate->ftw = Frame->State.AbridgedFTW;
@@ -594,6 +608,8 @@ uint64_t SignalDelegator::SetupFrame_ia32(FEXCore::Core::InternalThreadState* Th
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, nullptr);
}
fpstate->mxcsr = Frame->State.mxcsr;
// FCW store default
fpstate->fcw = Frame->State.FCW;
// Reconstruct FSW
@@ -729,6 +745,8 @@ uint64_t SignalDelegator::SetupRTFrame_ia32(FEXCore::Core::InternalThreadState*
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, nullptr);
}
fpstate->mxcsr = Frame->State.mxcsr;
// FCW store default
fpstate->fcw = Frame->State.FCW;
// Reconstruct FSW
@@ -0,0 +1,65 @@
#include <catch2/catch_test_macros.hpp>
#include <cstdint>
#include <signal.h>
extern "C" void IntInstruction();
#pragma GCC diagnostic ignored "-Wattributes" // Suppress warning in case control-flow checks aren't enabled
__attribute__((naked, nocf_check)) static void InvalidINT() {
__asm volatile(R"(
IntInstruction:
hlt;
ret;
)");
}
__attribute__((noinline)) uint32_t GetMXCSR() {
uint32_t Result {};
asm volatile("stmxcsr %[Result]" : [Result] "=m"(Result)::"memory");
return Result;
}
void SetMXCSR(uint32_t MXCSR) {
asm volatile("ldmxcsr %[MXCSR]" ::[MXCSR] "m"(MXCSR) : "memory");
}
static uint32_t MXCSRFromContext {};
static void MXCSR_Handler(int signal, siginfo_t* siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
// Enable DAZ. This will get masked off on signal return.
SetMXCSR(0x1fc0);
#ifdef REG_RIP
#define FEX_IP_REG REG_RIP
#else
#define FEX_IP_REG REG_EIP
#endif
_context->uc_mcontext.gregs[FEX_IP_REG] += 1;
// Save the MXCSR from the context.
MXCSRFromContext = _context->uc_mcontext.fpregs->mxcsr;
#undef FEX_IP_REG
}
TEST_CASE("Signals: Restoring MXCSR") {
struct sigaction act {};
act.sa_sigaction = MXCSR_Handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
// Get the old MXCSR mask.
auto MXCSROld = GetMXCSR();
InvalidINT();
// Get the potentially broken MXCSR mask.
auto MXCSRNew = GetMXCSR();
// Ensure that all three masks match.
CHECK(MXCSRFromContext == MXCSROld);
CHECK(MXCSROld == MXCSRNew);
printf("0x%08x 0x%08x\n", MXCSROld, MXCSRNew);
}
@@ -1343,8 +1343,8 @@
"stp q28, q29, [x4, #352]",
"stp q30, q31, [x4, #384]",
"ldr w20, [x28, #972]",
"mov w21, #0xffc0",
"and w20, w20, #0xffc0",
"mov w21, #0xffff",
"stp w20, w21, [x4, #24]"
]
},
@@ -1583,8 +1583,8 @@
"cbnz x20, #+0x8",
"b #+0x14",
"ldr w20, [x28, #972]",
"mov w21, #0xffc0",
"and w20, w20, #0xffc0",
"mov w21, #0xffff",
"stp w20, w21, [x4, #24]",
"ubfx x20, x4, #0, #3",
"str x20, [x4, #512]"
@@ -1687,8 +1687,8 @@
"cbnz x20, #+0x8",
"b #+0x14",
"ldr w20, [x28, #972]",
"mov w21, #0xffc0",
"and w20, w20, #0xffc0",
"mov w21, #0xffff",
"stp w20, w21, [x4, #24]",
"ubfx x20, x4, #0, #3",
"str x20, [x4, #512]"
@@ -1615,8 +1615,8 @@
"stp q28, q29, [x4, #352]",
"stp q30, q31, [x4, #384]",
"ldr w20, [x28, #972]",
"mov w21, #0xffc0",
"and w20, w20, #0xffc0",
"mov w21, #0xffff",
"stp w20, w21, [x4, #24]"
]
},
@@ -1855,8 +1855,8 @@
"cbnz x20, #+0x8",
"b #+0x14",
"ldr w20, [x28, #972]",
"mov w21, #0xffc0",
"and w20, w20, #0xffc0",
"mov w21, #0xffff",
"stp w20, w21, [x4, #24]",
"ubfx x20, x4, #0, #3",
"str x20, [x4, #512]"