FEXCore: Removes syscall optimization

The JIT was doing a bunch of additional work where it was saving and
restoring registers and then juggling the arguments back in to a stack
frame. All of this is nonsensical without the optimization where we
could call syscalls inline without a stack frame.

Instead remove this optimization entirely and behave like a "generic"
syscall path always. The Linux syscall handler now pulls the arguments
out of the CPU context directly and stores the result back in to RAX
directly as well.

This has knock-on effects where technically syscalls are
going to be slightly faster because no stack frame setup for the
arguments, but additionally we are going to be able to have syscalls be
proper serialization points where we can interrupt the syscall and
long-jump out without problems.

Bumps the DiskCache version again because it causes codegen to change.
This commit is contained in:
Ryan Houdek committed 2026-08-31 19:23:18 -07:00
1 parent 8cf2bf7ad8
commit 66cad978c3
19 files changed
+158 -201

No files matched your search

+2 -6
View File
@@ -479,13 +479,9 @@ private:
class SimpleSyscallHandler : public FEXCore::HLE::SyscallHandler, public FEXCore::Allocator::FEXAllocOperators {
public:
SimpleSyscallHandler() {
// Just claim to be linux 64-bit for simplicity.
OSABI = FEXCore::HLE::SyscallOSABI::OS_LINUX64;
}
uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) override {
SimpleSyscallHandler() = default;
void HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) override {
// Don't do anything
return 0;
}
// These are no-ops implementations of the SyscallHandler API
+1 -2
View File
@@ -11,9 +11,8 @@ namespace FEX::DummyHandlers {
class DummySyscallHandler : public FEXCore::HLE::SyscallHandler, public FEXCore::Allocator::FEXAllocOperators {
public:
uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) override {
void HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) override {
// Don't do anything
return 0;
}
// These are no-ops implementations of the SyscallHandler API
+5 -10
View File
@@ -65,14 +65,11 @@ class AOTSyscallHandler : public FEXCore::HLE::SyscallHandler {
class AOTSyscallHandler : public FEXCore::HLE::SyscallHandler, public FEX::HLE::SyscallMmapInterface {
#endif
public:
AOTSyscallHandler(FEXCore::Context::Context& CTX, FEXCore::HLE::SyscallOSABI SyscallOSABI)
: CTX(CTX) {
OSABI = SyscallOSABI;
}
AOTSyscallHandler(FEXCore::Context::Context& CTX)
: CTX(CTX) {}
uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) override {
void HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) override {
// Don't do anything
return 0;
}
FEXCore::Context::Context& CTX;
@@ -427,8 +424,7 @@ static std::optional<std::string> GenerateSingleCache(FEXCore::ExecutableFileInf
#ifdef _WIN32
OvercommitTracker = std::make_unique<FEX::Windows::OvercommitTracker>(IsWine);
auto SyscallOSABI = FEXCore::HLE::SyscallOSABI::OS_GENERIC;
auto SyscallHandler = std::make_unique<AOTSyscallHandler>(*CTX, SyscallOSABI);
auto SyscallHandler = std::make_unique<AOTSyscallHandler>(*CTX);
SyscallHandler->VAFileStart =
TryMapImage(SyscallHandler->InvalidationTracker, SyscallHandler->ImageTracker, Binary.FileId, Binary).value_or(0);
@@ -441,8 +437,7 @@ static std::optional<std::string> GenerateSingleCache(FEXCore::ExecutableFileInf
#else
Loader.CalculateHWCaps(CTX.get());
auto SyscallOSABI = Is64Bit ? FEXCore::HLE::SyscallOSABI::OS_LINUX64 : FEXCore::HLE::SyscallOSABI::OS_LINUX32;
auto SyscallHandler = std::make_unique<AOTSyscallHandler>(*CTX, SyscallOSABI);
auto SyscallHandler = std::make_unique<AOTSyscallHandler>(*CTX);
// Populate relocations from ELF file
{
@@ -316,15 +316,13 @@ void SeccompEmulator::DeserializeFilters(FEXCore::Core::CpuStateFrame* Frame, in
}
SeccompEmulator::ExecuteFilterResult
SeccompEmulator::ExecuteFilter(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC, FEXCore::HLE::SyscallArguments* Args) {
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
if (Thread->Filters.empty()) {
SeccompEmulator::ExecuteFilter(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC, FEX::HLE::SyscallArguments* Args) {
if (!HasFilter(Frame)) {
// Seccomp not installed. Allow it.
return {false, 0};
}
// Reconstruct the RIP from the JITPC.
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
const uint64_t RIP = Thread->Thread->CTX->RestoreRIPFromHostPC(Frame->Thread, JITPC);
const auto Arch = Is64BitMode() ? AUDIT_ARCH_X86_64 : AUDIT_ARCH_I386;
@@ -9,6 +9,7 @@ $end_info$
#include <FEXCore/Config/Config.h>
#include <FEXCore/fextl/list.h>
#include <FEXCore/Utils/SignalScopeGuards.h>
#include "LinuxSyscalls/ThreadManager.h"
#include <atomic>
#include <csignal>
@@ -20,23 +21,16 @@ struct sock_fprog;
struct seccomp_data;
struct seccomp_notif_sizes;
namespace FEXCore {
namespace Core {
struct CpuStateFrame;
}
namespace HLE {
struct SyscallArguments;
}
} // namespace FEXCore
namespace FEXCore::Core {
struct CpuStateFrame;
} // namespace FEXCore::Core
namespace FEX::HLE {
class SignalDelegator;
class SyscallHandler;
struct ThreadStateObject;
struct SyscallArguments;
using SeccompFilterFunc = uint64_t (*)(uint32_t Acc, uint32_t Index, uint32_t Tmp, uint32_t Tmp2, void* Data);
struct SeccompFilterInfo final {
@@ -65,7 +59,13 @@ public:
bool EarlyReturn {};
uint64_t Result;
};
ExecuteFilterResult ExecuteFilter(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC, FEXCore::HLE::SyscallArguments* Args);
ExecuteFilterResult ExecuteFilter(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC, FEX::HLE::SyscallArguments* Args);
bool HasFilter(FEXCore::Core::CpuStateFrame* Frame) const {
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
return !Thread->Filters.empty();
}
int GetKillSignal() const {
return CurrentKillSignal;
}
@@ -808,63 +808,109 @@ uint32_t SyscallHandler::CalculateGuestKernelVersion() {
return std::max(LinuxVersion::KernelVersion(5, 15), std::min(LinuxVersion::KernelVersion(6, 11), GetHostKernelVersion()));
}
uint64_t SyscallHandler::HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) {
// Grab the return address which will be inside the JIT.
const uint64_t JITPC = reinterpret_cast<uint64_t>(__builtin_extract_return_addr(__builtin_return_address(0)));
template<bool Is64Bit>
void SyscallHandler::HandleSyscallImpl(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC) {
auto SetResult = [](FEXCore::Core::CpuStateFrame* Frame, uint64_t Result) {
const auto Mask = Is64Bit ? ~0ULL : ~0U;
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
const auto SeccompResult = SeccompEmulator.ExecuteFilter(Frame, JITPC, Args);
Thread->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RAX] = Result & Mask;
};
if (SeccompResult.EarlyReturn) {
return SeccompResult.Result;
if (SeccompEmulator.HasFilter(Frame)) {
FEX::HLE::SyscallArguments Args {
.Argument =
{
GetArg(Is64Bit, Frame, 0),
GetArg(Is64Bit, Frame, 1),
GetArg(Is64Bit, Frame, 2),
GetArg(Is64Bit, Frame, 3),
GetArg(Is64Bit, Frame, 4),
GetArg(Is64Bit, Frame, 5),
GetArg(Is64Bit, Frame, 6),
},
};
const auto SeccompResult = SeccompEmulator.ExecuteFilter(Frame, JITPC, &Args);
if (SeccompResult.EarlyReturn) {
SetResult(Frame, SeccompResult.Result);
return;
}
}
if (Args->Argument[0] >= Definitions.size()) {
return -ENOSYS;
const auto SyscallNum = GetArg(Is64Bit, Frame, 0);
if (SyscallNum >= Definitions.size()) {
SetResult(Frame, -ENOSYS);
return;
}
auto& Def = Definitions[Args->Argument[0]];
auto& Def = Definitions[SyscallNum];
uint64_t Result {};
switch (Def.NumArgs) {
case 0: Result = std::invoke(Def.Ptr0, Frame); break;
case 1: Result = std::invoke(Def.Ptr1, Frame, Args->Argument[1]); break;
case 2: Result = std::invoke(Def.Ptr2, Frame, Args->Argument[1], Args->Argument[2]); break;
case 3: Result = std::invoke(Def.Ptr3, Frame, Args->Argument[1], Args->Argument[2], Args->Argument[3]); break;
case 4: Result = std::invoke(Def.Ptr4, Frame, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4]); break;
case 1: Result = std::invoke(Def.Ptr1, Frame, GetArg(Is64Bit, Frame, 1)); break;
case 2: Result = std::invoke(Def.Ptr2, Frame, GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2)); break;
case 3: Result = std::invoke(Def.Ptr3, Frame, GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3)); break;
case 4:
Result =
std::invoke(Def.Ptr4, Frame, GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3), GetArg(Is64Bit, Frame, 4));
break;
case 5:
Result = std::invoke(Def.Ptr5, Frame, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5]);
Result = std::invoke(Def.Ptr5, Frame, GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3),
GetArg(Is64Bit, Frame, 4), GetArg(Is64Bit, Frame, 5));
break;
case 6:
Result = std::invoke(Def.Ptr6, Frame, Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5],
Args->Argument[6]);
Result = std::invoke(Def.Ptr6, Frame, GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3),
GetArg(Is64Bit, Frame, 4), GetArg(Is64Bit, Frame, 5), GetArg(Is64Bit, Frame, 6));
break;
// for missing syscalls
case 255: return std::invoke(Def.Ptr1, Frame, Args->Argument[0]);
case 255: Result = std::invoke(Def.Ptr1, Frame, GetArg(Is64Bit, Frame, 0)); break;
default:
LOGMAN_MSG_A_FMT("Unhandled syscall: {}", Args->Argument[0]);
return -1;
LOGMAN_MSG_A_FMT("Unhandled syscall: {}", GetArg(Is64Bit, Frame, 0));
Result = -ENOSYS;
break;
}
#ifdef DEBUG_STRACE
Strace(Args, Result);
Strace(Frame, Result);
#endif
return Result;
SetResult(Frame, Result);
}
void SyscallHandler::HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) {
// Grab the return address which will be inside the JIT.
const uint64_t JITPC = reinterpret_cast<uint64_t>(__builtin_extract_return_addr(__builtin_return_address(0)));
const auto Is64Bit = Is64BitMode();
// TODO: At some point these will be runtime selectable based on `syscall` versus `int 0x80` entrypoint.
if (Is64Bit) {
HandleSyscallImpl<true>(Frame, JITPC);
} else {
HandleSyscallImpl<false>(Frame, JITPC);
}
}
#ifdef DEBUG_STRACE
void SyscallHandler::Strace(FEXCore::HLE::SyscallArguments* Args, uint64_t Ret) {
auto& Def = Definitions[Args->Argument[0]];
void SyscallHandler::Strace(FEXCore::Core::CpuStateFrame* Frame, uint64_t Ret) {
const auto Is64Bit = Is64BitMode();
auto& Def = Definitions[GetArg(Is64Bit, Frame, 0)];
switch (Def.NumArgs) {
case 0: LogMan::Msg::DFmt(Def.StraceFmt.c_str(), Ret); break;
case 1: LogMan::Msg::DFmt(Def.StraceFmt.c_str(), Args->Argument[1], Ret); break;
case 2: LogMan::Msg::DFmt(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Ret); break;
case 3: LogMan::Msg::DFmt(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Ret); break;
case 4: LogMan::Msg::DFmt(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Ret); break;
case 1: LogMan::Msg::DFmt(Def.StraceFmt.c_str(), GetArg(Is64Bit, Frame, 1), Ret); break;
case 2: LogMan::Msg::DFmt(Def.StraceFmt.c_str(), GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), Ret); break;
case 3:
LogMan::Msg::DFmt(Def.StraceFmt.c_str(), GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3), Ret);
break;
case 4:
LogMan::Msg::DFmt(Def.StraceFmt.c_str(), GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3),
GetArg(Is64Bit, Frame, 4), Ret);
break;
case 5:
LogMan::Msg::DFmt(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5], Ret);
LogMan::Msg::DFmt(Def.StraceFmt.c_str(), GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3),
GetArg(Is64Bit, Frame, 4), GetArg(Is64Bit, Frame, 5), Ret);
break;
case 6:
LogMan::Msg::DFmt(Def.StraceFmt.c_str(), Args->Argument[1], Args->Argument[2], Args->Argument[3], Args->Argument[4], Args->Argument[5],
Args->Argument[6], Ret);
LogMan::Msg::DFmt(Def.StraceFmt.c_str(), GetArg(Is64Bit, Frame, 1), GetArg(Is64Bit, Frame, 2), GetArg(Is64Bit, Frame, 3),
GetArg(Is64Bit, Frame, 4), GetArg(Is64Bit, Frame, 5), GetArg(Is64Bit, Frame, 6), Ret);
break;
default: break;
}
@@ -69,6 +69,10 @@ namespace Core {
} // namespace FEXCore
namespace FEX::HLE {
struct SyscallArguments {
static constexpr std::size_t MAX_ARGS = 7;
uint64_t Argument[MAX_ARGS];
};
class SyscallHandler;
class SignalDelegator;
@@ -124,8 +128,7 @@ public:
virtual ~SyscallHandler();
// In the case that the syscall doesn't hit the optimized path then we still need to go here
uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) final override;
void HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) final override;
void DefaultProgramBreak(uint64_t Base, uint64_t Size);
void DeserializeSeccompFD(FEX::HLE::ThreadStateObject* Thread, int FD) {
@@ -362,12 +365,36 @@ private:
bool NeedToCheckXID {true};
#ifdef DEBUG_STRACE
void Strace(FEXCore::HLE::SyscallArguments* Args, uint64_t Ret);
void Strace(FEXCore::Core::CpuStateFrame* Frame, uint64_t Ret);
#endif
fextl::unique_ptr<FEXCore::HLE::SourcecodeMap> GenerateMap(std::string_view GuestBinaryFile, std::string_view GuestBinaryFileId) override;
fextl::unique_ptr<FEX::HLE::MemAllocator> Alloc32Handler {};
std::atomic<uint64_t> AnonSharedId {1};
static inline uint64_t GetArg(bool Is64Bit, FEXCore::Core::CpuStateFrame* Frame, size_t Arg) {
constexpr size_t SyscallArgs = 7;
using SyscallArray = std::array<uint64_t, SyscallArgs>;
static constexpr SyscallArray GPRIndexes_64 = {
FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDX,
FEXCore::X86State::REG_R10, FEXCore::X86State::REG_R8, FEXCore::X86State::REG_R9,
};
static constexpr SyscallArray GPRIndexes_32 = {
FEXCore::X86State::REG_RAX, FEXCore::X86State::REG_RBX, FEXCore::X86State::REG_RCX, FEXCore::X86State::REG_RDX,
FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP,
};
const auto Index = Is64Bit ? GPRIndexes_64[Arg] : GPRIndexes_32[Arg];
const auto Mask = Is64Bit ? ~0ULL : ~0U;
const auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
return Thread->Thread->CurrentFrame->State.gregs[Index] & Mask;
};
template<bool Is64Bit>
void HandleSyscallImpl(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC);
};
#define SYSCALL_ERRNO() \
@@ -29,7 +29,6 @@ x32SyscallHandler::x32SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::S
FEX::HLE::ThunkHandler* ThunkHandler, fextl::unique_ptr<MemAllocator> Allocator)
: SyscallHandler {ctx, _SignalDelegation, ThunkHandler}
, AllocHandler {std::move(Allocator)} {
OSABI = FEXCore::HLE::SyscallOSABI::OS_LINUX32;
RegisterSyscallHandlers();
}
@@ -15,8 +15,6 @@ namespace FEX::HLE::x64 {
x64SyscallHandler::x64SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, FEX::HLE::ThunkHandler* ThunkHandler)
: SyscallHandler {ctx, _SignalDelegation, ThunkHandler} {
OSABI = FEXCore::HLE::SyscallOSABI::OS_LINUX64;
RegisterSyscallHandlers();
}
-4
View File
@@ -108,15 +108,11 @@ ExitFunctionSuspendPoint:
Name:; \
adrp x16, WineSyscallDispatcher; \
ldr x16, [x16, HASH:lo12:WineSyscallDispatcher]; \
cbz x16, 1f; \
mov x9, x30; \
adrp x8, WineIdName; \
ldr x8, [x8, HASH:lo12:WineIdName]; \
blr x16; \
ret; \
1:; \
svc HASH WindowsId; \
ret
// Allows for continuing from a full native context, as the NTDLL NtContinue export takes in an x64 context with EC and
// the conversion to that loses the ARM64EC ABI-disallowed registers that FEX uses.
+2 -4
View File
@@ -513,11 +513,9 @@ static void RethrowGuestException(const EXCEPTION_RECORD& Rec, ARM64_NT_CONTEXT&
class ECSyscallHandler : public FEXCore::HLE::SyscallHandler, public FEXCore::Allocator::FEXAllocOperators {
public:
ECSyscallHandler() {
OSABI = FEXCore::HLE::SyscallOSABI::OS_GENERIC;
}
ECSyscallHandler() = default;
uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) override {
void HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) override {
ProcessPendingCrossProcessEmulatorWork();
// Manually raise an exeption with the current JIT state packed into a native context, ntdll handles this and
+19 -11
View File
@@ -417,6 +417,20 @@ bool HandleSuspendInterrupt(TLS TLS, CONTEXT* Context, uint64_t FaultAddress) {
}
} // namespace Context
// Calls a 1-argument function `Func` setting the parent unwind frame information to the given SP and PC
__attribute__((naked)) extern "C" uint64_t SEHFrameTrampoline1Args(void* Arg0, void* Func, uint64_t Sp, uint64_t Pc) {
asm(".seh_proc SEHFrameTrampoline1Args;"
"stp x2, x3, [sp, #-0x10]!;"
".seh_pushframe;"
"stp x29, x30, [sp, #-0x10]!;"
".seh_save_fplr_x 16;"
".seh_endprologue;"
"blr x1;"
"ldp x29, x30, [sp], 0x20;"
"ret;"
".seh_endproc;");
}
// Calls a 2-argument function `Func` setting the parent unwind frame information to the given SP and PC
__attribute__((naked)) extern "C" uint64_t SEHFrameTrampoline2Args(void* Arg0, void* Arg1, void* Func, uint64_t Sp, uint64_t Pc) {
asm(".seh_proc SEHFrameTrampoline2Args;"
@@ -433,11 +447,9 @@ __attribute__((naked)) extern "C" uint64_t SEHFrameTrampoline2Args(void* Arg0, v
class WowSyscallHandler : public FEXCore::HLE::SyscallHandler, public FEXCore::Allocator::FEXAllocOperators {
public:
WowSyscallHandler() {
OSABI = FEXCore::HLE::SyscallOSABI::OS_GENERIC;
}
WowSyscallHandler() = default;
static uint64_t HandleSyscallImpl(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) {
static void HandleSyscallImpl(FEXCore::Core::CpuStateFrame* Frame) {
const uint64_t ReturnRIP = *(uint32_t*)(Frame->State.gregs[FEXCore::X86State::REG_RSP]); // Return address from the stack
uint64_t ReturnRSP = Frame->State.gregs[FEXCore::X86State::REG_RSP] + 4; // Stack pointer after popping return address
uint64_t ReturnRAX = 0;
@@ -470,20 +482,16 @@ public:
Context::LockJITContext(TLS);
Frame->State.gregs[FEXCore::X86State::REG_RAX] = ReturnRAX;
}
// NORETURNEDRESULT causes this result to be ignored since we restore all registers back from memory after a syscall anyway
return 0;
}
uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) override {
void HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) override {
const auto TLS = GetTLS();
// Stash the the context pointer on the stack, as Simulate can be called from this syscall handler which would overwrite it
CONTEXT* EntryContext = TLS.EntryContext();
// Call the syscall handler with unwind information pointing to Simulate as its caller
uint64_t Ret = SEHFrameTrampoline2Args(reinterpret_cast<void*>(Frame), reinterpret_cast<void*>(Args),
reinterpret_cast<void*>(&HandleSyscallImpl), EntryContext->Sp, EntryContext->Pc);
uint64_t Ret =
SEHFrameTrampoline1Args(reinterpret_cast<void*>(Frame), reinterpret_cast<void*>(&HandleSyscallImpl), EntryContext->Sp, EntryContext->Pc);
TLS.EntryContext() = EntryContext;
return Ret;
}
std::optional<FEXCore::ExecutableFileSectionInfo> LookupExecutableFileSection(FEXCore::Core::InternalThreadState*, uint64_t Address) override {