diff --git a/FEXCore/Source/Interface/Core/Frontend.cpp b/FEXCore/Source/Interface/Core/Frontend.cpp index 3ea384c44..ff5288392 100644 --- a/FEXCore/Source/Interface/Core/Frontend.cpp +++ b/FEXCore/Source/Interface/Core/Frontend.cpp @@ -69,7 +69,6 @@ static uint32_t MapVEXToReg(uint8_t vvvv, bool HasXMM) { Decoder::Decoder(FEXCore::Core::InternalThreadState* Thread) : Thread {Thread} , CTX {static_cast(Thread->CTX)} - , OSABI {CTX->SyscallHandler ? CTX->SyscallHandler->GetOSABI() : FEXCore::HLE::SyscallOSABI::OS_UNKNOWN} , PoolObject {CTX->FrontendAllocator, sizeof(FEXCore::X86Tables::DecodedInst) * DefaultDecodedBufferSize} { FEX_CONFIG_OPT(ReducedPrecision, X87REDUCEDPRECISION); @@ -1358,7 +1357,8 @@ const Decoder::DecodeStream Decoder::AdjustAddrForSpecialRegion(const uint8_t* _ constexpr uint64_t VSyscall_Base = 0xFFFF'FFFF'FF60'0000ULL; constexpr uint64_t VSyscall_End = VSyscall_Base + 0x1000; - if (OSABI == FEXCore::HLE::SyscallOSABI::OS_LINUX64 && RIP >= VSyscall_Base && RIP < VSyscall_End) { + if (BlockInfo.Is64BitMode && CTX->HostFeatures.HostType == FEXCore::HostFeatures::HostTypeEnum::Linux && RIP >= VSyscall_Base && + RIP < VSyscall_End) { // VSyscall // This doesn't exist on AArch64 and on x86_64 hosts this is emulated with faults to a region mapped with --xp permissions // Offset 0: vgettimeofday diff --git a/FEXCore/Source/Interface/Core/Frontend.h b/FEXCore/Source/Interface/Core/Frontend.h index f668d2b9c..e98f864b3 100644 --- a/FEXCore/Source/Interface/Core/Frontend.h +++ b/FEXCore/Source/Interface/Core/Frontend.h @@ -19,9 +19,6 @@ namespace FEXCore::Context { class ContextImpl; } -namespace FEXCore::HLE { -enum class SyscallOSABI; -} namespace FEXCore::Frontend { class Decoder final { @@ -89,7 +86,6 @@ private: FEXCore::Core::InternalThreadState* Thread; FEXCore::Context::ContextImpl* CTX; - const FEXCore::HLE::SyscallOSABI OSABI {}; FEX_CONFIG_OPT(EnableCodeCacheValidation, ENABLECODECACHEVALIDATION); diff --git a/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp b/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp index d555e0648..2f67eed3e 100644 --- a/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/BranchOps.cpp @@ -277,11 +277,9 @@ DEF_OP(CondJump) { } DEF_OP(Syscall) { - auto Op = IROp->C(); // Arguments are passed as follows: // X0: SyscallHandler // X1: ThreadState - // X2: Pointer to SyscallArguments PushDynamicRegs(TMP1); @@ -300,31 +298,18 @@ DEF_OP(Syscall) { LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, GPRSpillMask & 0xFFFF); str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo)); - uint64_t SPOffset = AlignUp(FEXCore::HLE::SyscallArguments::MAX_ARGS * 8, 16); - sub(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset); - for (uint32_t i = 0; i < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++i) { - if (Op->Header.Args[i].IsInvalid()) { - continue; - } - str(GetReg(Op->Header.Args[i]).X(), ARMEmitter::Reg::rsp, i * 8); - } - ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.SyscallHandlerObj)); ldr(ARMEmitter::XReg::x3, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.SyscallHandlerFunc)); mov(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r1, STATE.R()); - // SP supporting move - add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r2, ARMEmitter::Reg::rsp, 0); if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] { GenerateIndirectRuntimeCall(ARMEmitter::Reg::r3); } else { blr(ARMEmitter::Reg::r3); } - add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset); - - // Result is now in x0 // Fix the stack and any values that were stepped on + // Syscall result is in any static register that the frontend desired. FillStaticRegs({ .OptionalReg = ARMEmitter::Reg::r1, .OptionalReg2 = ARMEmitter::Reg::r2, @@ -337,14 +322,6 @@ DEF_OP(Syscall) { str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo)); PopDynamicRegs(); - - const auto OSABI = CTX->SyscallHandler->GetOSABI(); - - if (OSABI != FEXCore::HLE::SyscallOSABI::OS_GENERIC) { - // Move result to its destination register. - // Only if `NORETURNEDRESULT` wasn't set, otherwise we might overwrite the CPUState refilled with `FillStaticRegs` - mov(ARMEmitter::Size::i64Bit, GetReg(Node), ARMEmitter::Reg::r0); - } } DEF_OP(Thunk) { diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 20de85532..d2552f433 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -36,35 +36,6 @@ using X86Tables::OpToIndex; #define OpcodeArgs [[maybe_unused]] FEXCore::X86Tables::DecodedOp Op void OpDispatchBuilder::SyscallOp(OpcodeArgs, bool IsSyscallInst) { - constexpr size_t SyscallArgs = 7; - using SyscallArray = std::array; - - size_t NumArguments {}; - const SyscallArray* GPRIndexes {}; - 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 OSABI = CTX->SyscallHandler->GetOSABI(); - if (OSABI == FEXCore::HLE::SyscallOSABI::OS_LINUX64) { - NumArguments = GPRIndexes_64.size(); - GPRIndexes = &GPRIndexes_64; - } else if (OSABI == FEXCore::HLE::SyscallOSABI::OS_LINUX32) { - NumArguments = GPRIndexes_32.size(); - GPRIndexes = &GPRIndexes_32; - } else if (OSABI == FEXCore::HLE::SyscallOSABI::OS_GENERIC) { - // All registers will be spilled before the syscall and filled afterwards so no JIT-side argument handling is necessary. - NumArguments = 0; - GPRIndexes = nullptr; - } else { - ERROR_AND_DIE_FMT("Unhandled OSABI syscall"); - } - // Calculate flags early. CalculateDeferredFlags(); @@ -72,13 +43,6 @@ void OpDispatchBuilder::SyscallOp(OpcodeArgs, bool IsSyscallInst) { auto NewRIP = GetRelocatedPC(Op, -Op->InstSize); _StoreContextGPR(GPRSize, NewRIP, offsetof(FEXCore::Core::CPUState, rip)); - Ref Arguments[SyscallArgs] { - InvalidNode, InvalidNode, InvalidNode, InvalidNode, InvalidNode, InvalidNode, InvalidNode, - }; - for (size_t i = 0; i < NumArguments; ++i) { - Arguments[i] = LoadGPRRegister(GPRIndexes->at(i)); - } - if (IsSyscallInst) { // If this is the `Syscall` instruction rather than `int 0x80` then we need to do some additional work. // RCX = RIP after this instruction @@ -94,12 +58,7 @@ void OpDispatchBuilder::SyscallOp(OpcodeArgs, bool IsSyscallInst) { } FlushRegisterCache(); - auto SyscallOp = _Syscall(Arguments[0], Arguments[1], Arguments[2], Arguments[3], Arguments[4], Arguments[5], Arguments[6]); - - // Generic ABI doesn't store result in RAX. - if (OSABI != FEXCore::HLE::SyscallOSABI::OS_GENERIC) { - StoreGPRRegister(X86State::REG_RAX, SyscallOp); - } + _Syscall(); if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_BLOCK_END) { // RIP could have been updated after coming back from the Syscall. @@ -5051,11 +5010,6 @@ void OpDispatchBuilder::RDTSCPOp(OpcodeArgs) { } void OpDispatchBuilder::RDPIDOp(OpcodeArgs) { - if (CTX->HostFeatures.HostType != FEXCore::HostFeatures::HostTypeEnum::Linux && !CTX->HostFeatures.SupportsCPUIndexInTPIDRRO) { - // RDTSCP is unsupported on Win32 platforms if TPIDRRO isn't supported. - UnimplementedOp(Op); - return; - } StoreResultGPR(Op, _ProcessorID()); } diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index 5d46bdf57..2fc6a6960 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -326,11 +326,10 @@ "CallbackReturn": { "HasSideEffects": true }, - "GPR = Syscall GPR:$SyscallID, GPR:$Arg0, GPR:$Arg1, GPR:$Arg2, GPR:$Arg3, GPR:$Arg4, GPR:$Arg5": { + "Syscall": { "HasSideEffects": true, "Desc": ["Dispatches a guest syscall through to the SyscallHandler class" - ], - "DestSize": "OpSize::i64Bit" + ] }, "Thunk GPR:$ArgPtr, SHA256Sum:$ThunkNameHash": { diff --git a/FEXCore/include/FEXCore/Core/DiskCache.h b/FEXCore/include/FEXCore/Core/DiskCache.h index 760426c3b..ee21be3f6 100644 --- a/FEXCore/include/FEXCore/Core/DiskCache.h +++ b/FEXCore/include/FEXCore/Core/DiskCache.h @@ -205,7 +205,7 @@ namespace DiskCache { // TODO: This header is in global installed header path, but uses internal headers. // Migrate this once that is fixed. - static constexpr uint16_t FormatVersion = 4; + static constexpr uint16_t FormatVersion = 5; FEX_DEFAULT_VISIBILITY uint16_t GetFormatVersion(); } // namespace DiskCache diff --git a/FEXCore/include/FEXCore/HLE/SyscallHandler.h b/FEXCore/include/FEXCore/HLE/SyscallHandler.h index d1f4decd7..0ba0218e3 100644 --- a/FEXCore/include/FEXCore/HLE/SyscallHandler.h +++ b/FEXCore/include/FEXCore/HLE/SyscallHandler.h @@ -17,29 +17,6 @@ struct CpuStateFrame; } // namespace FEXCore::Core namespace FEXCore::HLE { -struct SyscallArguments { - static constexpr std::size_t MAX_ARGS = 7; - uint64_t Argument[MAX_ARGS]; -}; - -struct SyscallABI { - // Expectation is that the backend will be aware of how to modify the arguments based on numbering - // Only GPRs expected - uint8_t NumArgs; - // If the syscall has a return then it should be stored in the ABI specific syscall register - // Linux = RAX - bool HasReturn; - - int32_t HostSyscallNumber; -}; - -enum class SyscallOSABI { - OS_UNKNOWN, - OS_LINUX64, - OS_LINUX32, - OS_GENERIC, // No JIT-side argument handling, spill/fill all regs. -}; - struct ExecutableRangeInfo { uint64_t Base; uint64_t Size; @@ -53,11 +30,8 @@ class SyscallHandler { public: virtual ~SyscallHandler() = default; - virtual uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) = 0; + virtual void HandleSyscall(FEXCore::Core::CpuStateFrame* Frame) = 0; - SyscallOSABI GetOSABI() const { - return OSABI; - } virtual void MarkGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) {} virtual void InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) {} virtual void MarkOvercommitRange(uint64_t Start, uint64_t Length) {} @@ -72,8 +46,5 @@ public: } virtual void SleepThread(FEXCore::Context::Context* CTX, FEXCore::Core::CpuStateFrame* Frame) {} - -protected: - SyscallOSABI OSABI; }; } // namespace FEXCore::HLE diff --git a/Source/Tools/CodeSizeValidation/Main.cpp b/Source/Tools/CodeSizeValidation/Main.cpp index 86de9e2d7..07436620a 100644 --- a/Source/Tools/CodeSizeValidation/Main.cpp +++ b/Source/Tools/CodeSizeValidation/Main.cpp @@ -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 diff --git a/Source/Tools/CommonTools/DummyHandlers.h b/Source/Tools/CommonTools/DummyHandlers.h index 91666a525..3de15eb13 100644 --- a/Source/Tools/CommonTools/DummyHandlers.h +++ b/Source/Tools/CommonTools/DummyHandlers.h @@ -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 diff --git a/Source/Tools/FEXOfflineCompiler/Main.cpp b/Source/Tools/FEXOfflineCompiler/Main.cpp index 0c344dc91..e40ecdb64 100644 --- a/Source/Tools/FEXOfflineCompiler/Main.cpp +++ b/Source/Tools/FEXOfflineCompiler/Main.cpp @@ -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 GenerateSingleCache(FEXCore::ExecutableFileInf #ifdef _WIN32 OvercommitTracker = std::make_unique(IsWine); - auto SyscallOSABI = FEXCore::HLE::SyscallOSABI::OS_GENERIC; - auto SyscallHandler = std::make_unique(*CTX, SyscallOSABI); + auto SyscallHandler = std::make_unique(*CTX); SyscallHandler->VAFileStart = TryMapImage(SyscallHandler->InvalidationTracker, SyscallHandler->ImageTracker, Binary.FileId, Binary).value_or(0); @@ -441,8 +437,7 @@ static std::optional 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(*CTX, SyscallOSABI); + auto SyscallHandler = std::make_unique(*CTX); // Populate relocations from ELF file { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.cpp index 539ffa3c3..7b0e9f91c 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.cpp @@ -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; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.h index 0ea2af834..b10f664ed 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Seccomp/SeccompEmulator.h @@ -9,6 +9,7 @@ $end_info$ #include #include #include +#include "LinuxSyscalls/ThreadManager.h" #include #include @@ -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; } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp index c391337de..01600e605 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp @@ -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(__builtin_extract_return_addr(__builtin_return_address(0))); +template +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(__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(Frame, JITPC); + } else { + HandleSyscallImpl(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; } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index 3909930cb..7b417e83e 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -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 GenerateMap(std::string_view GuestBinaryFile, std::string_view GuestBinaryFileId) override; fextl::unique_ptr Alloc32Handler {}; std::atomic 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; + + 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 + void HandleSyscallImpl(FEXCore::Core::CpuStateFrame* Frame, uint64_t JITPC); }; #define SYSCALL_ERRNO() \ diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp index ea52df84c..7cea2833b 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp @@ -29,7 +29,6 @@ x32SyscallHandler::x32SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::S FEX::HLE::ThunkHandler* ThunkHandler, fextl::unique_ptr Allocator) : SyscallHandler {ctx, _SignalDelegation, ThunkHandler} , AllocHandler {std::move(Allocator)} { - OSABI = FEXCore::HLE::SyscallOSABI::OS_LINUX32; RegisterSyscallHandlers(); } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp index 55d8b1922..5b69a5d7b 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp @@ -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(); } diff --git a/Source/Windows/ARM64EC/Module.S b/Source/Windows/ARM64EC/Module.S index 4c18172ea..f76f92d77 100644 --- a/Source/Windows/ARM64EC/Module.S +++ b/Source/Windows/ARM64EC/Module.S @@ -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. diff --git a/Source/Windows/ARM64EC/Module.cpp b/Source/Windows/ARM64EC/Module.cpp index c8df78bfe..d15d0676c 100644 --- a/Source/Windows/ARM64EC/Module.cpp +++ b/Source/Windows/ARM64EC/Module.cpp @@ -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 diff --git a/Source/Windows/WOW64/Module.cpp b/Source/Windows/WOW64/Module.cpp index f6533cf7a..0fca8574d 100644 --- a/Source/Windows/WOW64/Module.cpp +++ b/Source/Windows/WOW64/Module.cpp @@ -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(Frame), reinterpret_cast(Args), - reinterpret_cast(&HandleSyscallImpl), EntryContext->Sp, EntryContext->Pc); + uint64_t Ret = + SEHFrameTrampoline1Args(reinterpret_cast(Frame), reinterpret_cast(&HandleSyscallImpl), EntryContext->Sp, EntryContext->Pc); TLS.EntryContext() = EntryContext; - return Ret; } std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState*, uint64_t Address) override {