mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-08 04:04:10 +02:00
I saw a red herring that I thought the high cpu usage in steamwebhelper could come from signal handlers. This turned out to not be the case, but now I've got this implemented. Installs the few signal handlers that we need upfront but for everything that isn't a mandatory signal we instead now wait until the guest also installs that signal handler. This fixes #1107
190 lines
6.8 KiB
C++
190 lines
6.8 KiB
C++
#include <FEXCore/Config/Config.h>
|
|
#include <FEXCore/Core/CodeLoader.h>
|
|
#include <FEXCore/Core/Context.h>
|
|
#include <FEXCore/Core/CoreState.h>
|
|
#include <FEXCore/Core/CPUBackend.h>
|
|
#include <FEXCore/Core/X86Enums.h>
|
|
#include <FEXCore/HLE/SyscallHandler.h>
|
|
#include <FEXCore/Debug/InternalThreadState.h>
|
|
|
|
#include <ucontext.h>
|
|
#include <cassert>
|
|
#include <iostream>
|
|
#include <sys/mman.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/ptrace.h>
|
|
#include <errno.h>
|
|
#include <sys/user.h>
|
|
#include <stdint.h>
|
|
|
|
#include <signal.h>
|
|
#include <xbyak/xbyak.h>
|
|
using namespace Xbyak;
|
|
|
|
namespace HostFactory {
|
|
#ifdef _M_X86_64
|
|
class HostCore final : public FEXCore::CPU::CPUBackend, public Xbyak::CodeGenerator {
|
|
public:
|
|
explicit HostCore(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread, bool Fallback);
|
|
~HostCore() override;
|
|
std::string GetName() override { return "Host Core"; }
|
|
void* CompileCode(uint64_t Entry, FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) override;
|
|
|
|
void *MapRegion(void *HostPtr, uint64_t VirtualGuestPtr, uint64_t Size) override {
|
|
return HostPtr;
|
|
}
|
|
|
|
void Initialize() override;
|
|
bool NeedsOpDispatch() override { return false; }
|
|
|
|
bool HandleSIGSEGV(FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext);
|
|
|
|
private:
|
|
uint64_t ReturningStackLocation;
|
|
uint64_t ThreadStopHandlerAddress;
|
|
};
|
|
|
|
HostCore::~HostCore() {
|
|
}
|
|
|
|
HostCore::HostCore(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread, bool Fallback)
|
|
: CodeGenerator(4096) {
|
|
FEXCore::Context::RegisterHostSignalHandler(CTX, SIGSEGV,
|
|
[](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
|
auto InternalThread = Thread;
|
|
HostCore *Core = reinterpret_cast<HostCore*>(InternalThread->CPUBackend.get());
|
|
return Core->HandleSIGSEGV(Thread, Signal, info, ucontext);
|
|
},
|
|
true
|
|
);
|
|
|
|
FEXCore::Context::RegisterHostSignalHandler(CTX, 63, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
|
return true;
|
|
}, true);
|
|
}
|
|
|
|
bool HostCore::HandleSIGSEGV(FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) {
|
|
ucontext_t* _context = (ucontext_t*)ucontext;
|
|
mcontext_t* _mcontext = &_context->uc_mcontext;
|
|
|
|
// Check our current instruction that we just executed to ensure it was an HLT
|
|
uint8_t *Inst = reinterpret_cast<uint8_t*>(_mcontext->gregs[REG_RIP]);
|
|
constexpr uint8_t HLT = 0xF4;
|
|
if (Inst[0] != HLT) {
|
|
return false;
|
|
}
|
|
|
|
// Store our host state in to the guest for testing against
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RAX] = _mcontext->gregs[REG_RAX];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RBX] = _mcontext->gregs[REG_RBX];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RCX] = _mcontext->gregs[REG_RCX];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDX] = _mcontext->gregs[REG_RDX];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RBP] = _mcontext->gregs[REG_RBP];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = _mcontext->gregs[REG_RSI];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = _mcontext->gregs[REG_RDI];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = _mcontext->gregs[REG_RSP];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R8] = _mcontext->gregs[REG_R8];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R9] = _mcontext->gregs[REG_R9];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R10] = _mcontext->gregs[REG_R10];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R11] = _mcontext->gregs[REG_R11];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R12] = _mcontext->gregs[REG_R12];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R13] = _mcontext->gregs[REG_R13];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R14] = _mcontext->gregs[REG_R14];
|
|
Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_R15] = _mcontext->gregs[REG_R15];
|
|
Thread->CurrentFrame->State.rip = _mcontext->gregs[REG_RIP];
|
|
|
|
for (size_t i = 0; i < 16; ++i) {
|
|
memcpy(&Thread->CurrentFrame->State.xmm[i], &_mcontext->fpregs->_xmm[i], sizeof(_mcontext->fpregs->_xmm[0]));
|
|
}
|
|
|
|
uint16_t CurrentOffset = (_mcontext->fpregs->swd >> 11) & 7;
|
|
|
|
for (size_t i = 0; i < 8; ++i) {
|
|
memcpy(&Thread->CurrentFrame->State.mm[(i + CurrentOffset) % 8], &_mcontext->fpregs->_st[i], sizeof(_mcontext->fpregs->_st[0]));
|
|
}
|
|
|
|
// Our thread is stopping
|
|
// We don't care about anything at this point
|
|
// Set the stack to our starting location when we entered the JIT and get out safely
|
|
_mcontext->gregs[REG_RSP] = ReturningStackLocation;
|
|
|
|
// Set the new PC
|
|
_mcontext->gregs[REG_RIP] = ThreadStopHandlerAddress;
|
|
|
|
return true;
|
|
}
|
|
|
|
void HostCore::Initialize() {
|
|
DispatchPtr = getCurr<CPUBackend::AsmDispatch>();
|
|
// x86-64 ABI has the stack aligned when /call/ happens
|
|
// Which means the destination has a misaligned stack at that point
|
|
push(rbx);
|
|
push(rbp);
|
|
push(r12);
|
|
push(r13);
|
|
push(r14);
|
|
push(r15);
|
|
sub(rsp, 8);
|
|
|
|
// Save this stack pointer so we can cleanly shutdown the emulation with a long jump
|
|
// regardless of where we were in the stack
|
|
mov(rax, (uint64_t)&ReturningStackLocation);
|
|
mov(qword [rax], rsp);
|
|
|
|
push(qword [rdi + offsetof(FEXCore::Core::CPUState, rip)]);
|
|
mov (rax, 0);
|
|
mov (rbx, 0);
|
|
mov (rcx, 0);
|
|
mov (rdx, 0);
|
|
mov (rbp, 0);
|
|
mov (rsi, 0);
|
|
mov (r8, 0);
|
|
mov (r9, 0);
|
|
mov (r10, 0);
|
|
mov (r11, 0);
|
|
mov (r12, 0);
|
|
mov (r13, 0);
|
|
mov (r14, 0);
|
|
mov (r15, 0);
|
|
|
|
finit();
|
|
{
|
|
// Load our RIP
|
|
// RSP won't be set to zero here but should be fine
|
|
ret();
|
|
}
|
|
|
|
ThreadStopHandlerAddress = getCurr<uint64_t>();
|
|
|
|
add(rsp, 8);
|
|
|
|
pop(r15);
|
|
pop(r14);
|
|
pop(r13);
|
|
pop(r12);
|
|
pop(rbp);
|
|
pop(rbx);
|
|
|
|
ret();
|
|
ready();
|
|
}
|
|
|
|
void* HostCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
|
|
return std::make_unique<HostCore>(CTX, Thread, false);
|
|
}
|
|
#else
|
|
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
|
|
LOGMAN_MSG_A("HostCPU factory doesn't exist for this host");
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|