Linux/GuestFrames: Ensure MXCSR is saved and restored on signal

This was causing an unfortunate set of circumstances where Dark Souls
III was modifying MXCSR and we weren't saving it, cause the value to change
from 0x9fc0 to 0.

This "enabled" float exceptions by unmasking the exception masks in
MXCSR. This in turn had Dark Souls III's `expf` function to fault out,
as it checks if the MXCSR exception masks are set or not for determining
if underflow should assert or not.

Wow64/arm64ec has a similar problem where it always sets back to default
on signal. Which means game lose DAZ, but I'm not fixing that bug right
now.

Fixes #5391
This commit is contained in:
Ryan Houdek committed 2026-03-28 17:44:45 -07:00
1 parent f0854a16fe
commit f78e194cf7
1 file changed
+18
@@ -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