mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 17:00:19 +02:00
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
857 lines
37 KiB
C++
857 lines
37 KiB
C++
// SPDX-License-Identifier: MIT
|
|
/*
|
|
$info$
|
|
tags: LinuxSyscalls|common
|
|
desc: Handles host -> host and host -> guest signal routing, emulates procmask & co
|
|
$end_info$
|
|
*/
|
|
|
|
#include "LinuxSyscalls/SignalDelegator.h"
|
|
|
|
#include <FEXCore/Core/Context.h>
|
|
#include <FEXCore/Core/SignalDelegator.h>
|
|
#include <FEXCore/Debug/InternalThreadState.h>
|
|
#include <FEXCore/Utils/ArchHelpers/Arm64.h>
|
|
#include <FEXCore/Utils/FPState.h>
|
|
#include <FEXCore/Utils/MathUtils.h>
|
|
|
|
#include <csignal>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
namespace FEX::HLE {
|
|
constexpr uint32_t X86_SA_RESTORER = 0x04000000;
|
|
// Total number of layouts that siginfo supports.
|
|
enum class SigInfoLayout {
|
|
LAYOUT_KILL,
|
|
LAYOUT_TIMER,
|
|
LAYOUT_POLL,
|
|
LAYOUT_FAULT,
|
|
LAYOUT_FAULT_RIP,
|
|
LAYOUT_CHLD,
|
|
LAYOUT_RT,
|
|
LAYOUT_SYS,
|
|
};
|
|
|
|
// Calculate the siginfo layout based on Signal and si_code.
|
|
static SigInfoLayout CalculateSigInfoLayout(int Signal, int si_code, uint32_t err_code) {
|
|
if (si_code > SI_USER && si_code < SI_KERNEL) {
|
|
// For signals that are not considered RT.
|
|
if (Signal == SIGSEGV || Signal == SIGBUS || Signal == SIGTRAP) {
|
|
if (err_code & FEXCore::X86State::X86_PF_INSTR) {
|
|
// Fault layout but addr refers to RIP.
|
|
return SigInfoLayout::LAYOUT_FAULT_RIP;
|
|
} else {
|
|
// Regular FAULT layout.
|
|
return SigInfoLayout::LAYOUT_FAULT;
|
|
}
|
|
} else if (Signal == SIGILL || Signal == SIGFPE) {
|
|
// Fault layout but addr refers to RIP.
|
|
return SigInfoLayout::LAYOUT_FAULT_RIP;
|
|
} else if (Signal == SIGCHLD) {
|
|
// Child layout
|
|
return SigInfoLayout::LAYOUT_CHLD;
|
|
} else if (Signal == SIGPOLL) {
|
|
// Poll layout
|
|
return SigInfoLayout::LAYOUT_POLL;
|
|
} else if (Signal == SIGSYS) {
|
|
// Sys layout
|
|
return SigInfoLayout::LAYOUT_SYS;
|
|
}
|
|
} else {
|
|
// Negative si_codes are kernel specific things.
|
|
if (si_code == SI_TIMER) {
|
|
return SigInfoLayout::LAYOUT_TIMER;
|
|
} else if (si_code == SI_SIGIO) {
|
|
return SigInfoLayout::LAYOUT_POLL;
|
|
} else if (si_code < 0) {
|
|
return SigInfoLayout::LAYOUT_RT;
|
|
}
|
|
}
|
|
|
|
return SigInfoLayout::LAYOUT_KILL;
|
|
}
|
|
|
|
static uint32_t ConvertSignalToTrapNo(int Signal, siginfo_t* HostSigInfo) {
|
|
switch (Signal) {
|
|
case SIGSEGV:
|
|
if (HostSigInfo->si_code == SEGV_MAPERR || HostSigInfo->si_code == SEGV_ACCERR) {
|
|
// Protection fault
|
|
return FEXCore::X86State::X86_TRAPNO_PF;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Unknown mapping, fall back to old behaviour and just pass signal
|
|
return Signal;
|
|
}
|
|
|
|
static uint32_t ConvertSignalToError(void* ucontext, int Signal, siginfo_t* HostSigInfo) {
|
|
switch (Signal) {
|
|
case SIGSEGV:
|
|
if (HostSigInfo->si_code == SEGV_MAPERR || HostSigInfo->si_code == SEGV_ACCERR) {
|
|
// Protection fault
|
|
// Always a user fault for us
|
|
return ArchHelpers::Context::GetProtectFlags(ucontext);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Not a page fault issue
|
|
return 0;
|
|
}
|
|
|
|
template<typename T>
|
|
static void SetXStateInfo(T* xstate, bool is_avx_enabled) {
|
|
auto* fpstate = &xstate->fpstate;
|
|
|
|
fpstate->sw_reserved.magic1 = is_avx_enabled ? FEXCore::x86_64::fpx_sw_bytes::FP_XSTATE_MAGIC_1 : 0;
|
|
fpstate->sw_reserved.extended_size = is_avx_enabled ? sizeof(T) : 0;
|
|
|
|
fpstate->sw_reserved.xfeatures |= FEXCore::x86_64::fpx_sw_bytes::FEATURE_FP | FEXCore::x86_64::fpx_sw_bytes::FEATURE_SSE;
|
|
if (is_avx_enabled) {
|
|
fpstate->sw_reserved.xfeatures |= FEXCore::x86_64::fpx_sw_bytes::FEATURE_YMM;
|
|
}
|
|
|
|
fpstate->sw_reserved.xstate_size = fpstate->sw_reserved.extended_size;
|
|
|
|
if (is_avx_enabled) {
|
|
xstate->xstate_hdr.xfeatures = 0;
|
|
xstate->magic2.magic = FEXCore::x86_64::fpx_sw_bytes::FP_XSTATE_MAGIC_2;
|
|
}
|
|
}
|
|
|
|
void SignalDelegator::RestoreFrame_x64(FEXCore::Core::InternalThreadState* Thread, ArchHelpers::Context::ContextBackup* Context,
|
|
FEXCore::Core::CpuStateFrame* Frame, void* ucontext) {
|
|
auto* guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(Context->UContextLocation);
|
|
[[maybe_unused]] auto* guest_siginfo = reinterpret_cast<siginfo_t*>(Context->SigInfoLocation);
|
|
|
|
// If the guest modified the RIP then we need to take special precautions here
|
|
if (Context->OriginalRIP != guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP] || Context->FaultToTopAndGeneratedException) {
|
|
|
|
// Restore previous `InSyscallInfo` structure.
|
|
Frame->InSyscallInfo = Context->InSyscallInfo;
|
|
|
|
// Hack! Go back to the top of the dispatcher top
|
|
// This is only safe inside the JIT rather than anything outside of it
|
|
ArchHelpers::Context::SetPc(ucontext, Config.AbsoluteLoopTopAddressFillSRA);
|
|
ArchHelpers::Context::SetFillSRASingleInst(ucontext, false);
|
|
// Set our state register to point to our guest thread data
|
|
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
|
|
|
|
Frame->State.rip = guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP];
|
|
|
|
// Restore segments.
|
|
// FS and GS are explicitly ignored here, as WRFSGSbase is used instead.
|
|
Frame->State.cs_idx = (guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_CSGSFS] >> 0) & 0xffff;
|
|
Frame->State.ss_idx = (guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_CSGSFS] >> 48) & 0xffff;
|
|
|
|
Frame->State.cs_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.cs_idx));
|
|
Frame->State.ss_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.ss_idx));
|
|
|
|
// XXX: Full context setting
|
|
CTX->SetFlagsFromCompactedEFLAGS(Thread, guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_EFL]);
|
|
|
|
#define COPY_REG(x) Frame->State.gregs[FEXCore::X86State::REG_##x] = guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x];
|
|
COPY_REG(R8);
|
|
COPY_REG(R9);
|
|
COPY_REG(R10);
|
|
COPY_REG(R11);
|
|
COPY_REG(R12);
|
|
COPY_REG(R13);
|
|
COPY_REG(R14);
|
|
COPY_REG(R15);
|
|
COPY_REG(RDI);
|
|
COPY_REG(RSI);
|
|
COPY_REG(RBP);
|
|
COPY_REG(RBX);
|
|
COPY_REG(RDX);
|
|
COPY_REG(RAX);
|
|
COPY_REG(RCX);
|
|
COPY_REG(RSP);
|
|
#undef COPY_REG
|
|
auto* xstate = reinterpret_cast<FEXCore::x86_64::xstate*>(guest_uctx->uc_mcontext.fpregs);
|
|
auto* fpstate = &xstate->fpstate;
|
|
|
|
if (SupportsAVX) {
|
|
CTX->SetXMMRegistersFromState(Thread, fpstate->_xmm, xstate->ymmh.ymmh_space);
|
|
} else {
|
|
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;
|
|
|
|
// Deconstruct FSW
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_IE_LOC] = fpstate->fsw & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] = (fpstate->fsw >> 8) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] = (fpstate->fsw >> 9) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] = (fpstate->fsw >> 10) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] = (fpstate->fsw >> 14) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] = (fpstate->fsw >> 11) & 0b111;
|
|
|
|
// Copy float registers
|
|
const uint16_t CurrentOffset = Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC];
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
|
const auto modulo_i = (i + CurrentOffset) % 8;
|
|
memcpy(&Frame->State.mm[modulo_i], &fpstate->_st[i], sizeof(Frame->State.mm[0]));
|
|
}
|
|
}
|
|
}
|
|
|
|
void SignalDelegator::RestoreFrame_ia32(FEXCore::Core::InternalThreadState* Thread, ArchHelpers::Context::ContextBackup* Context,
|
|
FEXCore::Core::CpuStateFrame* Frame, void* ucontext) {
|
|
SigFrame_i32* guest_uctx = reinterpret_cast<SigFrame_i32*>(Context->UContextLocation);
|
|
// If the guest modified the RIP then we need to take special precautions here
|
|
if (Context->OriginalRIP != guest_uctx->sc.ip || Context->FaultToTopAndGeneratedException) {
|
|
// Restore previous `InSyscallInfo` structure.
|
|
Frame->InSyscallInfo = Context->InSyscallInfo;
|
|
|
|
// Hack! Go back to the top of the dispatcher top
|
|
// This is only safe inside the JIT rather than anything outside of it
|
|
ArchHelpers::Context::SetPc(ucontext, Config.AbsoluteLoopTopAddressFillSRA);
|
|
ArchHelpers::Context::SetFillSRASingleInst(ucontext, false);
|
|
// Set our state register to point to our guest thread data
|
|
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
|
|
|
|
// XXX: Full context setting
|
|
CTX->SetFlagsFromCompactedEFLAGS(Thread, guest_uctx->sc.flags);
|
|
|
|
Frame->State.rip = guest_uctx->sc.ip;
|
|
Frame->State.cs_idx = guest_uctx->sc.cs;
|
|
Frame->State.ds_idx = guest_uctx->sc.ds;
|
|
Frame->State.es_idx = guest_uctx->sc.es;
|
|
Frame->State.fs_idx = guest_uctx->sc.fs;
|
|
Frame->State.gs_idx = guest_uctx->sc.gs;
|
|
Frame->State.ss_idx = guest_uctx->sc.ss;
|
|
|
|
Frame->State.cs_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.cs_idx));
|
|
Frame->State.ds_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.ds_idx));
|
|
Frame->State.es_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.es_idx));
|
|
Frame->State.fs_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.fs_idx));
|
|
Frame->State.gs_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.gs_idx));
|
|
Frame->State.ss_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.ss_idx));
|
|
|
|
#define COPY_REG(x, y) Frame->State.gregs[FEXCore::X86State::REG_##x] = guest_uctx->sc.y;
|
|
COPY_REG(RDI, di);
|
|
COPY_REG(RSI, si);
|
|
COPY_REG(RBP, bp);
|
|
COPY_REG(RBX, bx);
|
|
COPY_REG(RDX, dx);
|
|
COPY_REG(RAX, ax);
|
|
COPY_REG(RCX, cx);
|
|
COPY_REG(RSP, sp);
|
|
#undef COPY_REG
|
|
auto* xstate = reinterpret_cast<FEXCore::x86::xstate*>(guest_uctx->sc.fpstate);
|
|
auto* fpstate = &xstate->fpstate;
|
|
|
|
// Extended XMM state
|
|
if (SupportsAVX) {
|
|
CTX->SetXMMRegistersFromState(Thread, fpstate->_xmm, xstate->ymmh.ymmh_space);
|
|
} else {
|
|
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);
|
|
|
|
// Deconstruct FSW
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_IE_LOC] = fpstate->fsw & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] = (fpstate->fsw >> 8) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] = (fpstate->fsw >> 9) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] = (fpstate->fsw >> 10) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] = (fpstate->fsw >> 14) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] = (fpstate->fsw >> 11) & 0b111;
|
|
|
|
// Copy float registers
|
|
const uint16_t CurrentOffset = Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC];
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
|
// 32-bit st register size is only 10 bytes. Not padded to 16byte like x86-64
|
|
const auto modulo_i = (i + CurrentOffset) % 8;
|
|
memcpy(&Frame->State.mm[modulo_i], &fpstate->_st[i], 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SignalDelegator::RestoreRTFrame_ia32(FEXCore::Core::InternalThreadState* Thread, ArchHelpers::Context::ContextBackup* Context,
|
|
FEXCore::Core::CpuStateFrame* Frame, void* ucontext) {
|
|
RTSigFrame_i32* guest_uctx = reinterpret_cast<RTSigFrame_i32*>(Context->UContextLocation);
|
|
// If the guest modified the RIP then we need to take special precautions here
|
|
if (Context->OriginalRIP != guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP] || Context->FaultToTopAndGeneratedException) {
|
|
|
|
// Restore previous `InSyscallInfo` structure.
|
|
Frame->InSyscallInfo = Context->InSyscallInfo;
|
|
|
|
// Hack! Go back to the top of the dispatcher top
|
|
// This is only safe inside the JIT rather than anything outside of it
|
|
ArchHelpers::Context::SetPc(ucontext, Config.AbsoluteLoopTopAddressFillSRA);
|
|
ArchHelpers::Context::SetFillSRASingleInst(ucontext, false);
|
|
// Set our state register to point to our guest thread data
|
|
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
|
|
|
|
// XXX: Full context setting
|
|
CTX->SetFlagsFromCompactedEFLAGS(Thread, guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EFL]);
|
|
|
|
Frame->State.rip = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP];
|
|
Frame->State.cs_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_CS];
|
|
Frame->State.ds_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_DS];
|
|
Frame->State.es_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_ES];
|
|
Frame->State.fs_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_FS];
|
|
Frame->State.gs_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_GS];
|
|
Frame->State.ss_idx = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_SS];
|
|
|
|
Frame->State.cs_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.cs_idx));
|
|
Frame->State.ds_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.ds_idx));
|
|
Frame->State.es_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.es_idx));
|
|
Frame->State.fs_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.fs_idx));
|
|
Frame->State.gs_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.gs_idx));
|
|
Frame->State.ss_cached = Frame->State.CalculateGDTBase(*Frame->State.GetSegmentFromIndex(Frame->State, Frame->State.ss_idx));
|
|
|
|
#define COPY_REG(x) Frame->State.gregs[FEXCore::X86State::REG_##x] = guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_##x];
|
|
COPY_REG(RDI);
|
|
COPY_REG(RSI);
|
|
COPY_REG(RBP);
|
|
COPY_REG(RBX);
|
|
COPY_REG(RDX);
|
|
COPY_REG(RAX);
|
|
COPY_REG(RCX);
|
|
COPY_REG(RSP);
|
|
#undef COPY_REG
|
|
auto* xstate = reinterpret_cast<FEXCore::x86::xstate*>(guest_uctx->uc.uc_mcontext.fpregs);
|
|
auto* fpstate = &xstate->fpstate;
|
|
|
|
// Extended XMM state
|
|
if (SupportsAVX) {
|
|
CTX->SetXMMRegistersFromState(Thread, fpstate->_xmm, xstate->ymmh.ymmh_space);
|
|
} else {
|
|
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);
|
|
|
|
// Deconstruct FSW
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_IE_LOC] = fpstate->fsw & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] = (fpstate->fsw >> 8) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] = (fpstate->fsw >> 9) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] = (fpstate->fsw >> 10) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] = (fpstate->fsw >> 14) & 1;
|
|
Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] = (fpstate->fsw >> 11) & 0b111;
|
|
|
|
// Copy float registers
|
|
const uint16_t CurrentOffset = Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC];
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
|
// 32-bit st register size is only 10 bytes. Not padded to 16byte like x86-64
|
|
const auto modulo_i = (i + CurrentOffset) % 8;
|
|
memcpy(&Frame->State.mm[modulo_i], &fpstate->_st[i], 10);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint64_t SignalDelegator::SetupFrame_x64(FEXCore::Core::InternalThreadState* Thread, ArchHelpers::Context::ContextBackup* ContextBackup,
|
|
FEXCore::Core::CpuStateFrame* Frame, int Signal, siginfo_t* HostSigInfo, void* ucontext,
|
|
GuestSigAction* GuestAction, stack_t* GuestStack, uint64_t NewGuestSP, const uint32_t eflags) {
|
|
|
|
// Back up past the redzone, which is 128bytes
|
|
// 32-bit doesn't have a redzone
|
|
NewGuestSP -= 128;
|
|
|
|
// On 64-bit the kernel sets up the siginfo_t and ucontext_t regardless of SA_SIGINFO set.
|
|
// This allows the application to /always/ get the siginfo and ucontext even if it didn't set this flag.
|
|
//
|
|
// Signal frame layout on stack needs to be as follows
|
|
// void* ReturnPointer
|
|
// ucontext_t
|
|
// siginfo_t
|
|
// FP state
|
|
// Host stack location
|
|
NewGuestSP -= sizeof(uint64_t);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(uint64_t));
|
|
|
|
uint64_t HostStackLocation = NewGuestSP;
|
|
|
|
if (SupportsAVX) {
|
|
NewGuestSP -= sizeof(FEXCore::x86_64::xstate);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(FEXCore::x86_64::xstate));
|
|
} else {
|
|
NewGuestSP -= sizeof(FEXCore::x86_64::_libc_fpstate);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(FEXCore::x86_64::_libc_fpstate));
|
|
}
|
|
|
|
uint64_t FPStateLocation = NewGuestSP;
|
|
|
|
NewGuestSP -= sizeof(siginfo_t);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(siginfo_t));
|
|
uint64_t SigInfoLocation = NewGuestSP;
|
|
|
|
NewGuestSP -= sizeof(FEXCore::x86_64::ucontext_t);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(FEXCore::x86_64::ucontext_t));
|
|
uint64_t UContextLocation = NewGuestSP;
|
|
|
|
ContextBackup->FPStateLocation = FPStateLocation;
|
|
ContextBackup->UContextLocation = UContextLocation;
|
|
ContextBackup->SigInfoLocation = SigInfoLocation;
|
|
|
|
FEXCore::x86_64::ucontext_t* guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
|
|
siginfo_t* guest_siginfo = reinterpret_cast<siginfo_t*>(SigInfoLocation);
|
|
// Store where the host context lives in the guest stack.
|
|
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
|
|
|
|
// We have extended float information
|
|
guest_uctx->uc_flags = FEXCore::x86_64::UC_FP_XSTATE | FEXCore::x86_64::UC_SIGCONTEXT_SS | FEXCore::x86_64::UC_STRICT_RESTORE_SS;
|
|
|
|
// Pointer to where the fpreg memory is
|
|
guest_uctx->uc_mcontext.fpregs = reinterpret_cast<FEXCore::x86_64::_libc_fpstate*>(FPStateLocation);
|
|
auto* xstate = reinterpret_cast<FEXCore::x86_64::xstate*>(FPStateLocation);
|
|
SetXStateInfo(xstate, SupportsAVX);
|
|
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP] = ContextBackup->OriginalRIP;
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_EFL] = eflags;
|
|
// This stores the CS/GS/FS selectors. It ALSO stores the SS selector in the top 16 bits...For some reason.
|
|
// Despite the naming, the endianness is swapped from what you'd expect.
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_CSGSFS] =
|
|
((uint64_t)Frame->State.ss_idx << 48) | ((uint64_t)Frame->State.fs_idx << 32) | ((uint64_t)Frame->State.gs_idx << 16) |
|
|
((uint64_t)Frame->State.cs_idx << 0);
|
|
|
|
// aarch64 and x86_64 siginfo_t matches. We can just copy this over
|
|
// SI_USER could also potentially have random data in it, needs to be bit perfect
|
|
// For guest faults we don't have a real way to reconstruct state to a real guest RIP
|
|
*guest_siginfo = *HostSigInfo;
|
|
|
|
if (ContextBackup->FaultToTopAndGeneratedException) {
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_TRAPNO] = Frame->SynchronousFaultData.TrapNo;
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_ERR] = Frame->SynchronousFaultData.err_code;
|
|
|
|
// Overwrite si_code and si_addr
|
|
guest_siginfo->si_code = Thread->CurrentFrame->SynchronousFaultData.si_code;
|
|
guest_siginfo->si_addr = reinterpret_cast<void*>(ContextBackup->OriginalRIP);
|
|
Signal = Frame->SynchronousFaultData.Signal;
|
|
} else {
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_TRAPNO] = ConvertSignalToTrapNo(Signal, HostSigInfo);
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_ERR] = ConvertSignalToError(ucontext, Signal, HostSigInfo);
|
|
}
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_OLDMASK] = 0;
|
|
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_CR2] = 0;
|
|
|
|
#define COPY_REG(x) guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_##x] = Frame->State.gregs[FEXCore::X86State::REG_##x];
|
|
COPY_REG(R8);
|
|
COPY_REG(R9);
|
|
COPY_REG(R10);
|
|
COPY_REG(R11);
|
|
COPY_REG(R12);
|
|
COPY_REG(R13);
|
|
COPY_REG(R14);
|
|
COPY_REG(R15);
|
|
COPY_REG(RDI);
|
|
COPY_REG(RSI);
|
|
COPY_REG(RBP);
|
|
COPY_REG(RBX);
|
|
COPY_REG(RDX);
|
|
COPY_REG(RAX);
|
|
COPY_REG(RCX);
|
|
COPY_REG(RSP);
|
|
#undef COPY_REG
|
|
|
|
auto* fpstate = &xstate->fpstate;
|
|
|
|
// Copy float registers
|
|
const uint16_t CurrentOffset = Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC];
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
|
const auto modulo_i = (i + CurrentOffset) % 8;
|
|
memcpy(&fpstate->_st[i], &Frame->State.mm[modulo_i], sizeof(Frame->State.mm[0]));
|
|
}
|
|
|
|
if (SupportsAVX) {
|
|
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, xstate->ymmh.ymmh_space);
|
|
} else {
|
|
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;
|
|
|
|
// Reconstruct FSW
|
|
fpstate->fsw = (Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) | (Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
|
|
(Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) | (Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
|
|
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14) | Frame->State.flags[FEXCore::X86State::X87FLAG_IE_LOC];
|
|
|
|
// Copy over signal stack information
|
|
guest_uctx->uc_stack.ss_flags = GuestStack->ss_flags;
|
|
guest_uctx->uc_stack.ss_sp = GuestStack->ss_sp;
|
|
guest_uctx->uc_stack.ss_size = GuestStack->ss_size;
|
|
|
|
// Apparently RAX is always set to zero in case of badly misbehaving C applications and variadics.
|
|
Frame->State.gregs[FEXCore::X86State::REG_RAX] = 0;
|
|
Frame->State.gregs[FEXCore::X86State::REG_RDI] = Signal;
|
|
Frame->State.gregs[FEXCore::X86State::REG_RSI] = SigInfoLocation;
|
|
Frame->State.gregs[FEXCore::X86State::REG_RDX] = UContextLocation;
|
|
|
|
// Set up the new SP for stack handling
|
|
// The host is required to provide us a restorer.
|
|
// If the guest didn't provide a restorer then the application should fail with a SIGSEGV.
|
|
// TODO: Emulate SIGSEGV when the guest doesn't provide a restorer.
|
|
NewGuestSP -= 8;
|
|
if (GuestAction->restorer) {
|
|
*(uint64_t*)NewGuestSP = (uint64_t)GuestAction->restorer;
|
|
} else {
|
|
// XXX: Emulate SIGSEGV here
|
|
// *(uint64_t*)NewGuestSP = SignalReturn;
|
|
}
|
|
|
|
return NewGuestSP;
|
|
}
|
|
|
|
uint64_t SignalDelegator::SetupFrame_ia32(FEXCore::Core::InternalThreadState* Thread, ArchHelpers::Context::ContextBackup* ContextBackup,
|
|
FEXCore::Core::CpuStateFrame* Frame, int Signal, siginfo_t* HostSigInfo, void* ucontext,
|
|
GuestSigAction* GuestAction, stack_t* GuestStack, uint64_t NewGuestSP, const uint32_t eflags) {
|
|
|
|
const uint64_t SignalReturn = reinterpret_cast<uint64_t>(VDSOPointers.VDSO_kernel_sigreturn);
|
|
|
|
NewGuestSP -= sizeof(uint64_t);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(uint64_t));
|
|
|
|
uint64_t HostStackLocation = NewGuestSP;
|
|
|
|
if (SupportsAVX) {
|
|
NewGuestSP -= sizeof(FEXCore::x86::xstate);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(FEXCore::x86::xstate));
|
|
} else {
|
|
NewGuestSP -= sizeof(FEXCore::x86::_libc_fpstate);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(FEXCore::x86::_libc_fpstate));
|
|
}
|
|
|
|
uint64_t FPStateLocation = NewGuestSP;
|
|
|
|
NewGuestSP -= sizeof(SigFrame_i32);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(SigFrame_i32));
|
|
uint64_t SigFrameLocation = NewGuestSP;
|
|
|
|
ContextBackup->FPStateLocation = FPStateLocation;
|
|
ContextBackup->UContextLocation = SigFrameLocation;
|
|
ContextBackup->SigInfoLocation = 0;
|
|
|
|
SigFrame_i32* guest_uctx = reinterpret_cast<SigFrame_i32*>(SigFrameLocation);
|
|
// Store where the host context lives in the guest stack.
|
|
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
|
|
|
|
// Pointer to where the fpreg memory is
|
|
guest_uctx->sc.fpstate = static_cast<uint32_t>(FPStateLocation);
|
|
auto* xstate = reinterpret_cast<FEXCore::x86::xstate*>(FPStateLocation);
|
|
SetXStateInfo(xstate, SupportsAVX);
|
|
|
|
guest_uctx->sc.cs = Frame->State.cs_idx;
|
|
guest_uctx->sc.ds = Frame->State.ds_idx;
|
|
guest_uctx->sc.es = Frame->State.es_idx;
|
|
guest_uctx->sc.fs = Frame->State.fs_idx;
|
|
guest_uctx->sc.gs = Frame->State.gs_idx;
|
|
guest_uctx->sc.ss = Frame->State.ss_idx;
|
|
|
|
if (ContextBackup->FaultToTopAndGeneratedException) {
|
|
guest_uctx->sc.trapno = Frame->SynchronousFaultData.TrapNo;
|
|
guest_uctx->sc.err = Frame->SynchronousFaultData.err_code;
|
|
Signal = Frame->SynchronousFaultData.Signal;
|
|
} else {
|
|
guest_uctx->sc.trapno = ConvertSignalToTrapNo(Signal, HostSigInfo);
|
|
guest_uctx->sc.err = ConvertSignalToError(ucontext, Signal, HostSigInfo);
|
|
}
|
|
|
|
guest_uctx->sc.ip = ContextBackup->OriginalRIP;
|
|
guest_uctx->sc.flags = eflags;
|
|
guest_uctx->sc.sp_at_signal = 0;
|
|
|
|
#define COPY_REG(x, y) guest_uctx->sc.x = Frame->State.gregs[FEXCore::X86State::REG_##y];
|
|
COPY_REG(di, RDI);
|
|
COPY_REG(si, RSI);
|
|
COPY_REG(bp, RBP);
|
|
COPY_REG(bx, RBX);
|
|
COPY_REG(dx, RDX);
|
|
COPY_REG(ax, RAX);
|
|
COPY_REG(cx, RCX);
|
|
COPY_REG(sp, RSP);
|
|
#undef COPY_REG
|
|
|
|
auto* fpstate = &xstate->fpstate;
|
|
|
|
// Copy float registers
|
|
const uint16_t CurrentOffset = Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC];
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
|
const auto modulo_i = (i + CurrentOffset) % 8;
|
|
// 32-bit st register size is only 10 bytes. Not padded to 16byte like x86-64
|
|
memcpy(&fpstate->_st[i], &Frame->State.mm[modulo_i], 10);
|
|
}
|
|
|
|
// Extended XMM state
|
|
fpstate->status = FEXCore::x86::fpstate_magic::MAGIC_XFPSTATE;
|
|
|
|
if (SupportsAVX) {
|
|
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, xstate->ymmh.ymmh_space);
|
|
} else {
|
|
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, nullptr);
|
|
}
|
|
|
|
fpstate->mxcsr = Frame->State.mxcsr;
|
|
|
|
// FCW store default
|
|
fpstate->fcw = Frame->State.FCW;
|
|
// Reconstruct FSW
|
|
fpstate->fsw = (Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) | (Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
|
|
(Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) | (Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
|
|
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14) | Frame->State.flags[FEXCore::X86State::X87FLAG_IE_LOC];
|
|
fpstate->ftw = FEXCore::FPState::ConvertFromAbridgedFTW(fpstate->fsw, Frame->State.mm, Frame->State.AbridgedFTW);
|
|
|
|
// Curiously non-rt signals don't support altstack. So that state doesn't exist here.
|
|
|
|
// Copy over the signal information.
|
|
guest_uctx->Signal = Signal;
|
|
|
|
// Retcode needs to be bit-exact for debuggers
|
|
constexpr static uint8_t retcode[] = {
|
|
0x58, // pop eax
|
|
0xb8, // mov
|
|
0x77, 0x00, 0x00, 0x00, // 32-bit sigreturn
|
|
0xcd, 0x80, // int 0x80
|
|
};
|
|
|
|
memcpy(guest_uctx->retcode, &retcode, sizeof(retcode));
|
|
|
|
// 32-bit Guest can provide its own restorer or we need to provide our own.
|
|
// On a real host this restorer will live in VDSO.
|
|
const bool HasRestorer = (GuestAction->sa_flags & X86_SA_RESTORER) == X86_SA_RESTORER;
|
|
if (HasRestorer) {
|
|
guest_uctx->pretcode = (uint32_t)(uint64_t)GuestAction->restorer;
|
|
} else {
|
|
guest_uctx->pretcode = SignalReturn;
|
|
LOGMAN_THROW_A_FMT(SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
|
|
}
|
|
|
|
// Support regparm=3
|
|
Frame->State.gregs[FEXCore::X86State::REG_RAX] = Signal;
|
|
Frame->State.gregs[FEXCore::X86State::REG_RDX] = 0;
|
|
Frame->State.gregs[FEXCore::X86State::REG_RCX] = 0;
|
|
|
|
return NewGuestSP;
|
|
}
|
|
|
|
uint64_t SignalDelegator::SetupRTFrame_ia32(FEXCore::Core::InternalThreadState* Thread, ArchHelpers::Context::ContextBackup* ContextBackup,
|
|
FEXCore::Core::CpuStateFrame* Frame, int Signal, siginfo_t* HostSigInfo, void* ucontext,
|
|
GuestSigAction* GuestAction, stack_t* GuestStack, uint64_t NewGuestSP, const uint32_t eflags) {
|
|
|
|
const uint64_t SignalReturn = reinterpret_cast<uint64_t>(VDSOPointers.VDSO_kernel_rt_sigreturn);
|
|
|
|
NewGuestSP -= sizeof(uint64_t);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(uint64_t));
|
|
|
|
uint64_t HostStackLocation = NewGuestSP;
|
|
|
|
if (SupportsAVX) {
|
|
NewGuestSP -= sizeof(FEXCore::x86::xstate);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(FEXCore::x86::xstate));
|
|
} else {
|
|
NewGuestSP -= sizeof(FEXCore::x86::_libc_fpstate);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(FEXCore::x86::_libc_fpstate));
|
|
}
|
|
|
|
uint64_t FPStateLocation = NewGuestSP;
|
|
|
|
NewGuestSP -= sizeof(RTSigFrame_i32);
|
|
NewGuestSP = FEXCore::AlignDown(NewGuestSP, alignof(RTSigFrame_i32));
|
|
|
|
uint64_t SigFrameLocation = NewGuestSP;
|
|
RTSigFrame_i32* guest_uctx = reinterpret_cast<RTSigFrame_i32*>(SigFrameLocation);
|
|
// Store where the host context lives in the guest stack.
|
|
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
|
|
|
|
ContextBackup->FPStateLocation = FPStateLocation;
|
|
ContextBackup->UContextLocation = SigFrameLocation;
|
|
ContextBackup->SigInfoLocation = 0; // Part of frame.
|
|
|
|
// We have extended float information
|
|
guest_uctx->uc.uc_flags = FEXCore::x86::UC_FP_XSTATE;
|
|
guest_uctx->uc.uc_link = 0;
|
|
|
|
// Pointer to where the fpreg memory is
|
|
guest_uctx->uc.uc_mcontext.fpregs = static_cast<uint32_t>(FPStateLocation);
|
|
auto* xstate = reinterpret_cast<FEXCore::x86::xstate*>(FPStateLocation);
|
|
SetXStateInfo(xstate, SupportsAVX);
|
|
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_CS] = Frame->State.cs_idx;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_DS] = Frame->State.ds_idx;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_ES] = Frame->State.es_idx;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_FS] = Frame->State.fs_idx;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_GS] = Frame->State.gs_idx;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_SS] = Frame->State.ss_idx;
|
|
|
|
if (ContextBackup->FaultToTopAndGeneratedException) {
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_TRAPNO] = Frame->SynchronousFaultData.TrapNo;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR] = Frame->SynchronousFaultData.err_code;
|
|
Signal = Frame->SynchronousFaultData.Signal;
|
|
} else {
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_TRAPNO] = ConvertSignalToTrapNo(Signal, HostSigInfo);
|
|
guest_uctx->info.si_code = HostSigInfo->si_code;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR] = ConvertSignalToError(ucontext, Signal, HostSigInfo);
|
|
}
|
|
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP] = ContextBackup->OriginalRIP;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EFL] = eflags;
|
|
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_UESP] = Frame->State.gregs[FEXCore::X86State::REG_RSP];
|
|
guest_uctx->uc.uc_mcontext.cr2 = 0;
|
|
|
|
#define COPY_REG(x) guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_##x] = Frame->State.gregs[FEXCore::X86State::REG_##x];
|
|
COPY_REG(RDI);
|
|
COPY_REG(RSI);
|
|
COPY_REG(RBP);
|
|
COPY_REG(RBX);
|
|
COPY_REG(RDX);
|
|
COPY_REG(RAX);
|
|
COPY_REG(RCX);
|
|
COPY_REG(RSP);
|
|
#undef COPY_REG
|
|
|
|
auto* fpstate = &xstate->fpstate;
|
|
|
|
// Copy float registers
|
|
const uint16_t CurrentOffset = Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC];
|
|
for (size_t i = 0; i < FEXCore::Core::CPUState::NUM_MMS; ++i) {
|
|
const auto modulo_i = (i + CurrentOffset) % 8;
|
|
// 32-bit st register size is only 10 bytes. Not padded to 16byte like x86-64
|
|
memcpy(&fpstate->_st[i], &Frame->State.mm[modulo_i], 10);
|
|
}
|
|
|
|
// Extended XMM state
|
|
fpstate->status = FEXCore::x86::fpstate_magic::MAGIC_XFPSTATE;
|
|
|
|
if (SupportsAVX) {
|
|
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, xstate->ymmh.ymmh_space);
|
|
} else {
|
|
CTX->ReconstructXMMRegisters(Thread, fpstate->_xmm, nullptr);
|
|
}
|
|
|
|
fpstate->mxcsr = Frame->State.mxcsr;
|
|
|
|
// FCW store default
|
|
fpstate->fcw = Frame->State.FCW;
|
|
// Reconstruct FSW
|
|
fpstate->fsw = (Frame->State.flags[FEXCore::X86State::X87FLAG_TOP_LOC] << 11) | (Frame->State.flags[FEXCore::X86State::X87FLAG_C0_LOC] << 8) |
|
|
(Frame->State.flags[FEXCore::X86State::X87FLAG_C1_LOC] << 9) | (Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
|
|
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14) | Frame->State.flags[FEXCore::X86State::X87FLAG_IE_LOC];
|
|
fpstate->ftw = FEXCore::FPState::ConvertFromAbridgedFTW(fpstate->fsw, Frame->State.mm, Frame->State.AbridgedFTW);
|
|
|
|
// Copy over signal stack information
|
|
guest_uctx->uc.uc_stack.ss_flags = GuestStack->ss_flags;
|
|
guest_uctx->uc.uc_stack.ss_sp = static_cast<uint32_t>(reinterpret_cast<uint64_t>(GuestStack->ss_sp));
|
|
guest_uctx->uc.uc_stack.ss_size = GuestStack->ss_size;
|
|
|
|
// Setup siginfo
|
|
// These three elements are in every siginfo
|
|
guest_uctx->info.si_signo = HostSigInfo->si_signo;
|
|
guest_uctx->info.si_errno = HostSigInfo->si_errno;
|
|
if (ContextBackup->FaultToTopAndGeneratedException) {
|
|
guest_uctx->info.si_code = Frame->SynchronousFaultData.si_code;
|
|
} else {
|
|
guest_uctx->info.si_code = HostSigInfo->si_code;
|
|
}
|
|
|
|
const SigInfoLayout Layout =
|
|
CalculateSigInfoLayout(Signal, guest_uctx->info.si_code, guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR]);
|
|
|
|
switch (Layout) {
|
|
case SigInfoLayout::LAYOUT_KILL:
|
|
guest_uctx->info._sifields._kill.pid = HostSigInfo->si_pid;
|
|
guest_uctx->info._sifields._kill.uid = HostSigInfo->si_uid;
|
|
break;
|
|
case SigInfoLayout::LAYOUT_TIMER:
|
|
guest_uctx->info._sifields._timer.tid = HostSigInfo->si_timerid;
|
|
guest_uctx->info._sifields._timer.overrun = HostSigInfo->si_overrun;
|
|
guest_uctx->info._sifields._timer.sigval.sival_int = HostSigInfo->si_int;
|
|
break;
|
|
case SigInfoLayout::LAYOUT_POLL:
|
|
guest_uctx->info._sifields._poll.band = HostSigInfo->si_band;
|
|
guest_uctx->info._sifields._poll.fd = HostSigInfo->si_fd;
|
|
break;
|
|
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<uint32_t>(reinterpret_cast<uintptr_t>(HostSigInfo->si_addr));
|
|
break;
|
|
case SigInfoLayout::LAYOUT_FAULT_RIP:
|
|
// Macro expansion to get the si_addr
|
|
// Can't really give a real result here. Pull from the context for now
|
|
guest_uctx->info._sifields._sigfault.addr = ContextBackup->OriginalRIP;
|
|
break;
|
|
case SigInfoLayout::LAYOUT_CHLD:
|
|
guest_uctx->info._sifields._sigchld.pid = HostSigInfo->si_pid;
|
|
guest_uctx->info._sifields._sigchld.uid = HostSigInfo->si_uid;
|
|
guest_uctx->info._sifields._sigchld.status = HostSigInfo->si_status;
|
|
guest_uctx->info._sifields._sigchld.utime = HostSigInfo->si_utime;
|
|
guest_uctx->info._sifields._sigchld.stime = HostSigInfo->si_stime;
|
|
break;
|
|
case SigInfoLayout::LAYOUT_RT:
|
|
guest_uctx->info._sifields._rt.pid = HostSigInfo->si_pid;
|
|
guest_uctx->info._sifields._rt.uid = HostSigInfo->si_uid;
|
|
guest_uctx->info._sifields._rt.sigval.sival_int = HostSigInfo->si_int;
|
|
break;
|
|
case SigInfoLayout::LAYOUT_SYS:
|
|
guest_uctx->info._sifields._sigsys.call_addr = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(HostSigInfo->si_call_addr));
|
|
guest_uctx->info._sifields._sigsys.syscall = HostSigInfo->si_syscall;
|
|
// We need to lie about the architecture here.
|
|
// Otherwise we would expose incorrect information to the guest.
|
|
constexpr uint32_t AUDIT_LE = 0x4000'0000U;
|
|
constexpr uint32_t MACHINE_I386 = 3; // This matches the ELF definition.
|
|
guest_uctx->info._sifields._sigsys.arch = AUDIT_LE | MACHINE_I386;
|
|
break;
|
|
}
|
|
|
|
// Setup the guest stack context.
|
|
guest_uctx->Signal = Signal;
|
|
guest_uctx->pinfo = (uint32_t)(uint64_t)&guest_uctx->info;
|
|
guest_uctx->puc = (uint32_t)(uint64_t)&guest_uctx->uc;
|
|
|
|
// Retcode needs to be bit-exact for debuggers
|
|
constexpr static uint8_t rt_retcode[] = {
|
|
0xb8, // mov
|
|
0xad, 0x00, 0x00, 0x00, // 32-bit rt_sigreturn
|
|
0xcd, 0x80, // int 0x80
|
|
0x0, // Pad
|
|
};
|
|
|
|
memcpy(guest_uctx->retcode, &rt_retcode, sizeof(rt_retcode));
|
|
|
|
// 32-bit Guest can provide its own restorer or we need to provide our own.
|
|
// On a real host this restorer will live in VDSO.
|
|
const bool HasRestorer = (GuestAction->sa_flags & X86_SA_RESTORER) == X86_SA_RESTORER;
|
|
if (HasRestorer) {
|
|
guest_uctx->pretcode = (uint32_t)(uint64_t)GuestAction->restorer;
|
|
} else {
|
|
guest_uctx->pretcode = SignalReturn;
|
|
LOGMAN_THROW_A_FMT(SignalReturn < 0x1'0000'0000ULL, "This needs to be below 4GB");
|
|
}
|
|
|
|
// Support regparm=3
|
|
Frame->State.gregs[FEXCore::X86State::REG_RAX] = Signal;
|
|
Frame->State.gregs[FEXCore::X86State::REG_RDX] = guest_uctx->pinfo;
|
|
Frame->State.gregs[FEXCore::X86State::REG_RCX] = guest_uctx->puc;
|
|
|
|
return NewGuestSP;
|
|
}
|
|
|
|
} // namespace FEX::HLE
|