mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 10:00:16 +02:00
WOW64: Use CHPEv2 for suspend and exception support
CHPE_V2_CPU_AREA_INFO is the mechanism used on ARM64EC to coordinate thread state, suspension and exception handling with the kernel. It's typically set up by ntdll for ARM64EC processes, but Wine uses the TEB pointer without restricting where it's coming from. Set ChpeV2CpuAreaInfo in FEX WOW64 thread initialization. Fixes 32-bit debug events and a number of CPU context handling tests. Reduces the number of wineserver round-trips required for suspend and avoids the need for remote thread creation for inter-process suspends.
This commit is contained in:
1 parent
e431bfe0fa
commit
af04940466
5 files changed
+86
-252
No files matched your search
@@ -358,11 +358,6 @@ bool ContextImpl::InitCore() {
|
||||
// Set up the SignalDelegator config since core is initialized.
|
||||
SignalDelegation->SetConfig(Dispatcher->MakeSignalDelegatorConfig());
|
||||
|
||||
#if defined(_WIN32) && !defined(ARCHITECTURE_arm64ec)
|
||||
// WOW64 always needs the interrupt fault check to be enabled.
|
||||
Config.NeedsPendingInterruptFaultCheck = true;
|
||||
#endif
|
||||
|
||||
if (Config.GdbServer) {
|
||||
// If gdbserver is enabled then this needs to be enabled.
|
||||
Config.NeedsPendingInterruptFaultCheck = true;
|
||||
|
||||
@@ -766,7 +766,7 @@ void Arm64JITCore::EmitTFCheck() {
|
||||
void Arm64JITCore::EmitSuspendInterruptCheck() {
|
||||
if (CTX->Config.NeedsPendingInterruptFaultCheck) {
|
||||
// Trigger a fault if there are any pending interrupts
|
||||
// Used only for suspend on WIN32 at the moment
|
||||
// Used only for gdbserver at the moment
|
||||
constexpr size_t InterruptPageOffset =
|
||||
offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState);
|
||||
if constexpr (InterruptPageOffset <= 32760) {
|
||||
@@ -778,7 +778,7 @@ void Arm64JITCore::EmitSuspendInterruptCheck() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARCHITECTURE_arm64ec
|
||||
#ifdef _WIN32
|
||||
static constexpr uint16_t SuspendMagic {0xCAFE};
|
||||
|
||||
ldr(TMP2.W(), STATE_PTR(CpuStateFrame, SuspendDoorbell));
|
||||
|
||||
@@ -427,7 +427,7 @@ struct CpuStateFrame {
|
||||
|
||||
InternalThreadState* Thread;
|
||||
|
||||
#ifdef ARCHITECTURE_arm64ec
|
||||
#ifdef _WIN32
|
||||
// Set by the kernel on ARM64EC whenever the JIT should cooperatively suspend running guest code.
|
||||
uint32_t SuspendDoorbell {};
|
||||
#endif
|
||||
|
||||
+82
-239
@@ -63,18 +63,6 @@ $end_info$
|
||||
#include <wine/debug.h>
|
||||
#include <wine/unixlib.h>
|
||||
|
||||
namespace ControlBits {
|
||||
// When this is unset, a thread can be safely interrupted and have its context recovered
|
||||
// IMPORTANT: This can only safely be written by the owning thread
|
||||
static constexpr uint32_t IN_JIT {1U << 0};
|
||||
|
||||
// JIT entry polls this bit until it is unset, at which point CONTROL_IN_JIT will be set
|
||||
static constexpr uint32_t PAUSED {1U << 1};
|
||||
|
||||
// When this is set, the CPU context stored in the CPU area has not yet been flushed to the FEX TLS
|
||||
static constexpr uint32_t WOW_CPU_AREA_DIRTY {1U << 2};
|
||||
}; // namespace ControlBits
|
||||
|
||||
struct TLS {
|
||||
enum class Slot : size_t {
|
||||
ENTRY_CONTEXT = WOW64_TLS_MAX_NUMBER - 1,
|
||||
@@ -116,8 +104,28 @@ struct TLS {
|
||||
}
|
||||
};
|
||||
|
||||
CHPE_V2_CPU_AREA_INFO*& GetCpuArea() {
|
||||
return ((__TEB*)NtCurrentTeb())->ChpeV2CpuAreaInfo;
|
||||
}
|
||||
|
||||
WOW64_CONTEXT* GetWow64Context() {
|
||||
WOW64_CONTEXT* WowContext;
|
||||
RtlWow64GetCurrentCpuArea(nullptr, reinterpret_cast<void**>(&WowContext), nullptr);
|
||||
return WowContext;
|
||||
}
|
||||
|
||||
struct FrontendThreadData {
|
||||
bool InLockedRWXRead {};
|
||||
CHPE_V2_CPU_AREA_INFO CpuArea {};
|
||||
|
||||
FrontendThreadData(FEXCore::Core::InternalThreadState* Thread) {
|
||||
CpuArea.SuspendDoorbell = reinterpret_cast<ULONG*>(&Thread->CurrentFrame->SuspendDoorbell);
|
||||
GetCpuArea() = &CpuArea;
|
||||
}
|
||||
|
||||
~FrontendThreadData() {
|
||||
GetCpuArea() = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class WowSyscallHandler;
|
||||
@@ -192,8 +200,9 @@ void HandleImageUnmap(uint64_t Address, uint64_t Size) {
|
||||
} // namespace
|
||||
|
||||
namespace Context {
|
||||
void LoadStateFromWowContext(FEXCore::Core::InternalThreadState* Thread, uint64_t WowTEB, WOW64_CONTEXT* Context) {
|
||||
void LoadStateFromWowContext(FEXCore::Core::InternalThreadState* Thread) {
|
||||
auto& State = Thread->CurrentFrame->State;
|
||||
WOW64_CONTEXT* Context = GetWow64Context();
|
||||
|
||||
// General register state
|
||||
|
||||
@@ -218,6 +227,7 @@ void LoadStateFromWowContext(FEXCore::Core::InternalThreadState* Thread, uint64_
|
||||
|
||||
// The TEB is the only populated GDT entry by default
|
||||
auto GDT = State.GetSegmentFromIndex(State, (Context->SegFs & 0xffff));
|
||||
uint64_t WowTEB = GetWowTEB(NtCurrentTeb());
|
||||
State.SetGDTBase(GDT, WowTEB);
|
||||
State.SetGDTLimit(GDT, 0xF'FFFFU);
|
||||
State.fs_cached = WowTEB;
|
||||
@@ -242,7 +252,8 @@ void LoadStateFromWowContext(FEXCore::Core::InternalThreadState* Thread, uint64_
|
||||
State.AbridgedFTW = XSave->TagWord;
|
||||
}
|
||||
|
||||
void StoreWowContextFromState(FEXCore::Core::InternalThreadState* Thread, WOW64_CONTEXT* Context) {
|
||||
void StoreWowContextFromState(WOW64_CONTEXT* Context) {
|
||||
auto Thread = GetTLS().ThreadState();
|
||||
auto& State = Thread->CurrentFrame->State;
|
||||
|
||||
// General register state
|
||||
@@ -289,21 +300,13 @@ void StoreWowContextFromState(FEXCore::Core::InternalThreadState* Thread, WOW64_
|
||||
Context->FloatSave.Cr0NpxState = XSave->StatusWord | 0xffff0000;
|
||||
}
|
||||
|
||||
NTSTATUS FlushThreadStateContext(HANDLE Thread) {
|
||||
const auto [Err, TLS] = GetThreadTLS(Thread);
|
||||
if (Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
WOW64_CONTEXT TmpWowContext {.ContextFlags = WOW64_CONTEXT_FULL | WOW64_CONTEXT_EXTENDED_REGISTERS};
|
||||
|
||||
Context::StoreWowContextFromState(TLS.ThreadState(), &TmpWowContext);
|
||||
return RtlWow64SetThreadContext(Thread, &TmpWowContext);
|
||||
void FlushThreadStateContext() {
|
||||
StoreWowContextFromState(GetWow64Context());
|
||||
}
|
||||
|
||||
void ReconstructThreadState(TLS TLS, CONTEXT* Context) {
|
||||
void ReconstructThreadState(CONTEXT* Context) {
|
||||
const auto& Config = SignalDelegator->GetConfig();
|
||||
auto* Thread = TLS.ThreadState();
|
||||
auto* Thread = GetTLS().ThreadState();
|
||||
auto& State = Thread->CurrentFrame->State;
|
||||
|
||||
State.rip = CTX->RestoreRIPFromHostPC(Thread, Context->Pc);
|
||||
@@ -323,27 +326,24 @@ void ReconstructThreadState(TLS TLS, CONTEXT* Context) {
|
||||
CTX->SetFlagsFromCompactedEFLAGS(Thread, EFlags);
|
||||
}
|
||||
|
||||
WOW64_CONTEXT ReconstructWowContext(TLS TLS, CONTEXT* Context) {
|
||||
WOW64_CONTEXT* ReconstructWowContext(CONTEXT* Context) {
|
||||
if (!IsDispatcherAddress(Context->Pc)) {
|
||||
ReconstructThreadState(TLS, Context);
|
||||
ReconstructThreadState(Context);
|
||||
}
|
||||
|
||||
WOW64_CONTEXT WowContext {
|
||||
.ContextFlags = WOW64_CONTEXT_ALL,
|
||||
};
|
||||
|
||||
auto* XSave = reinterpret_cast<XSAVE_FORMAT*>(WowContext.ExtendedRegisters);
|
||||
WOW64_CONTEXT* WowContext = GetWow64Context();
|
||||
auto* XSave = reinterpret_cast<XSAVE_FORMAT*>(WowContext->ExtendedRegisters);
|
||||
XSave->ControlWord = 0x27f;
|
||||
XSave->MxCsr = 0x1f80;
|
||||
|
||||
Context::StoreWowContextFromState(TLS.ThreadState(), &WowContext);
|
||||
StoreWowContextFromState(WowContext);
|
||||
return WowContext;
|
||||
}
|
||||
|
||||
static std::optional<FEX::Windows::TSOHandlerConfig> HandlerConfig;
|
||||
|
||||
bool HandleUnalignedAccess(TLS TLS, CONTEXT* Context) {
|
||||
auto Thread = TLS.ThreadState();
|
||||
bool HandleUnalignedAccess(CONTEXT* Context) {
|
||||
auto Thread = GetTLS().ThreadState();
|
||||
if (!Thread->CTX->IsAddressInCodeBuffer(Thread, Context->Pc)) {
|
||||
return false;
|
||||
}
|
||||
@@ -354,60 +354,36 @@ bool HandleUnalignedAccess(TLS TLS, CONTEXT* Context) {
|
||||
return Result.has_value();
|
||||
}
|
||||
|
||||
void LockJITContext(TLS TLS) {
|
||||
uint32_t Expected = TLS.ControlWord().load(), New;
|
||||
void LockJITContext() {
|
||||
GetCpuArea()->InSimulation = -1;
|
||||
LoadStateFromWowContext(GetTLS().ThreadState());
|
||||
}
|
||||
|
||||
// Spin until PAUSED is unset, setting IN_JIT when that occurs
|
||||
do {
|
||||
Expected = Expected & ~ControlBits::PAUSED;
|
||||
New = (Expected | ControlBits::IN_JIT) & ~ControlBits::WOW_CPU_AREA_DIRTY;
|
||||
} while (!TLS.ControlWord().compare_exchange_weak(Expected, New, std::memory_order::relaxed));
|
||||
std::atomic_signal_fence(std::memory_order::seq_cst);
|
||||
|
||||
// If the CPU area is dirty, flush it to the JIT context before reentry
|
||||
if (Expected & ControlBits::WOW_CPU_AREA_DIRTY) {
|
||||
WOW64_CONTEXT* WowContext;
|
||||
RtlWow64GetCurrentCpuArea(nullptr, reinterpret_cast<void**>(&WowContext), nullptr);
|
||||
Context::LoadStateFromWowContext(TLS.ThreadState(), GetWowTEB(NtCurrentTeb()), WowContext);
|
||||
void UnlockJITContext() {
|
||||
CHPE_V2_CPU_AREA_INFO* CpuArea = GetCpuArea();
|
||||
CpuArea->InSimulation = 0;
|
||||
if (*CpuArea->SuspendDoorbell) {
|
||||
CONTEXT ResumeContext;
|
||||
RtlCaptureContext(&ResumeContext);
|
||||
if (*CpuArea->SuspendDoorbell) {
|
||||
NtContinue(&ResumeContext, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnlockJITContext(TLS TLS) {
|
||||
std::atomic_signal_fence(std::memory_order::seq_cst);
|
||||
TLS.ControlWord().fetch_and(~ControlBits::IN_JIT, std::memory_order::relaxed);
|
||||
}
|
||||
|
||||
class ScopedJITContextLock {
|
||||
private:
|
||||
TLS TLSData;
|
||||
|
||||
public:
|
||||
ScopedJITContextLock(TLS TLSData)
|
||||
: TLSData {TLSData} {
|
||||
LockJITContext(TLSData);
|
||||
}
|
||||
|
||||
~ScopedJITContextLock() {
|
||||
UnlockJITContext(TLSData);
|
||||
}
|
||||
};
|
||||
|
||||
bool HandleSuspendInterrupt(TLS TLS, CONTEXT* Context, uint64_t FaultAddress) {
|
||||
if (FaultAddress != reinterpret_cast<uint64_t>(&TLS.ThreadState()->InterruptFaultPage)) {
|
||||
bool HandleSuspendInterrupt(CONTEXT* Context) {
|
||||
static constexpr uint32_t SuspendTrapMagic {0xD4395FC0}; // brk #0xCAFE
|
||||
if (*reinterpret_cast<uint32_t*>(Context->Pc) != SuspendTrapMagic) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void* TmpAddress = reinterpret_cast<void*>(FaultAddress);
|
||||
SIZE_T TmpSize = FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
ULONG TmpProt;
|
||||
NtProtectVirtualMemory(NtCurrentProcess(), &TmpAddress, &TmpSize, PAGE_READWRITE, &TmpProt);
|
||||
|
||||
// Since interrupts only happen at the start of blocks, the reconstructed state should be entirely accurate
|
||||
ReconstructThreadState(TLS, Context);
|
||||
ReconstructThreadState(Context);
|
||||
FlushThreadStateContext();
|
||||
|
||||
// Yield to the suspender
|
||||
UnlockJITContext(TLS);
|
||||
LockJITContext(TLS);
|
||||
UnlockJITContext();
|
||||
LockJITContext();
|
||||
|
||||
// Adjust context to return to the dispatcher, reloading SRA from thread state
|
||||
const auto& Config = SignalDelegator->GetConfig();
|
||||
@@ -464,10 +440,10 @@ public:
|
||||
Frame->State.gregs[FEXCore::X86State::REG_RSP] = ReturnRSP + sizeof(StackLayout);
|
||||
Frame->State.rip = ReturnRIP;
|
||||
|
||||
const auto TLS = GetTLS();
|
||||
Context::UnlockJITContext(TLS);
|
||||
Context::FlushThreadStateContext();
|
||||
Context::UnlockJITContext();
|
||||
ReturnRAX = static_cast<uint64_t>(WineUnixCall(StackArgs->Handle, StackArgs->ID, ULongToPtr(StackArgs->Args)));
|
||||
Context::LockJITContext(TLS);
|
||||
Context::LockJITContext();
|
||||
Frame->State.gregs[FEXCore::X86State::REG_RAX] = ReturnRAX;
|
||||
} else if (Frame->State.rip == (uint64_t)BridgeInstrs::Syscall) {
|
||||
const uint64_t EntryRAX = Frame->State.gregs[FEXCore::X86State::REG_RAX];
|
||||
@@ -475,11 +451,11 @@ public:
|
||||
Frame->State.gregs[FEXCore::X86State::REG_RSP] = ReturnRSP;
|
||||
Frame->State.rip = ReturnRIP;
|
||||
|
||||
const auto TLS = GetTLS();
|
||||
Context::UnlockJITContext(TLS);
|
||||
Context::FlushThreadStateContext();
|
||||
Context::UnlockJITContext();
|
||||
Wow64ProcessPendingCrossProcessItems();
|
||||
ReturnRAX = static_cast<uint64_t>(Wow64SystemServiceEx(static_cast<UINT>(EntryRAX), reinterpret_cast<UINT*>(ReturnRSP + 4)));
|
||||
Context::LockJITContext(TLS);
|
||||
Context::LockJITContext();
|
||||
Frame->State.gregs[FEXCore::X86State::REG_RAX] = ReturnRAX;
|
||||
}
|
||||
}
|
||||
@@ -633,9 +609,8 @@ void BTCpuThreadInit() {
|
||||
|
||||
const auto TLS = GetTLS();
|
||||
TLS.ThreadState() = Thread;
|
||||
TLS.ControlWord().fetch_or(ControlBits::WOW_CPU_AREA_DIRTY, std::memory_order::relaxed);
|
||||
|
||||
Thread->FrontendPtr = new FrontendThreadData();
|
||||
Thread->FrontendPtr = new FrontendThreadData(Thread);
|
||||
|
||||
auto ThreadTID = GetCurrentThreadId();
|
||||
Threads.emplace(ThreadTID, Thread);
|
||||
@@ -704,60 +679,11 @@ void* __wine_get_unix_opcode() {
|
||||
}
|
||||
|
||||
NTSTATUS BTCpuGetContext(HANDLE Thread, HANDLE Process, void* Unknown, WOW64_CONTEXT* Context) {
|
||||
if (!FEX::Windows::ValidateHandleAccess(Thread, THREAD_GET_CONTEXT)) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||
auto [Err, TLS] = GetThreadTLS(*ThreadDup);
|
||||
if (Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
Context::ScopedJITContextLock Lk {TLS};
|
||||
if (Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
return RtlWow64GetThreadContext(*ThreadDup, Context);
|
||||
return RtlWow64GetThreadContext(Thread, Context);
|
||||
}
|
||||
|
||||
NTSTATUS BTCpuSetContext(HANDLE Thread, HANDLE Process, void* Unknown, WOW64_CONTEXT* Context) {
|
||||
if (!FEX::Windows::ValidateHandleAccess(Thread, THREAD_SET_CONTEXT)) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||
auto [Err, TLS] = GetThreadTLS(*ThreadDup);
|
||||
if (Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
// Back-up the input context incase we've been passed the CPU area (the flush below would wipe it out otherwise)
|
||||
WOW64_CONTEXT TmpContext = *Context;
|
||||
|
||||
Context::ScopedJITContextLock Lk {TLS};
|
||||
if (Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
// Merge the input context into the CPU area then pass the full context into the JIT
|
||||
if (Err = RtlWow64SetThreadContext(*ThreadDup, &TmpContext); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
TmpContext.ContextFlags = WOW64_CONTEXT_FULL | WOW64_CONTEXT_EXTENDED_REGISTERS;
|
||||
|
||||
if (Err = RtlWow64GetThreadContext(*ThreadDup, &TmpContext); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
if (Thread == GetCurrentThread() && TLS.CachedCallRetSp()) {
|
||||
TLS.ThreadState()->CurrentFrame->State.callret_sp = TLS.CachedCallRetSp();
|
||||
}
|
||||
|
||||
Context::LoadStateFromWowContext(TLS.ThreadState(), GetWowTEB(TLS.TEB), &TmpContext);
|
||||
return STATUS_SUCCESS;
|
||||
return RtlWow64SetThreadContext(Thread, Context);
|
||||
}
|
||||
|
||||
// .seh_pushframe doesn't restore the frame pointer, so if when unwinding from RtlCaptureContext an operation is used
|
||||
@@ -785,102 +711,20 @@ extern "C" void BTCpuSimulateImpl(CONTEXT* entry_context) {
|
||||
TLS.EntryContext() = entry_context;
|
||||
TLS.CachedCallRetSp() = TLS.ThreadState()->CurrentFrame->State.callret_sp;
|
||||
|
||||
Context::ScopedJITContextLock Lk {TLS};
|
||||
Context::LockJITContext();
|
||||
CTX->ExecuteThread(TLS.ThreadState());
|
||||
Context::UnlockJITContext();
|
||||
}
|
||||
|
||||
NTSTATUS BTCpuSuspendLocalThread(HANDLE Thread, ULONG* Count) {
|
||||
if (!FEX::Windows::ValidateHandleAccess(Thread, THREAD_SUSPEND_RESUME)) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
auto ThreadDup = FEX::Windows::DupHandle(Thread, THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT);
|
||||
THREAD_BASIC_INFORMATION Info;
|
||||
if (NTSTATUS Err = NtQueryInformationThread(*ThreadDup, ThreadBasicInformation, &Info, sizeof(Info), nullptr); Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
const auto ThreadTID = reinterpret_cast<uint64_t>(Info.ClientId.UniqueThread);
|
||||
if (ThreadTID == GetCurrentThreadId()) {
|
||||
LogMan::Msg::DFmt("Suspending self");
|
||||
// Mark the CPU area as dirty, to force the JIT context to be restored from it on entry as it may be changed using
|
||||
// SetThreadContext (which doesn't use the BTCpu API)
|
||||
if (!(GetTLS().ControlWord().fetch_or(ControlBits::WOW_CPU_AREA_DIRTY, std::memory_order::relaxed) & ControlBits::WOW_CPU_AREA_DIRTY)) {
|
||||
if (NTSTATUS Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
}
|
||||
|
||||
return NtSuspendThread(*ThreadDup, Count);
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Suspending thread: {:X}", ThreadTID);
|
||||
|
||||
auto [Err, TLS] = GetThreadTLS(*ThreadDup);
|
||||
if (Err) {
|
||||
return Err;
|
||||
}
|
||||
|
||||
std::scoped_lock Lock(ThreadCreationMutex);
|
||||
|
||||
// If the thread hasn't yet been initialized, suspend it without special handling as it wont yet have entered the JIT
|
||||
if (!Threads.contains(ThreadTID)) {
|
||||
LogMan::Msg::DFmt("Thread suspended: {:X}", ThreadTID);
|
||||
return NtSuspendThread(*ThreadDup, Count);
|
||||
}
|
||||
|
||||
// If CONTROL_IN_JIT is unset at this point, then it can never be set (and thus the JIT cannot be reentered) as
|
||||
// CONTROL_PAUSED has been set, as such, while this may redundantly request interrupts in rare cases it will never
|
||||
// miss them
|
||||
if (TLS.ControlWord().fetch_or(ControlBits::PAUSED, std::memory_order::relaxed) & ControlBits::IN_JIT) {
|
||||
LogMan::Msg::DFmt("Thread {:X} is in JIT, polling for interrupt", ThreadTID);
|
||||
|
||||
ULONG TmpProt;
|
||||
void* TmpAddress = &TLS.ThreadState()->InterruptFaultPage;
|
||||
SIZE_T TmpSize = FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
NtProtectVirtualMemory(NtCurrentProcess(), &TmpAddress, &TmpSize, PAGE_READONLY, &TmpProt);
|
||||
}
|
||||
|
||||
// Spin until the JIT is interrupted
|
||||
FEXCore::Utils::SpinWaitLock::WaitBitMaskPred(TLS.ControlWordAddress(), ControlBits::IN_JIT, 0U, std::equal_to<>());
|
||||
|
||||
// The JIT has now been interrupted and the context stored in the thread's CPU area is up-to-date
|
||||
if (Err = NtSuspendThread(*ThreadDup, Count); Err) {
|
||||
TLS.ControlWord().fetch_and(~ControlBits::PAUSED, std::memory_order::relaxed);
|
||||
return Err;
|
||||
}
|
||||
|
||||
CONTEXT TmpContext {
|
||||
.ContextFlags = CONTEXT_INTEGER,
|
||||
};
|
||||
|
||||
// NtSuspendThread may return before the thread is actually suspended, so a sync operation like NtGetContextThread
|
||||
// needs to be called to ensure it is before we unset CONTROL_PAUSED
|
||||
std::ignore = NtGetContextThread(*ThreadDup, &TmpContext);
|
||||
|
||||
// Mark the CPU area as dirty, to force the JIT context to be restored from it on entry as it may be changed using
|
||||
// SetThreadContext (which doesn't use the BTCpu API)
|
||||
if (!(TLS.ControlWord().fetch_or(ControlBits::WOW_CPU_AREA_DIRTY, std::memory_order::relaxed) & ControlBits::WOW_CPU_AREA_DIRTY)) {
|
||||
if (Err = Context::FlushThreadStateContext(*ThreadDup); Err) {
|
||||
return Err;
|
||||
}
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Thread suspended: {:X}", ThreadTID);
|
||||
|
||||
// Now the thread is suspended on the host, unset CONTROL_PAUSED so that NtResumeThread will
|
||||
// continue execution in the JIT
|
||||
TLS.ControlWord().fetch_and(~ControlBits::PAUSED, std::memory_order::relaxed);
|
||||
|
||||
return Err;
|
||||
return NtSuspendThread(Thread, Count);
|
||||
}
|
||||
|
||||
// Returns true if exception dispatch should be halted and the execution context restored to Ptrs->Context
|
||||
bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) {
|
||||
auto* Context = Ptrs->ContextRecord;
|
||||
auto* Exception = Ptrs->ExceptionRecord;
|
||||
auto TLS = GetTLS();
|
||||
auto Thread = TLS.ThreadState();
|
||||
auto Thread = GetTLS().ThreadState();
|
||||
FEXCORE_PROFILE_ACCUMULATION(Thread, AccumulatedSignalTime);
|
||||
|
||||
if (Exception->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
|
||||
@@ -894,11 +738,6 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Context::HandleSuspendInterrupt(TLS, Context, FaultAddress)) {
|
||||
LogMan::Msg::DFmt("Resumed from suspend");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FEX::Windows::JITGuardPage::HandleJITGuardPage(Thread, reinterpret_cast<void*>(FaultAddress), Context->X,
|
||||
reinterpret_cast<__uint128_t*>(Context->V), &Context->Pc)) {
|
||||
return true;
|
||||
@@ -910,7 +749,7 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) {
|
||||
if (InvalidationTracker->HandleRWXAccessViolation(Thread, Context->Pc, FaultAddress)) {
|
||||
if (CTX->IsAddressInCodeBuffer(Thread, Context->Pc) && !CTX->IsCurrentBlockSingleInst(Thread) &&
|
||||
CTX->IsAddressInCurrentBlock(Thread, FaultAddress & FEXCore::Utils::FEX_PAGE_MASK, FEXCore::Utils::FEX_PAGE_SIZE)) {
|
||||
Context::ReconstructThreadState(TLS, Context);
|
||||
Context::ReconstructThreadState(Context);
|
||||
LogMan::Msg::DFmt("Handled inline self-modifying code: pc: {:X} rip: {:X} fault: {:X}", Context->Pc,
|
||||
Thread->CurrentFrame->State.rip, FaultAddress);
|
||||
|
||||
@@ -930,29 +769,33 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Exception->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION && Context::HandleSuspendInterrupt(Context)) {
|
||||
LogMan::Msg::DFmt("Resumed from suspend");
|
||||
return true;
|
||||
}
|
||||
|
||||
FEXCORE_PROFILE_INSTANT_INCREMENT(Thread, AccumulatedSIGBUSCount, 1);
|
||||
if (Exception->ExceptionCode == EXCEPTION_DATATYPE_MISALIGNMENT && Context::HandleUnalignedAccess(TLS, Context)) {
|
||||
if (Exception->ExceptionCode == EXCEPTION_DATATYPE_MISALIGNMENT && Context::HandleUnalignedAccess(Context)) {
|
||||
LogMan::Msg::DFmt("Handled unaligned atomic: new pc: {:X}", Context->Pc);
|
||||
return true;
|
||||
}
|
||||
|
||||
LogMan::Msg::DFmt("Reconstructing context");
|
||||
|
||||
WOW64_CONTEXT WowContext = Context::ReconstructWowContext(TLS, Context);
|
||||
LogMan::Msg::DFmt("pc: {:X} eip: {:X}", Context->Pc, WowContext.Eip);
|
||||
WOW64_CONTEXT* WowContext = Context::ReconstructWowContext(Context);
|
||||
LogMan::Msg::DFmt("pc: {:X} eip: {:X} code: {:X}", Context->Pc, WowContext->Eip, Exception->ExceptionCode);
|
||||
|
||||
auto& Fault = Thread->CurrentFrame->SynchronousFaultData;
|
||||
BOOL FirstChance = TRUE;
|
||||
EXCEPTION_RECORD GuestException =
|
||||
FEX::Windows::HandleGuestException(Fault, *Exception, WowContext.Eip, WowContext.Eax, WowContext.Ecx, FirstChance);
|
||||
FEX::Windows::HandleGuestException(Fault, *Exception, WowContext->Eip, WowContext->Eax, WowContext->Ecx, FirstChance);
|
||||
if (GuestException.ExceptionCode == EXCEPTION_SINGLE_STEP) {
|
||||
WowContext.EFlags &= ~(1 << FEXCore::X86State::RFLAG_TF_RAW_LOC);
|
||||
WowContext->EFlags &= ~(1 << FEXCore::X86State::RFLAG_TF_RAW_LOC);
|
||||
}
|
||||
// wow64.dll will handle adjusting PC in the dispatched context after a breakpoint
|
||||
|
||||
BTCpuSetContext(GetCurrentThread(), GetCurrentProcess(), nullptr, &WowContext);
|
||||
Context::UnlockJITContext(TLS);
|
||||
NtRaiseException(&GuestException, TLS.EntryContext(), FirstChance);
|
||||
Context::UnlockJITContext();
|
||||
NtRaiseException(&GuestException, GetTLS().EntryContext(), FirstChance);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ extern "C" {
|
||||
|
||||
#define STATUS_EMULATION_SYSCALL ((NTSTATUS)0x40000039)
|
||||
|
||||
#ifdef ARCHITECTURE_arm64ec
|
||||
typedef struct _CHPE_V2_CPU_AREA_INFO {
|
||||
BOOLEAN InSimulation; /* 000 */
|
||||
BOOLEAN InSyscallCallback; /* 001 */
|
||||
@@ -44,7 +43,6 @@ typedef struct _CHPE_V2_CPU_AREA_INFO {
|
||||
void* EmulatorData[4]; /* 030 */
|
||||
ULONG64 EmulatorDataInline; /* 050 */
|
||||
} CHPE_V2_CPU_AREA_INFO, *PCHPE_V2_CPU_AREA_INFO;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ULONG version;
|
||||
@@ -340,10 +338,8 @@ typedef struct __TEB { /* win32/win64 */
|
||||
PVOID* TlsExpansionSlots; /* f94/1780 */
|
||||
#ifdef _WIN64
|
||||
union {
|
||||
PVOID DeallocationBStore; /* /1788 */
|
||||
#ifdef ARCHITECTURE_arm64ec
|
||||
PVOID DeallocationBStore; /* /1788 */
|
||||
CHPE_V2_CPU_AREA_INFO* ChpeV2CpuAreaInfo; /* /1788 */
|
||||
#endif
|
||||
} DUMMYUNIONNAME;
|
||||
PVOID BStoreLimit; /* /1790 */
|
||||
#endif
|
||||
|
||||
Reference in new issue
Block a user