From 33a675624ca8b36b9b36e36d8cbbac0ee64c6a2a Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 19 Sep 2024 22:07:47 -0700 Subject: [PATCH 1/9] FEXCore/Thunks: Remove lock passing in AddCustomIREntrypoint This was a legacy feature that is no longer necessary every since we removed the frontend IRLoader. --- FEXCore/Source/Interface/Context/Context.h | 15 ++++----------- FEXCore/Source/Interface/Core/Core.cpp | 10 +++++----- FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp | 6 +++--- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index ad8b20cc09..fc9ad6f758 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -68,17 +68,9 @@ struct CustomIRResult { void* Creator; void* Data; - explicit operator bool() const noexcept { - return !lock; - } - - CustomIRResult(std::unique_lock&& lock, void* Creator, void* Data) + CustomIRResult(void* Creator, void* Data) : Creator(Creator) - , Data(Data) - , lock(std::move(lock)) {} - -private: - std::unique_lock lock; + , Data(Data) {} }; using BlockDelinkerFunc = void (*)(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record); @@ -194,7 +186,8 @@ class ContextImpl final : public FEXCore::Context::Context { bool IsAddressInCodeBuffer(FEXCore::Core::InternalThreadState* Thread, uintptr_t Address) const override; // returns false if a handler was already registered - CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator = nullptr, void* Data = nullptr); + std::optional + AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator = nullptr, void* Data = nullptr); void AppendThunkDefinitions(std::span Definitions) override; diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 37726b277c..babca608ab 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -982,7 +982,8 @@ void ContextImpl::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState* Thre Thread->LookupCache->Erase(Thread->CurrentFrame, GuestRIP); } -CustomIRResult ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator, void* Data) { +std::optional +ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator, void* Data) { LOGMAN_THROW_A_FMT(Config.Is64BitMode || !(Entrypoint >> 32), "64-bit Entrypoint in 32-bit mode {:x}", Entrypoint); std::unique_lock lk(CustomIRMutex); @@ -992,11 +993,10 @@ CustomIRResult ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIR if (!InsertedIterator.second) { const auto& [fn, Creator, Data] = InsertedIterator.first->second; - return CustomIRResult(std::move(lk), Creator, Data); - } else { - lk.unlock(); - return CustomIRResult(std::move(lk), 0, 0); + return CustomIRResult(Creator, Data); } + + return std::nullopt; } void ContextImpl::RemoveCustomIREntrypoint(uintptr_t Entrypoint) { diff --git a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index fc3860cabd..2c2b5412a7 100644 --- a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -234,11 +234,11 @@ struct ThunkHandler_impl final : public ThunkHandler { }, CTX->ThunkHandler.get(), (void*)args->target_addr); - if (!Result) { - if (Result.Creator != CTX->ThunkHandler.get()) { + if (Result.has_value()) { + if (Result->Creator != CTX->ThunkHandler.get()) { ERROR_AND_DIE_FMT("Input address for LinkAddressToGuestFunction is already linked by another module"); } - if (Result.Data != (void*)args->target_addr) { + if (Result->Data != (void*)args->target_addr) { // NOTE: This may happen in Vulkan thunks if the Vulkan driver resolves two different symbols // to the same function (e.g. vkGetPhysicalDeviceFeatures2/vkGetPhysicalDeviceFeatures2KHR) LogMan::Msg::EFmt("Input address for LinkAddressToGuestFunction is already linked elsewhere"); From aa44668a416e1c60e1d0e41157776c59fb3bfcb5 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 19 Sep 2024 22:17:34 -0700 Subject: [PATCH 2/9] FEXCore/Thunks: Moves the implementation of LinkAddressToGuestFunction to ContextImpl No functional change, just moving code here. --- FEXCore/Source/Interface/Context/Context.h | 2 + FEXCore/Source/Interface/Core/Core.cpp | 41 +++++++++++++++++ .../Source/Interface/HLE/Thunks/Thunks.cpp | 45 +------------------ FEXCore/include/FEXCore/Core/Context.h | 8 ++++ 4 files changed, 53 insertions(+), 43 deletions(-) diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index fc9ad6f758..e553656ba9 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -189,6 +189,8 @@ class ContextImpl final : public FEXCore::Context::Context { std::optional AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandler Handler, void* Creator = nullptr, void* Data = nullptr); + void AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t GuestThunkEntrypoint) override; + void AppendThunkDefinitions(std::span Definitions) override; public: diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index babca608ab..d067a21178 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -999,6 +999,47 @@ ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, CustomIREntrypointHandl return std::nullopt; } +void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t GuestThunkEntrypoint) { + LOGMAN_THROW_AA_FMT(Entrypoint, "Tried to link null pointer address to guest function"); + LOGMAN_THROW_AA_FMT(GuestThunkEntrypoint, "Tried to link address to null pointer guest function"); + if (!Config.Is64BitMode) { + LOGMAN_THROW_AA_FMT((Entrypoint >> 32) == 0, "Tried to link 64-bit address in 32-bit mode"); + LOGMAN_THROW_AA_FMT((GuestThunkEntrypoint >> 32) == 0, "Tried to link 64-bit address in 32-bit mode"); + } + + LogMan::Msg::DFmt("Thunks: Adding guest trampoline from address {:#x} to guest function {:#x}", Entrypoint, GuestThunkEntrypoint); + + auto Result = AddCustomIREntrypoint( + Entrypoint, + [this, GuestThunkEntrypoint](uintptr_t Entrypoint, FEXCore::IR::IREmitter* emit) { + auto IRHeader = emit->_IRHeader(emit->Invalid(), Entrypoint, 0, 0); + auto Block = emit->CreateCodeNode(); + IRHeader.first->Blocks = emit->WrapNode(Block); + emit->SetCurrentCodeBlock(Block); + + const uint8_t GPRSize = GetGPRSize(); + + if (GPRSize == 8) { + emit->_StoreRegister(emit->_Constant(Entrypoint), X86State::REG_R11, IR::GPRClass, GPRSize); + } else { + emit->_StoreContext(GPRSize, IR::FPRClass, emit->_VCastFromGPR(8, 8, emit->_Constant(Entrypoint)), offsetof(Core::CPUState, mm[0][0])); + } + emit->_ExitFunction(emit->_Constant(GuestThunkEntrypoint)); + }, + ThunkHandler.get(), (void*)GuestThunkEntrypoint); + + if (Result.has_value()) { + if (Result->Creator != ThunkHandler.get()) { + ERROR_AND_DIE_FMT("Input address for AddThunkTrampoline is already linked by another module"); + } + if (Result->Data != (void*)GuestThunkEntrypoint) { + // NOTE: This may happen in Vulkan thunks if the Vulkan driver resolves two different symbols + // to the same function (e.g. vkGetPhysicalDeviceFeatures2/vkGetPhysicalDeviceFeatures2KHR) + LogMan::Msg::EFmt("Input address for AddThunkTrampoline is already linked elsewhere"); + } + } +} + void ContextImpl::RemoveCustomIREntrypoint(uintptr_t Entrypoint) { LOGMAN_THROW_A_FMT(Config.Is64BitMode || !(Entrypoint >> 32), "64-bit Entrypoint in 32-bit mode {:x}", Entrypoint); diff --git a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index 2c2b5412a7..9e0eaa24de 100644 --- a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -6,9 +6,6 @@ tags: glue|thunks $end_info$ */ -#include "Interface/IR/IR.h" -#include "Interface/IR/IREmitter.h" - #include #include #include @@ -204,46 +201,8 @@ struct ThunkHandler_impl final : public ThunkHandler { }; auto args = reinterpret_cast(argsv); - auto CTX = static_cast(Thread->CTX); - - LOGMAN_THROW_AA_FMT(args->original_callee, "Tried to link null pointer address to guest function"); - LOGMAN_THROW_AA_FMT(args->target_addr, "Tried to link address to null pointer guest function"); - if (!CTX->Config.Is64BitMode) { - LOGMAN_THROW_AA_FMT((args->original_callee >> 32) == 0, "Tried to link 64-bit address in 32-bit mode"); - LOGMAN_THROW_AA_FMT((args->target_addr >> 32) == 0, "Tried to link 64-bit address in 32-bit mode"); - } - - LogMan::Msg::DFmt("Thunks: Adding guest trampoline from address {:#x} to guest function {:#x}", args->original_callee, args->target_addr); - - auto Result = CTX->AddCustomIREntrypoint( - args->original_callee, - [CTX, GuestThunkEntrypoint = args->target_addr](uintptr_t Entrypoint, FEXCore::IR::IREmitter* emit) { - auto IRHeader = emit->_IRHeader(emit->Invalid(), Entrypoint, 0, 0); - auto Block = emit->CreateCodeNode(); - IRHeader.first->Blocks = emit->WrapNode(Block); - emit->SetCurrentCodeBlock(Block); - - const uint8_t GPRSize = CTX->GetGPRSize(); - - if (GPRSize == 8) { - emit->_StoreRegister(emit->_Constant(Entrypoint), X86State::REG_R11, IR::GPRClass, GPRSize); - } else { - emit->_StoreContext(GPRSize, IR::FPRClass, emit->_VCastFromGPR(8, 8, emit->_Constant(Entrypoint)), offsetof(Core::CPUState, mm[0][0])); - } - emit->_ExitFunction(emit->_Constant(GuestThunkEntrypoint)); - }, - CTX->ThunkHandler.get(), (void*)args->target_addr); - - if (Result.has_value()) { - if (Result->Creator != CTX->ThunkHandler.get()) { - ERROR_AND_DIE_FMT("Input address for LinkAddressToGuestFunction is already linked by another module"); - } - if (Result->Data != (void*)args->target_addr) { - // NOTE: This may happen in Vulkan thunks if the Vulkan driver resolves two different symbols - // to the same function (e.g. vkGetPhysicalDeviceFeatures2/vkGetPhysicalDeviceFeatures2KHR) - LogMan::Msg::EFmt("Input address for LinkAddressToGuestFunction is already linked elsewhere"); - } - } + auto CTX = static_cast(Thread->CTX); + CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); } /** diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index 387aada428..e3c7e7443b 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -235,6 +235,14 @@ class Context { */ FEX_DEFAULT_VISIBILITY virtual void EnableExitOnHLT() = 0; + /** + * @brief Adds a new Thunk trampoline handler + * + * @param Entrypoint The guest PC that the custom thunk trampoline IR handler will be installed at. + * @param GuestThunkEntrypoint The thunk entrypoint that the IR handler will redirect to. + */ + FEX_DEFAULT_VISIBILITY virtual void AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t GuestThunkEntrypoint) = 0; + private: }; From 94462e4dd0748fdac1aa22d55b487a1f2b331f08 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 19 Sep 2024 22:43:04 -0700 Subject: [PATCH 3/9] Thunks: Temporarily add more TLS variables while moving Prep commit to give Thunks the data it needs before moving. --- FEXCore/Source/Interface/Context/Context.h | 3 - FEXCore/Source/Interface/Core/Core.cpp | 2 +- .../Source/Interface/HLE/Thunks/Thunks.cpp | 72 ++++++++++--------- FEXCore/Source/Interface/HLE/Thunks/Thunks.h | 7 +- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index e553656ba9..89583140a3 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -223,9 +223,6 @@ class ContextImpl final : public FEXCore::Context::Context { FEX_CONFIG_OPT(SMCChecks, SMCCHECKS); FEX_CONFIG_OPT(MaxInstPerBlock, MAXINST); FEX_CONFIG_OPT(RootFSPath, ROOTFS); - FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS); - FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32); - FEX_CONFIG_OPT(ThunkConfigFile, THUNKCONFIG); FEX_CONFIG_OPT(GlobalJITNaming, GLOBALJITNAMING); FEX_CONFIG_OPT(LibraryJITNaming, LIBRARYJITNAMING); FEX_CONFIG_OPT(BlockJITNaming, BLOCKJITNAMING); diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index d067a21178..a43f6f2234 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -387,7 +387,7 @@ void ContextImpl::ExecuteThread(FEXCore::Core::InternalThreadState* Thread) { void ContextImpl::InitializeThreadTLSData(FEXCore::Core::InternalThreadState* Thread) { // Let's do some initial bookkeeping here if (ThunkHandler) { - ThunkHandler->RegisterTLSState(Thread); + ThunkHandler->RegisterTLSState(this, Thread); } #ifndef _WIN32 Alloc::OSAllocator::RegisterTLSData(Thread); diff --git a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index 9e0eaa24de..058202d7e0 100644 --- a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -8,6 +8,7 @@ tags: glue|thunks #include #include +#include #include #include #include @@ -22,10 +23,8 @@ tags: glue|thunks #endif #include -#include "FEXCore/Core/X86Enums.h" #include #include -#include #include #include #include @@ -76,8 +75,15 @@ struct LoadlibArgs { const char* Name; }; -static thread_local FEXCore::Core::InternalThreadState* Thread = nullptr; +struct ThunkHandler_impl; +struct TEMP_TLS_DATA { + FEXCore::Core::InternalThreadState* Thread {}; + ThunkHandler_impl* ThunkHandler {}; + FEXCore::Context::Context* CTX {}; +}; + +static thread_local TEMP_TLS_DATA ThreadData {}; struct ExportEntry { uint8_t* sha256; @@ -165,23 +171,22 @@ struct ThunkHandler_impl final : public ThunkHandler { Set arg0/1 to arg regs, use CTX::HandleCallback to handle the callback */ static void CallCallback(void* callback, void* arg0, void* arg1) { - if (!Thread) { + if (!ThreadData.Thread) { ERROR_AND_DIE_FMT("Thunked library attempted to invoke guest callback asynchronously"); } - auto CTX = static_cast(Thread->CTX); - if (CTX->Config.Is64BitMode) { - Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0; - Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1; + if (ThreadData.ThunkHandler->Is64BitMode()) { + ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0; + ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1; } else { if ((reinterpret_cast(arg1) >> 32) != 0) { ERROR_AND_DIE_FMT("Tried to call guest function with arguments packed to a 64-bit address"); } - Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RCX] = (uintptr_t)arg0; - Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDX] = (uintptr_t)arg1; + ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RCX] = (uintptr_t)arg0; + ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDX] = (uintptr_t)arg1; } - Thread->CTX->HandleCallback(Thread, (uintptr_t)callback); + ThreadData.CTX->HandleCallback(ThreadData.Thread, (uintptr_t)callback); } /** @@ -201,8 +206,7 @@ struct ThunkHandler_impl final : public ThunkHandler { }; auto args = reinterpret_cast(argsv); - auto CTX = static_cast(Thread->CTX); - CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); + ThreadData.CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); } /** @@ -247,13 +251,13 @@ struct ThunkHandler_impl final : public ThunkHandler { } static void LoadLib(void* ArgsV) { - auto CTX = static_cast(Thread->CTX); - auto Args = reinterpret_cast(ArgsV); std::string_view Name = Args->Name; - auto SOName = (CTX->Config.Is64BitMode() ? CTX->Config.ThunkHostLibsPath() : CTX->Config.ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so"; + auto SOName = + (ThreadData.ThunkHandler->Is64BitMode() ? ThreadData.ThunkHandler->ThunkHostLibsPath() : ThreadData.ThunkHandler->ThunkHostLibsPath32()) + + "/" + Name.data() + "-host.so"; LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName); @@ -281,16 +285,14 @@ struct ThunkHandler_impl final : public ThunkHandler { Name); } - auto That = reinterpret_cast(CTX->ThunkHandler.get()); - { - std::lock_guard lk(That->ThunksMutex); + std::lock_guard lk(ThreadData.ThunkHandler->ThunksMutex); - That->Libs.insert(fextl::string {Name}); + ThreadData.ThunkHandler->Libs.insert(fextl::string {Name}); int i; for (i = 0; Exports[i].sha256; i++) { - That->Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; + ThreadData.ThunkHandler->Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; } LogMan::Msg::DFmt("Loaded {} syms", i); @@ -305,12 +307,9 @@ struct ThunkHandler_impl final : public ThunkHandler { auto& [Name, rv] = *reinterpret_cast(ArgsRV); - auto CTX = static_cast(Thread->CTX); - auto That = reinterpret_cast(CTX->ThunkHandler.get()); - { - std::shared_lock lk(That->ThunksMutex); - rv = That->Libs.contains(Name); + std::shared_lock lk(ThreadData.ThunkHandler->ThunksMutex); + rv = ThreadData.ThunkHandler->Libs.contains(Name); } } @@ -327,8 +326,10 @@ struct ThunkHandler_impl final : public ThunkHandler { } } - void RegisterTLSState(FEXCore::Core::InternalThreadState* _Thread) override { - Thread = _Thread; + void RegisterTLSState(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState* _Thread) override { + ThreadData.Thread = _Thread; + ThreadData.ThunkHandler = this; + ThreadData.CTX = CTX; } void AppendThunkDefinitions(std::span Definitions) override { @@ -336,6 +337,10 @@ struct ThunkHandler_impl final : public ThunkHandler { Thunks.emplace(Definition.Sum, Definition.ThunkFunction); } } + + FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); + FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS); + FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32); }; fextl::unique_ptr ThunkHandler::Create() { @@ -369,8 +374,7 @@ FEX_DEFAULT_VISIBILITY HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) { LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function"); - const auto CTX = static_cast(Thread->CTX); - const auto ThunkHandler = reinterpret_cast(CTX->ThunkHandler.get()); + const auto ThunkHandler = reinterpret_cast(ThreadData.ThunkHandler); const GuestcallInfo gci = {GuestUnpacker, GuestTarget}; @@ -436,15 +440,15 @@ FEX_DEFAULT_VISIBILITY void FinalizeHostTrampolineForGuestFunction(HostToGuestTr } FEX_DEFAULT_VISIBILITY void* GetGuestStack() { - if (!Thread) { + if (!ThreadData.Thread) { ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously"); } - return (void*)(uintptr_t)((Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP])); + return (void*)(uintptr_t)((ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP])); } FEX_DEFAULT_VISIBILITY void MoveGuestStack(uintptr_t NewAddress) { - if (!Thread) { + if (!ThreadData.Thread) { ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously"); } @@ -452,7 +456,7 @@ FEX_DEFAULT_VISIBILITY void MoveGuestStack(uintptr_t NewAddress) { ERROR_AND_DIE_FMT("Tried to set stack pointer for 32-bit guest to a 64-bit address"); } - Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = NewAddress; + ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = NewAddress; } #else diff --git a/FEXCore/Source/Interface/HLE/Thunks/Thunks.h b/FEXCore/Source/Interface/HLE/Thunks/Thunks.h index 6977924f53..de31da425f 100644 --- a/FEXCore/Source/Interface/HLE/Thunks/Thunks.h +++ b/FEXCore/Source/Interface/HLE/Thunks/Thunks.h @@ -7,13 +7,12 @@ tags: glue|thunks #pragma once -#include "Interface/IR/IR.h" - #include #include +#include namespace FEXCore::Context { -class ContextImpl; +class Context; } namespace FEXCore::Core { @@ -30,7 +29,7 @@ typedef void ThunkedFunction(void* ArgsRv); class ThunkHandler { public: virtual ThunkedFunction* LookupThunk(const IR::SHA256Sum& sha256) = 0; - virtual void RegisterTLSState(FEXCore::Core::InternalThreadState* Thread) = 0; + virtual void RegisterTLSState(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState* Thread) = 0; virtual ~ThunkHandler() {} static fextl::unique_ptr Create(); From 24211f8523415a27c4dd2fd5ad0aaed27f59ff4e Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 19 Sep 2024 23:30:28 -0700 Subject: [PATCH 4/9] FEXCore: Move ThunksHandler class to FEXLoader With as little changes as possible, because this is fairly tricky. --- FEXCore/Source/CMakeLists.txt | 1 - FEXCore/Source/Interface/Context/Context.cpp | 5 +++ FEXCore/Source/Interface/Context/Context.h | 3 +- .../Core/ArchHelpers/Arm64Emitter.cpp | 1 - FEXCore/Source/Interface/Core/Core.cpp | 10 ++---- .../Core/JIT/Arm64/Arm64Relocations.cpp | 3 +- .../Interface/Core/JIT/Arm64/BranchOps.cpp | 2 +- FEXCore/include/FEXCore/Core/Context.h | 2 ++ .../Thunks => include/FEXCore/Core}/Thunks.h | 3 -- Source/Tools/FEXLoader/FEXLoader.cpp | 5 +++ Source/Tools/LinuxEmulation/CMakeLists.txt | 1 + .../LinuxEmulation/LinuxSyscalls/Syscalls.h | 1 + .../Tools/LinuxEmulation}/Thunks.cpp | 33 ++++++++----------- Source/Tools/LinuxEmulation/Thunks.h | 13 ++++++++ 14 files changed, 48 insertions(+), 35 deletions(-) rename FEXCore/{Source/Interface/HLE/Thunks => include/FEXCore/Core}/Thunks.h (80%) rename {FEXCore/Source/Interface/HLE/Thunks => Source/Tools/LinuxEmulation}/Thunks.cpp (97%) create mode 100644 Source/Tools/LinuxEmulation/Thunks.h diff --git a/FEXCore/Source/CMakeLists.txt b/FEXCore/Source/CMakeLists.txt index c3183d53ce..3a56e03b82 100644 --- a/FEXCore/Source/CMakeLists.txt +++ b/FEXCore/Source/CMakeLists.txt @@ -127,7 +127,6 @@ set (SRCS Interface/Core/X86Tables/VEXTables.cpp Interface/Core/X86Tables/X87Tables.cpp Interface/Core/X86Tables/XOPTables.cpp - Interface/HLE/Thunks/Thunks.cpp Interface/GDBJIT/GDBJIT.cpp Interface/IR/AOTIR.cpp Interface/IR/IRDumper.cpp diff --git a/FEXCore/Source/Interface/Context/Context.cpp b/FEXCore/Source/Interface/Context/Context.cpp index 91a0ff082c..65ed7b9114 100644 --- a/FEXCore/Source/Interface/Context/Context.cpp +++ b/FEXCore/Source/Interface/Context/Context.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "FEXCore/Debug/InternalThreadState.h" #include @@ -48,6 +49,10 @@ void FEXCore::Context::ContextImpl::SetSyscallHandler(FEXCore::HLE::SyscallHandl SourcecodeResolver = Handler->GetSourcecodeResolver(); } +void FEXCore::Context::ContextImpl::SetThunkHandler(FEXCore::ThunkHandler* Handler) { + ThunkHandler = Handler; +} + FEXCore::CPUID::FunctionResults FEXCore::Context::ContextImpl::RunCPUIDFunction(uint32_t Function, uint32_t Leaf) { return CPUID.RunFunction(Function, Leaf); } diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index 89583140a3..a56079e1cb 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -147,6 +147,7 @@ class ContextImpl final : public FEXCore::Context::Context { #endif void SetSignalDelegator(FEXCore::SignalDelegator* SignalDelegation) override; void SetSyscallHandler(FEXCore::HLE::SyscallHandler* Handler) override; + void SetThunkHandler(FEXCore::ThunkHandler* Handler) override; FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) override; FEXCore::CPUID::XCRResults RunXCRFunction(uint32_t Function) override; @@ -247,7 +248,7 @@ class ContextImpl final : public FEXCore::Context::Context { FEXCore::CPUIDEmu CPUID; FEXCore::HLE::SyscallHandler* SyscallHandler {}; FEXCore::HLE::SourcecodeResolver* SourcecodeResolver {}; - fextl::unique_ptr ThunkHandler; + FEXCore::ThunkHandler* ThunkHandler {}; fextl::unique_ptr Dispatcher; FEXCore::Context::ExitHandler CustomExitHandler; diff --git a/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp b/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp index 58bc8a1469..e1472e9d0e 100644 --- a/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp +++ b/FEXCore/Source/Interface/Core/ArchHelpers/Arm64Emitter.cpp @@ -4,7 +4,6 @@ #include "FEXCore/Utils/AllocatorHooks.h" #include "Interface/Core/Dispatcher/Dispatcher.h" #include "Interface/Context/Context.h" -#include "Interface/HLE/Thunks/Thunks.h" #include #include diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index a43f6f2234..13d2747af3 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -19,7 +19,6 @@ desc: Glues Frontend, OpDispatcher and IR Opts & Compilation, LookupCache, Dispa #include "Interface/Core/JIT/JITCore.h" #include "Interface/Core/Dispatcher/Dispatcher.h" #include "Interface/Core/X86Tables/X86Tables.h" -#include "Interface/HLE/Thunks/Thunks.h" #include "Interface/IR/IR.h" #include "Interface/IR/IREmitter.h" #include "Interface/IR/Passes/RegisterAllocationPass.h" @@ -34,6 +33,7 @@ desc: Glues Frontend, OpDispatcher and IR Opts & Compilation, LookupCache, Dispa #include #include #include +#include #include #include #include @@ -346,7 +346,6 @@ bool ContextImpl::InitCore() { SignalDelegation->SetConfig(SignalConfig); #ifndef _WIN32 - ThunkHandler = FEXCore::ThunkHandler::Create(); #elif !defined(_M_ARM64EC) // WOW64 always needs the interrupt fault check to be enabled. Config.NeedsPendingInterruptFaultCheck = true; @@ -386,9 +385,6 @@ void ContextImpl::ExecuteThread(FEXCore::Core::InternalThreadState* Thread) { void ContextImpl::InitializeThreadTLSData(FEXCore::Core::InternalThreadState* Thread) { // Let's do some initial bookkeeping here - if (ThunkHandler) { - ThunkHandler->RegisterTLSState(this, Thread); - } #ifndef _WIN32 Alloc::OSAllocator::RegisterTLSData(Thread); #endif @@ -1026,10 +1022,10 @@ void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t Gu } emit->_ExitFunction(emit->_Constant(GuestThunkEntrypoint)); }, - ThunkHandler.get(), (void*)GuestThunkEntrypoint); + ThunkHandler, (void*)GuestThunkEntrypoint); if (Result.has_value()) { - if (Result->Creator != ThunkHandler.get()) { + if (Result->Creator != ThunkHandler) { ERROR_AND_DIE_FMT("Input address for AddThunkTrampoline is already linked by another module"); } if (Result->Data != (void*)GuestThunkEntrypoint) { diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/Arm64Relocations.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/Arm64Relocations.cpp index 67f7fc4dc5..c643ca733b 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/Arm64Relocations.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/Arm64Relocations.cpp @@ -7,7 +7,8 @@ desc: relocation logic of the arm64 splatter backend */ #include "Interface/Context/Context.h" #include "Interface/Core/JIT/Arm64/JITClass.h" -#include "Interface/HLE/Thunks/Thunks.h" + +#include namespace FEXCore::CPU { diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp index 8fc9dd2495..9591f2e126 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp @@ -11,11 +11,11 @@ tags: backend|arm64 #include "Interface/Core/JIT/Arm64/JITClass.h" +#include #include #include #include #include -#include namespace FEXCore::CPU { #define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const* IROp, IR::NodeID Node) diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index e3c7e7443b..46c88e3500 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -20,6 +20,7 @@ namespace FEXCore { class CodeLoader; struct HostFeatures; class ForkableSharedMutex; +class ThunkHandler; } // namespace FEXCore namespace FEXCore::Core { @@ -175,6 +176,7 @@ class Context { #endif FEX_DEFAULT_VISIBILITY virtual void SetSignalDelegator(FEXCore::SignalDelegator* SignalDelegation) = 0; FEX_DEFAULT_VISIBILITY virtual void SetSyscallHandler(FEXCore::HLE::SyscallHandler* Handler) = 0; + FEX_DEFAULT_VISIBILITY virtual void SetThunkHandler(FEXCore::ThunkHandler* Handler) = 0; FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) = 0; FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::XCRResults RunXCRFunction(uint32_t Function) = 0; diff --git a/FEXCore/Source/Interface/HLE/Thunks/Thunks.h b/FEXCore/include/FEXCore/Core/Thunks.h similarity index 80% rename from FEXCore/Source/Interface/HLE/Thunks/Thunks.h rename to FEXCore/include/FEXCore/Core/Thunks.h index de31da425f..381c5bbf2e 100644 --- a/FEXCore/Source/Interface/HLE/Thunks/Thunks.h +++ b/FEXCore/include/FEXCore/Core/Thunks.h @@ -29,11 +29,8 @@ typedef void ThunkedFunction(void* ArgsRv); class ThunkHandler { public: virtual ThunkedFunction* LookupThunk(const IR::SHA256Sum& sha256) = 0; - virtual void RegisterTLSState(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState* Thread) = 0; virtual ~ThunkHandler() {} - static fextl::unique_ptr Create(); - virtual void AppendThunkDefinitions(std::span Definitions) = 0; }; }; // namespace FEXCore diff --git a/Source/Tools/FEXLoader/FEXLoader.cpp b/Source/Tools/FEXLoader/FEXLoader.cpp index 2c590aa1c4..2b435058c9 100644 --- a/Source/Tools/FEXLoader/FEXLoader.cpp +++ b/Source/Tools/FEXLoader/FEXLoader.cpp @@ -21,6 +21,7 @@ desc: Glues the ELF loader, FEXCore and LinuxSyscalls to launch an elf under fex #include "LinuxSyscalls/x64/Syscalls.h" #include "LinuxSyscalls/SignalDelegator.h" #include "Linux/Utils/ELFContainer.h" +#include "Thunks.h" #include #include @@ -514,6 +515,8 @@ int main(int argc, char** argv, char** const envp) { auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get()) : FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), std::move(Allocator)); + auto ThunkHandler = FEX::HLE::CreateThunkHandler(); + // Load VDSO in to memory prior to mapping our ELFs. auto VDSOMapping = FEX::VDSO::LoadVDSOThunks(Loader.Is64BitMode(), SyscallHandler.get()); @@ -545,6 +548,7 @@ int main(int argc, char** argv, char** const envp) { CTX->SetSignalDelegator(SignalDelegation.get()); CTX->SetSyscallHandler(SyscallHandler.get()); + CTX->SetThunkHandler(ThunkHandler.get()); FEX_CONFIG_OPT(GdbServer, GDBSERVER); fextl::unique_ptr DebugServer; @@ -559,6 +563,7 @@ int main(int argc, char** argv, char** const envp) { auto ParentThread = SyscallHandler->TM.CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer()); SyscallHandler->TM.TrackThread(ParentThread); SignalDelegation->RegisterTLSState(ParentThread); + ThunkHandler->RegisterTLSState(CTX.get(), ParentThread); // Pass in our VDSO thunks CTX->AppendThunkDefinitions(FEX::VDSO::GetVDSOThunkDefinitions()); diff --git a/Source/Tools/LinuxEmulation/CMakeLists.txt b/Source/Tools/LinuxEmulation/CMakeLists.txt index 99005c166f..bdcade84cb 100644 --- a/Source/Tools/LinuxEmulation/CMakeLists.txt +++ b/Source/Tools/LinuxEmulation/CMakeLists.txt @@ -2,6 +2,7 @@ add_compile_options(-fno-operator-names) set (SRCS VDSO_Emulation.cpp + Thunks.cpp LinuxSyscalls/GdbServer.cpp LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp LinuxSyscalls/FaultSafeUserMemAccess.cpp diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index d864f37cd1..d855a43133 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -14,6 +14,7 @@ desc: Glue logic, STRACE magic #include "LinuxSyscalls/Seccomp/SeccompEmulator.h" #include +#include #include #include #include diff --git a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/Source/Tools/LinuxEmulation/Thunks.cpp similarity index 97% rename from FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp rename to Source/Tools/LinuxEmulation/Thunks.cpp index 058202d7e0..72a743dcb0 100644 --- a/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/Source/Tools/LinuxEmulation/Thunks.cpp @@ -6,23 +6,23 @@ tags: glue|thunks $end_info$ */ +#include "Thunks.h" +#include "LinuxSyscalls/ThreadManager.h" + #include #include #include +#include #include #include #include #include #include #include -#include "Thunks.h" #include -#ifndef _WIN32 #include -#endif -#include #include #include #include @@ -39,7 +39,6 @@ FEX_DEFAULT_VISIBILITY JEMALLOC_NOTHROW extern int glibc_je_is_known_allocation( } #endif -#ifndef _WIN32 static __attribute__((aligned(16), naked, section("HostToGuestTrampolineTemplate"))) void HostToGuestTrampolineTemplate() { #if defined(_M_X86_64) asm("lea 0f(%rip), %r11 \n" @@ -67,10 +66,8 @@ static __attribute__((aligned(16), naked, section("HostToGuestTrampolineTemplate extern char __start_HostToGuestTrampolineTemplate[]; extern char __stop_HostToGuestTrampolineTemplate[]; -#endif namespace FEXCore { -#ifndef _WIN32 struct LoadlibArgs { const char* Name; }; @@ -131,7 +128,7 @@ struct TruncatingSHA256Hash { HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker); -struct ThunkHandler_impl final : public ThunkHandler { +struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { std::shared_mutex ThunksMutex; fextl::unordered_map Thunks = { @@ -326,8 +323,8 @@ struct ThunkHandler_impl final : public ThunkHandler { } } - void RegisterTLSState(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState* _Thread) override { - ThreadData.Thread = _Thread; + void RegisterTLSState(FEXCore::Context::Context* CTX, FEX::HLE::ThreadStateObject* ThreadObject) override { + ThreadData.Thread = ThreadObject->Thread; ThreadData.ThunkHandler = this; ThreadData.CTX = CTX; } @@ -343,10 +340,6 @@ struct ThunkHandler_impl final : public ThunkHandler { FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32); }; -fextl::unique_ptr ThunkHandler::Create() { - return fextl::make_unique(); -} - /** * Generates a host-callable trampoline to call guest functions via the host ABI. * @@ -459,10 +452,10 @@ FEX_DEFAULT_VISIBILITY void MoveGuestStack(uintptr_t NewAddress) { ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = NewAddress; } -#else -fextl::unique_ptr ThunkHandler::Create() { - ERROR_AND_DIE_FMT("Unsupported"); -} -#endif - } // namespace FEXCore + +namespace FEX::HLE { +fextl::unique_ptr CreateThunkHandler() { + return fextl::make_unique(); +} +} // namespace FEX::HLE diff --git a/Source/Tools/LinuxEmulation/Thunks.h b/Source/Tools/LinuxEmulation/Thunks.h new file mode 100644 index 0000000000..77c3fc9cfa --- /dev/null +++ b/Source/Tools/LinuxEmulation/Thunks.h @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +#include +#include + +namespace FEX::HLE { +struct ThreadStateObject; + +class ThunkHandler : public FEXCore::ThunkHandler { +public: + virtual void RegisterTLSState(FEXCore::Context::Context* CTX, FEX::HLE::ThreadStateObject* ThreadObject) = 0; +}; +fextl::unique_ptr CreateThunkHandler(); +} // namespace FEX::HLE From e409a0afecd3177a489cdb5d84027aa1fda710d5 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 19 Sep 2024 23:44:20 -0700 Subject: [PATCH 5/9] Thunks: Shift namespace name Moves from FEXCore to FEX::HLE. Also moves the ThunkFunctions that get exposed to a namespace to make it more obvious that these are thunkhandlers rather than just static functions. --- Source/Tools/LinuxEmulation/Thunks.cpp | 188 +++++++++++++------------ ThunkLibs/include/common/Host.h | 22 +-- ThunkLibs/libwayland-client/Host.cpp | 12 +- 3 files changed, 115 insertions(+), 107 deletions(-) diff --git a/Source/Tools/LinuxEmulation/Thunks.cpp b/Source/Tools/LinuxEmulation/Thunks.cpp index 72a743dcb0..d74a7dcac5 100644 --- a/Source/Tools/LinuxEmulation/Thunks.cpp +++ b/Source/Tools/LinuxEmulation/Thunks.cpp @@ -67,7 +67,8 @@ static __attribute__((aligned(16), naked, section("HostToGuestTrampolineTemplate extern char __start_HostToGuestTrampolineTemplate[]; extern char __stop_HostToGuestTrampolineTemplate[]; -namespace FEXCore { +namespace FEX::HLE { + struct LoadlibArgs { const char* Name; }; @@ -84,7 +85,7 @@ static thread_local TEMP_TLS_DATA ThreadData {}; struct ExportEntry { uint8_t* sha256; - ThunkedFunction* Fn; + FEXCore::ThunkedFunction* Fn; }; struct TrampolineInstanceInfo { @@ -128,30 +129,38 @@ struct TruncatingSHA256Hash { HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker); +namespace ThunkFunctions { + void LoadLib(void* ArgsV); + void IsLibLoaded(void* ArgsRV); + void IsHostHeapAllocation(void* ArgsRV); + void LinkAddressToGuestFunction(void* argsv); + void AllocateHostTrampolineForGuestFunction(void* ArgsRV); +} // namespace ThunkFunctions + struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { std::shared_mutex ThunksMutex; - fextl::unordered_map Thunks = { + fextl::unordered_map Thunks = { {// sha256(fex:loadlib) {0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80}, - &LoadLib}, + &ThunkFunctions::LoadLib}, {// sha256(fex:is_lib_loaded) {0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff}, - &IsLibLoaded}, + &ThunkFunctions::IsLibLoaded}, {// sha256(fex:is_host_heap_allocation) {0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e}, - &IsHostHeapAllocation}, + &ThunkFunctions::IsHostHeapAllocation}, {// sha256(fex:link_address_to_function) {0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77}, - &LinkAddressToGuestFunction}, + &ThunkFunctions::LinkAddressToGuestFunction}, {// sha256(fex:allocate_host_trampoline_for_guest_function) {0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a}, - &AllocateHostTrampolineForGuestFunction}, + &ThunkFunctions::AllocateHostTrampolineForGuestFunction}, }; // Can't be a string_view. We need to keep a copy of the library name in-case string_view pointer goes away. @@ -186,68 +195,38 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { ThreadData.CTX->HandleCallback(ThreadData.Thread, (uintptr_t)callback); } - /** - * Instructs the Core to redirect calls to functions at the given - * address to another function. The original callee address is passed - * to the target function through an implicit argument stored in r11. - * - * For 32-bit the implicit argument is stored in the lower 32-bits of mm0. - * - * The primary use case of this is ensuring that host function pointers - * returned from thunked APIs can safely be called by the guest. - */ - static void LinkAddressToGuestFunction(void* argsv) { - struct args_t { - uintptr_t original_callee; - uintptr_t target_addr; // Guest function to call when branching to original_callee - }; + FEXCore::ThunkedFunction* LookupThunk(const FEXCore::IR::SHA256Sum& sha256) override { - auto args = reinterpret_cast(argsv); - ThreadData.CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); - } + std::shared_lock lk(ThunksMutex); - /** - * Guest-side helper to initiate creation of a host trampoline for - * calling guest functions. This must be followed by a host-side call - * to FinalizeHostTrampolineForGuestFunction to make the trampoline - * usable. - * - * This two-step initialization is equivalent to a host-side call to - * MakeHostTrampolineForGuestFunction. The split is needed if the - * host doesn't have all information needed to create the trampoline - * on its own. - */ - static void AllocateHostTrampolineForGuestFunction(void* ArgsRV) { - struct ArgsRV_t { - uintptr_t GuestUnpacker; - uintptr_t GuestTarget; - uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo - }* args = reinterpret_cast(ArgsRV); + auto it = Thunks.find(sha256); - args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker); + if (it != Thunks.end()) { + return it->second; + } else { + return nullptr; + } } - /** - * Checks if the given pointer is allocated on the host heap. - * - * This is useful for thunking APIs that need to work with both guest - * and host heap pointers. - */ - static void IsHostHeapAllocation(void* ArgsRV) { -#ifdef ENABLE_JEMALLOC_GLIBC - struct ArgsRV_t { - void* ptr; - bool rv; - }* args = reinterpret_cast(ArgsRV); + void RegisterTLSState(FEXCore::Context::Context* CTX, FEX::HLE::ThreadStateObject* ThreadObject) override { + ThreadData.Thread = ThreadObject->Thread; + ThreadData.ThunkHandler = this; + ThreadData.CTX = CTX; + } - args->rv = glibc_je_is_known_allocation(args->ptr); -#else - // Thunks usage without jemalloc isn't supported - ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information"); -#endif + void AppendThunkDefinitions(std::span Definitions) override { + for (auto& Definition : Definitions) { + Thunks.emplace(Definition.Sum, Definition.ThunkFunction); + } } - static void LoadLib(void* ArgsV) { + FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); + FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS); + FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32); +}; + +namespace ThunkFunctions { + void LoadLib(void* ArgsV) { auto Args = reinterpret_cast(ArgsV); std::string_view Name = Args->Name; @@ -289,14 +268,14 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { int i; for (i = 0; Exports[i].sha256; i++) { - ThreadData.ThunkHandler->Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; + ThreadData.ThunkHandler->Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; } LogMan::Msg::DFmt("Loaded {} syms", i); } } - static void IsLibLoaded(void* ArgsRV) { + void IsLibLoaded(void* ArgsRV) { struct ArgsRV_t { const char* Name; bool rv; @@ -310,35 +289,67 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { } } - ThunkedFunction* LookupThunk(const IR::SHA256Sum& sha256) override { + /** + * Checks if the given pointer is allocated on the host heap. + * + * This is useful for thunking APIs that need to work with both guest + * and host heap pointers. + */ + void IsHostHeapAllocation(void* ArgsRV) { +#ifdef ENABLE_JEMALLOC_GLIBC + struct ArgsRV_t { + void* ptr; + bool rv; + }* args = reinterpret_cast(ArgsRV); - std::shared_lock lk(ThunksMutex); + args->rv = glibc_je_is_known_allocation(args->ptr); +#else + // Thunks usage without jemalloc isn't supported + ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information"); +#endif + } - auto it = Thunks.find(sha256); + /** + * Instructs the Core to redirect calls to functions at the given + * address to another function. The original callee address is passed + * to the target function through an implicit argument stored in r11. + * + * For 32-bit the implicit argument is stored in the lower 32-bits of mm0. + * + * The primary use case of this is ensuring that host function pointers + * returned from thunked APIs can safely be called by the guest. + */ + void LinkAddressToGuestFunction(void* argsv) { + struct args_t { + uintptr_t original_callee; + uintptr_t target_addr; // Guest function to call when branching to original_callee + }; - if (it != Thunks.end()) { - return it->second; - } else { - return nullptr; - } + auto args = reinterpret_cast(argsv); + ThreadData.CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); } - void RegisterTLSState(FEXCore::Context::Context* CTX, FEX::HLE::ThreadStateObject* ThreadObject) override { - ThreadData.Thread = ThreadObject->Thread; - ThreadData.ThunkHandler = this; - ThreadData.CTX = CTX; - } + /** + * Guest-side helper to initiate creation of a host trampoline for + * calling guest functions. This must be followed by a host-side call + * to FinalizeHostTrampolineForGuestFunction to make the trampoline + * usable. + * + * This two-step initialization is equivalent to a host-side call to + * MakeHostTrampolineForGuestFunction. The split is needed if the + * host doesn't have all information needed to create the trampoline + * on its own. + */ + void AllocateHostTrampolineForGuestFunction(void* ArgsRV) { + struct ArgsRV_t { + uintptr_t GuestUnpacker; + uintptr_t GuestTarget; + uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo + }* args = reinterpret_cast(ArgsRV); - void AppendThunkDefinitions(std::span Definitions) override { - for (auto& Definition : Definitions) { - Thunks.emplace(Definition.Sum, Definition.ThunkFunction); - } + args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker); } - - FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); - FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS); - FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32); -}; +} // namespace ThunkFunctions /** * Generates a host-callable trampoline to call guest functions via the host ABI. @@ -452,10 +463,7 @@ FEX_DEFAULT_VISIBILITY void MoveGuestStack(uintptr_t NewAddress) { ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = NewAddress; } -} // namespace FEXCore - -namespace FEX::HLE { fextl::unique_ptr CreateThunkHandler() { - return fextl::make_unique(); + return fextl::make_unique(); } } // namespace FEX::HLE diff --git a/ThunkLibs/include/common/Host.h b/ThunkLibs/include/common/Host.h index 4bd071cd18..2d568490d7 100644 --- a/ThunkLibs/include/common/Host.h +++ b/ThunkLibs/include/common/Host.h @@ -15,12 +15,12 @@ category: thunklibs ~ These are generated + glue logic 1:1 thunks unless noted o #include "PackedArguments.h" -// Import FEXCore functions for use in host thunk libraries. +// Import FEX::HLE functions for use in host thunk libraries. // // Note these are statically linked into the FEX executable. The linker hence // doesn't know about them when linking thunk libraries. This issue is avoided // by declaring the functions as weak symbols. -namespace FEXCore { +namespace FEX::HLE { struct HostToGuestTrampolinePtr; __attribute__((weak)) HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker); @@ -30,7 +30,7 @@ __attribute__((weak)) HostToGuestTrampolinePtr* FinalizeHostTrampolineForGuestFu __attribute__((weak)) void* GetGuestStack(); __attribute__((weak)) void MoveGuestStack(uintptr_t NewAddress); -} // namespace FEXCore +} // namespace FEX::HLE template struct function_traits; @@ -504,19 +504,19 @@ auto Projection(guest_layout& data) { * restored. */ class GuestStackBumpAllocator final { - uintptr_t Top = reinterpret_cast(FEXCore::GetGuestStack()); + uintptr_t Top = reinterpret_cast(FEX::HLE::GetGuestStack()); uintptr_t Next = Top; public: ~GuestStackBumpAllocator() { - FEXCore::MoveGuestStack(Top); + FEX::HLE::MoveGuestStack(Top); } template T* New(Args&&... args) { Next -= sizeof(T); Next &= ~uintptr_t {alignof(T) - 1}; - FEXCore::MoveGuestStack(Next); + FEX::HLE::MoveGuestStack(Next); return new (reinterpret_cast(Next)) T {std::forward(args)...}; } }; @@ -625,19 +625,19 @@ struct GuestWrapperForHostFunction { template void MakeHostTrampolineForGuestFunctionAt(uintptr_t GuestTarget, uintptr_t GuestUnpacker, FuncType** Func) { - *Func = (FuncType*)FEXCore::MakeHostTrampolineForGuestFunction((void*)&CallbackUnpack::CallGuestPtr, GuestTarget, GuestUnpacker); + *Func = (FuncType*)FEX::HLE::MakeHostTrampolineForGuestFunction((void*)&CallbackUnpack::CallGuestPtr, GuestTarget, GuestUnpacker); } template void FinalizeHostTrampolineForGuestFunction(F* PreallocatedTrampolineForGuestFunction) { - FEXCore::FinalizeHostTrampolineForGuestFunction((FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction, - (void*)&CallbackUnpack::CallGuestPtr); + FEX::HLE::FinalizeHostTrampolineForGuestFunction((FEX::HLE::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction, + (void*)&CallbackUnpack::CallGuestPtr); } template void FinalizeHostTrampolineForGuestFunction(guest_layout PreallocatedTrampolineForGuestFunction) { - FEXCore::FinalizeHostTrampolineForGuestFunction((FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction.data, - (void*)&CallbackUnpack::CallGuestPtr); + FEX::HLE::FinalizeHostTrampolineForGuestFunction((FEX::HLE::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction.data, + (void*)&CallbackUnpack::CallGuestPtr); } // In the case of the thunk host_loader being the default, FEX need to use dlsym with RTLD_DEFAULT. diff --git a/ThunkLibs/libwayland-client/Host.cpp b/ThunkLibs/libwayland-client/Host.cpp index aa3ba829c9..b07d9f6398 100644 --- a/ThunkLibs/libwayland-client/Host.cpp +++ b/ThunkLibs/libwayland-client/Host.cpp @@ -335,8 +335,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest WaylandFinalizeHostTrampolineForGuestListener<>(callback); } else if (signature == "a") { // E.g. xdg_toplevel::wm_capabilities - FEXCore::FinalizeHostTrampolineForGuestFunction((FEXCore::HostToGuestTrampolinePtr*)callback, - (void*)CallGuestPtrWithWaylandArray); + FEX::HLE::FinalizeHostTrampolineForGuestFunction((FEX::HLE::HostToGuestTrampolinePtr*)callback, + (void*)CallGuestPtrWithWaylandArray); } else if (signature == "hu") { // E.g. zwp_linux_dmabuf_feedback_v1::format_table WaylandFinalizeHostTrampolineForGuestListener<'h', 'u'>(callback); @@ -354,8 +354,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest WaylandFinalizeHostTrampolineForGuestListener<'i', 'i'>(callback); } else if (signature == "iia") { // E.g. xdg_toplevel::configure - FEXCore::FinalizeHostTrampolineForGuestFunction((FEXCore::HostToGuestTrampolinePtr*)callback, - (void*)CallGuestPtrWithWaylandArray); + FEX::HLE::FinalizeHostTrampolineForGuestFunction((FEX::HLE::HostToGuestTrampolinePtr*)callback, + (void*)CallGuestPtrWithWaylandArray); } else if (signature == "iiiiissi") { // E.g. wl_output_listener::geometry WaylandFinalizeHostTrampolineForGuestListener<'i', 'i', 'i', 'i', 'i', 's', 's', 'i'>(callback); @@ -388,8 +388,8 @@ fexfn_impl_libwayland_client_wl_proxy_add_listener(struct wl_proxy* proxy, guest WaylandFinalizeHostTrampolineForGuestListener<'u', 'o'>(callback); } else if (signature == "uoa") { // E.g. wl_keyboard_listener::enter - FEXCore::FinalizeHostTrampolineForGuestFunction((FEXCore::HostToGuestTrampolinePtr*)callback, - (void*)CallGuestPtrWithWaylandArray); + FEX::HLE::FinalizeHostTrampolineForGuestFunction((FEX::HLE::HostToGuestTrampolinePtr*)callback, + (void*)CallGuestPtrWithWaylandArray); } else if (signature == "uoff") { // E.g. wl_pointer_listener::enter WaylandFinalizeHostTrampolineForGuestListener<'u', 'o', 'f', 'f'>(callback); From 0b8b5108b9df47a9b9381abec516b413a3752ccc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 19 Sep 2024 23:49:03 -0700 Subject: [PATCH 6/9] Thunks: Moves AppendThunkDefinitions function to the frontend Backend doesn't need access to this at all --- FEXCore/Source/Interface/Context/Context.h | 2 -- FEXCore/Source/Interface/Core/Core.cpp | 6 ------ FEXCore/include/FEXCore/Core/Context.h | 8 -------- FEXCore/include/FEXCore/Core/Thunks.h | 2 -- Source/Tools/FEXLoader/FEXLoader.cpp | 2 +- Source/Tools/LinuxEmulation/Thunks.h | 7 +++++++ 6 files changed, 8 insertions(+), 19 deletions(-) diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index a56079e1cb..429a33e8d7 100644 --- a/FEXCore/Source/Interface/Context/Context.h +++ b/FEXCore/Source/Interface/Context/Context.h @@ -192,8 +192,6 @@ class ContextImpl final : public FEXCore::Context::Context { void AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t GuestThunkEntrypoint) override; - void AppendThunkDefinitions(std::span Definitions) override; - public: friend class FEXCore::HLE::SyscallHandler; #ifdef JIT_ARM64 diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 13d2747af3..9774cddfc7 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -1055,12 +1055,6 @@ void ContextImpl::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry* Entry) { IRCaptureCache.UnloadAOTIRCacheEntry(Entry); } -void ContextImpl::AppendThunkDefinitions(std::span Definitions) { - if (ThunkHandler) { - ThunkHandler->AppendThunkDefinitions(Definitions); - } -} - void ContextImpl::ConfigureAOTGen(FEXCore::Core::InternalThreadState* Thread, fextl::set* ExternalBranches, uint64_t SectionMaxAddress) { Thread->FrontendDecoder->SetExternalBranches(ExternalBranches); Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress); diff --git a/FEXCore/include/FEXCore/Core/Context.h b/FEXCore/include/FEXCore/Core/Context.h index 46c88e3500..d05f9f72c9 100644 --- a/FEXCore/include/FEXCore/Core/Context.h +++ b/FEXCore/include/FEXCore/Core/Context.h @@ -213,14 +213,6 @@ class Context { */ FEX_DEFAULT_VISIBILITY virtual bool IsAddressInCodeBuffer(FEXCore::Core::InternalThreadState* Thread, uintptr_t Address) const = 0; - /** - * @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend. - * - * @param CTX A valid non-null context instance. - * @param Definitions A vector of thunk definitions that the frontend controls - */ - FEX_DEFAULT_VISIBILITY virtual void AppendThunkDefinitions(std::span Definitions) = 0; - /** * @brief Informs the context if hardware TSO is supported. * Once hardware TSO is enabled, then TSO emulation through atomics is disabled and relies on the hardware. diff --git a/FEXCore/include/FEXCore/Core/Thunks.h b/FEXCore/include/FEXCore/Core/Thunks.h index 381c5bbf2e..df97e64271 100644 --- a/FEXCore/include/FEXCore/Core/Thunks.h +++ b/FEXCore/include/FEXCore/Core/Thunks.h @@ -30,7 +30,5 @@ class ThunkHandler { public: virtual ThunkedFunction* LookupThunk(const IR::SHA256Sum& sha256) = 0; virtual ~ThunkHandler() {} - - virtual void AppendThunkDefinitions(std::span Definitions) = 0; }; }; // namespace FEXCore diff --git a/Source/Tools/FEXLoader/FEXLoader.cpp b/Source/Tools/FEXLoader/FEXLoader.cpp index 2b435058c9..ff47d01b89 100644 --- a/Source/Tools/FEXLoader/FEXLoader.cpp +++ b/Source/Tools/FEXLoader/FEXLoader.cpp @@ -566,7 +566,7 @@ int main(int argc, char** argv, char** const envp) { ThunkHandler->RegisterTLSState(CTX.get(), ParentThread); // Pass in our VDSO thunks - CTX->AppendThunkDefinitions(FEX::VDSO::GetVDSOThunkDefinitions()); + ThunkHandler->AppendThunkDefinitions(FEX::VDSO::GetVDSOThunkDefinitions()); SignalDelegation->SetVDSOSigReturn(); SyscallHandler->DeserializeSeccompFD(ParentThread, FEXSeccompFD); diff --git a/Source/Tools/LinuxEmulation/Thunks.h b/Source/Tools/LinuxEmulation/Thunks.h index 77c3fc9cfa..5a3a9b2801 100644 --- a/Source/Tools/LinuxEmulation/Thunks.h +++ b/Source/Tools/LinuxEmulation/Thunks.h @@ -8,6 +8,13 @@ struct ThreadStateObject; class ThunkHandler : public FEXCore::ThunkHandler { public: virtual void RegisterTLSState(FEXCore::Context::Context* CTX, FEX::HLE::ThreadStateObject* ThreadObject) = 0; + /** + * @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend. + * + * @param CTX A valid non-null context instance. + * @param Definitions A vector of thunk definitions that the frontend controls + */ + virtual void AppendThunkDefinitions(std::span Definitions) = 0; }; fextl::unique_ptr CreateThunkHandler(); } // namespace FEX::HLE From 611aa0a5b93b686fb58ec6533c727ef452b17ed1 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 20 Sep 2024 00:02:14 -0700 Subject: [PATCH 7/9] Thunks: Wire up TLS handling in the frontend Because it was moved from the backend to the frontend, re-wire up the TLS handling which requires going through our syscall handler. --- Source/Tools/FEXLoader/FEXLoader.cpp | 8 ++++---- .../LinuxEmulation/LinuxSyscalls/Syscalls.cpp | 15 +++++++++++++-- .../Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h | 8 +++++++- .../LinuxSyscalls/Syscalls/Thread.cpp | 8 ++++---- .../LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp | 10 +++++----- .../LinuxEmulation/LinuxSyscalls/x32/Syscalls.h | 10 ++++++---- .../LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp | 9 +++++---- .../LinuxEmulation/LinuxSyscalls/x64/Syscalls.h | 6 ++++-- .../Tools/TestHarnessRunner/TestHarnessRunner.cpp | 4 ++-- 9 files changed, 50 insertions(+), 28 deletions(-) diff --git a/Source/Tools/FEXLoader/FEXLoader.cpp b/Source/Tools/FEXLoader/FEXLoader.cpp index ff47d01b89..bd61bbb8db 100644 --- a/Source/Tools/FEXLoader/FEXLoader.cpp +++ b/Source/Tools/FEXLoader/FEXLoader.cpp @@ -511,12 +511,12 @@ int main(int argc, char** argv, char** const envp) { FEX::TSO::SetupTSOEmulation(CTX.get()); auto SignalDelegation = FEX::HLE::CreateSignalDelegator(CTX.get(), Program.ProgramName, SupportsAVX); - - auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get()) : - FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), std::move(Allocator)); - auto ThunkHandler = FEX::HLE::CreateThunkHandler(); + auto SyscallHandler = Loader.Is64BitMode() ? + FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get(), ThunkHandler.get()) : + FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), ThunkHandler.get(), std::move(Allocator)); + // Load VDSO in to memory prior to mapping our ELFs. auto VDSOMapping = FEX::VDSO::LoadVDSOThunks(Loader.Is64BitMode(), SyscallHandler.get()); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp index 92a80ee32d..b44100c80c 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp @@ -21,6 +21,7 @@ desc: Glue logic, brk allocations #include "LinuxSyscalls/x64/Syscalls.h" #include "LinuxSyscalls/x32/Types.h" #include "LinuxSyscalls/x64/Types.h" +#include "Thunks.h" #include #include @@ -740,12 +741,13 @@ void SyscallHandler::DefaultProgramBreak(uint64_t Base, uint64_t Size) { DataSpaceStartingSize = Size; } -SyscallHandler::SyscallHandler(FEXCore::Context::Context* _CTX, FEX::HLE::SignalDelegator* _SignalDelegation) +SyscallHandler::SyscallHandler(FEXCore::Context::Context* _CTX, FEX::HLE::SignalDelegator* _SignalDelegation, FEX::HLE::ThunkHandler* ThunkHandler) : TM {_CTX, _SignalDelegation} , SeccompEmulator {this, _SignalDelegation} , FM {_CTX} , CTX {_CTX} - , SignalDelegation {_SignalDelegation} { + , SignalDelegation {_SignalDelegation} + , ThunkHandler {ThunkHandler} { FEX::HLE::_SyscallHandler = this; HostKernelVersion = CalculateHostKernelVersion(); GuestKernelVersion = CalculateGuestKernelVersion(); @@ -872,6 +874,15 @@ void SyscallHandler::UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThr TM.UnlockAfterFork(LiveThread, Child); } +void SyscallHandler::RegisterTLSState(FEX::HLE::ThreadStateObject* Thread) { + SignalDelegation->RegisterTLSState(Thread); + ThunkHandler->RegisterTLSState(CTX, Thread); +} + +void SyscallHandler::UninstallTLSState(FEX::HLE::ThreadStateObject* Thread) { + SignalDelegation->UninstallTLSState(Thread); +} + static bool isHEX(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index d855a43133..f210bba598 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -67,6 +67,8 @@ namespace FEX::HLE { class SyscallHandler; class SignalDelegator; +class ThunkHandler; + void RegisterEpoll(FEX::HLE::SyscallHandler* Handler); void RegisterFD(FEX::HLE::SyscallHandler* Handler); void RegisterFS(FEX::HLE::SyscallHandler* Handler); @@ -255,6 +257,9 @@ class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::Source void LockBeforeFork(FEXCore::Core::InternalThreadState* Thread); void UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThread, bool Child); + void RegisterTLSState(FEX::HLE::ThreadStateObject* Thread); + void UninstallTLSState(FEX::HLE::ThreadStateObject* Thread); + SourcecodeResolver* GetSourcecodeResolver() override { return this; } @@ -273,7 +278,7 @@ class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::Source constexpr static uint64_t TASK_MAX_64BIT = (1ULL << 48); protected: - SyscallHandler(FEXCore::Context::Context* _CTX, FEX::HLE::SignalDelegator* _SignalDelegation); + SyscallHandler(FEXCore::Context::Context* _CTX, FEX::HLE::SignalDelegator* _SignalDelegation, FEX::HLE::ThunkHandler* ThunkHandler); fextl::vector Definitions {std::max(FEX::HLE::x64::SYSCALL_x64_MAX, FEX::HLE::x32::SYSCALL_x86_MAX), { @@ -297,6 +302,7 @@ class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::Source private: FEX::HLE::SignalDelegator* SignalDelegation; + FEX::HLE::ThunkHandler* ThunkHandler; std::mutex FutexMutex; std::mutex SyscallMutex; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp index 0b8fe142b5..0a1178019b 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls/Thread.cpp @@ -57,9 +57,9 @@ static void* ThreadHandler(void* Data) { Thread->ThreadInfo.PID = ::getpid(); Thread->ThreadInfo.TID = FHU::Syscalls::gettid(); - FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterTLSState(Thread); + FEX::HLE::_SyscallHandler->RegisterTLSState(Thread); CTX->ExecutionThread(Thread->Thread); - FEX::HLE::_SyscallHandler->GetSignalDelegator()->UninstallTLSState(Thread); + FEX::HLE::_SyscallHandler->UninstallTLSState(Thread); FEX::HLE::_SyscallHandler->TM.DestroyThread(Thread); return nullptr; } @@ -216,13 +216,13 @@ uint64_t HandleNewClone(FEX::HLE::ThreadStateObject* Thread, FEXCore::Context::C FEX::HLE::_SyscallHandler->TM.TrackThread(Thread); } - FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterTLSState(Thread); + FEX::HLE::_SyscallHandler->RegisterTLSState(Thread); // Start exuting the thread directly // Our host clone starts in a new stack space, so it can't return back to the JIT space CTX->ExecutionThread(Thread->Thread); - FEX::HLE::_SyscallHandler->GetSignalDelegator()->UninstallTLSState(Thread); + FEX::HLE::_SyscallHandler->UninstallTLSState(Thread); // The rest of the context remains as is and the thread will continue executing return Thread->Thread->StatusCode; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp index a25382a18d..18275ea7f0 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.cpp @@ -43,8 +43,8 @@ void RegisterTimer(FEX::HLE::SyscallHandler* Handler); void RegisterPassthrough(FEX::HLE::SyscallHandler* Handler); x32SyscallHandler::x32SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, - fextl::unique_ptr Allocator) - : SyscallHandler {ctx, _SignalDelegation} + FEX::HLE::ThunkHandler* ThunkHandler, fextl::unique_ptr Allocator) + : SyscallHandler {ctx, _SignalDelegation, ThunkHandler} , AllocHandler {std::move(Allocator)} { OSABI = FEXCore::HLE::SyscallOSABI::OS_LINUX32; RegisterSyscallHandlers(); @@ -91,9 +91,9 @@ void x32SyscallHandler::RegisterSyscallHandlers() { #endif } -fextl::unique_ptr -CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, fextl::unique_ptr Allocator) { - return fextl::make_unique(ctx, _SignalDelegation, std::move(Allocator)); +fextl::unique_ptr CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, + FEX::HLE::ThunkHandler* ThunkHandler, fextl::unique_ptr Allocator) { + return fextl::make_unique(ctx, _SignalDelegation, ThunkHandler, std::move(Allocator)); } } // namespace FEX::HLE::x32 diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.h index b41b322073..0930f602af 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Syscalls.h @@ -28,13 +28,15 @@ namespace Core { namespace FEX::HLE { class SignalDelegator; -} +class ThunkHandler; +} // namespace FEX::HLE namespace FEX::HLE::x32 { class x32SyscallHandler final : public FEX::HLE::SyscallHandler { public: - x32SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, fextl::unique_ptr Allocator); + x32SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, FEX::HLE::ThunkHandler* ThunkHandler, + fextl::unique_ptr Allocator); FEX::HLE::MemAllocator* GetAllocator() { return AllocHandler.get(); @@ -65,8 +67,8 @@ class x32SyscallHandler final : public FEX::HLE::SyscallHandler { fextl::unique_ptr AllocHandler {}; }; -fextl::unique_ptr -CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, fextl::unique_ptr Allocator); +fextl::unique_ptr CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, + FEX::HLE::ThunkHandler* ThunkHandler, fextl::unique_ptr Allocator); ////// // REGISTER_SYSCALL_IMPL implementation // Given a syscall name + a lambda, and it will generate an strace string, extract number of arguments diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp index e96460d8ca..e0495ed83d 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.cpp @@ -23,8 +23,8 @@ void RegisterTime(FEX::HLE::SyscallHandler* Handler); void RegisterNotImplemented(FEX::HLE::SyscallHandler* Handler); void RegisterPassthrough(FEX::HLE::SyscallHandler* Handler); -x64SyscallHandler::x64SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation) - : SyscallHandler {ctx, _SignalDelegation} { +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(); @@ -80,7 +80,8 @@ void x64SyscallHandler::RegisterSyscallHandlers() { #endif } -fextl::unique_ptr CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation) { - return fextl::make_unique(ctx, _SignalDelegation); +fextl::unique_ptr +CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, FEX::HLE::ThunkHandler* ThunkHandler) { + return fextl::make_unique(ctx, _SignalDelegation, ThunkHandler); } } // namespace FEX::HLE::x64 diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.h index 3f88828059..86b5d47186 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/x64/Syscalls.h @@ -23,6 +23,7 @@ tags: LinuxSyscalls|syscalls-x86-64 namespace FEX::HLE { class SignalDelegator; class SyscallHandler; +class ThunkHandler; } // namespace FEX::HLE namespace FEXCore::Core { @@ -32,7 +33,7 @@ struct InternalThreadState; namespace FEX::HLE::x64 { class x64SyscallHandler final : public FEX::HLE::SyscallHandler { public: - x64SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation); + x64SyscallHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, FEX::HLE::ThunkHandler* ThunkHandler); void* GuestMmap(FEXCore::Core::InternalThreadState* Thread, void* addr, size_t length, int prot, int flags, int fd, off_t offset) override; int GuestMunmap(FEXCore::Core::InternalThreadState* Thread, void* addr, uint64_t length) override; @@ -60,7 +61,8 @@ class x64SyscallHandler final : public FEX::HLE::SyscallHandler { void RegisterSyscallHandlers(); }; -fextl::unique_ptr CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation); +fextl::unique_ptr +CreateHandler(FEXCore::Context::Context* ctx, FEX::HLE::SignalDelegator* _SignalDelegation, FEX::HLE::ThunkHandler* ThunkHandler); ////// // REGISTER_SYSCALL_IMPL implementation diff --git a/Source/Tools/TestHarnessRunner/TestHarnessRunner.cpp b/Source/Tools/TestHarnessRunner/TestHarnessRunner.cpp index f8a17dcb8e..d922be9e38 100644 --- a/Source/Tools/TestHarnessRunner/TestHarnessRunner.cpp +++ b/Source/Tools/TestHarnessRunner/TestHarnessRunner.cpp @@ -300,8 +300,8 @@ int main(int argc, char** argv, char** const envp) { if (!IsHostRunner) { #ifndef _WIN32 - auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get()) : - FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), std::move(Allocator)); + auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get(), nullptr) : + FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), nullptr, std::move(Allocator)); #else auto SyscallHandler = FEX::WindowsHandlers::CreateSyscallHandler(); From 8c1740f5926694f1e973e77687d2fa2044f53b0b Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 20 Sep 2024 00:23:05 -0700 Subject: [PATCH 8/9] Thunks: Remove the temporary TLS variables From prep commit 511103ee5dcc9474b0b7468c05f61ce10fea4393. Now that the frontend is setup, we can remove the temporary TLS variables and use the ThreadObject directly. --- Source/Tools/FEXLoader/FEXLoader.cpp | 2 +- .../LinuxEmulation/LinuxSyscalls/Syscalls.cpp | 2 +- .../LinuxEmulation/LinuxSyscalls/Syscalls.h | 4 ++ Source/Tools/LinuxEmulation/Thunks.cpp | 62 +++++++++---------- Source/Tools/LinuxEmulation/Thunks.h | 2 +- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Source/Tools/FEXLoader/FEXLoader.cpp b/Source/Tools/FEXLoader/FEXLoader.cpp index bd61bbb8db..ea5188c85c 100644 --- a/Source/Tools/FEXLoader/FEXLoader.cpp +++ b/Source/Tools/FEXLoader/FEXLoader.cpp @@ -563,7 +563,7 @@ int main(int argc, char** argv, char** const envp) { auto ParentThread = SyscallHandler->TM.CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer()); SyscallHandler->TM.TrackThread(ParentThread); SignalDelegation->RegisterTLSState(ParentThread); - ThunkHandler->RegisterTLSState(CTX.get(), ParentThread); + ThunkHandler->RegisterTLSState(ParentThread); // Pass in our VDSO thunks ThunkHandler->AppendThunkDefinitions(FEX::VDSO::GetVDSOThunkDefinitions()); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp index b44100c80c..9b2e280a75 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.cpp @@ -876,7 +876,7 @@ void SyscallHandler::UnlockAfterFork(FEXCore::Core::InternalThreadState* LiveThr void SyscallHandler::RegisterTLSState(FEX::HLE::ThreadStateObject* Thread) { SignalDelegation->RegisterTLSState(Thread); - ThunkHandler->RegisterTLSState(CTX, Thread); + ThunkHandler->RegisterTLSState(Thread); } void SyscallHandler::UninstallTLSState(FEX::HLE::ThreadStateObject* Thread) { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index f210bba598..5417a3c6ec 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -192,6 +192,10 @@ class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::Source return SignalDelegation; } + FEX::HLE::ThunkHandler* GetThunkHandler() { + return ThunkHandler; + } + FEX_CONFIG_OPT(IsInterpreter, IS_INTERPRETER); FEX_CONFIG_OPT(IsInterpreterInstalled, INTERPRETER_INSTALLED); FEX_CONFIG_OPT(Filename, APP_FILENAME); diff --git a/Source/Tools/LinuxEmulation/Thunks.cpp b/Source/Tools/LinuxEmulation/Thunks.cpp index d74a7dcac5..fa8f487c77 100644 --- a/Source/Tools/LinuxEmulation/Thunks.cpp +++ b/Source/Tools/LinuxEmulation/Thunks.cpp @@ -7,6 +7,7 @@ tags: glue|thunks */ #include "Thunks.h" +#include "LinuxSyscalls/Syscalls.h" #include "LinuxSyscalls/ThreadManager.h" #include @@ -73,15 +74,7 @@ struct LoadlibArgs { const char* Name; }; -struct ThunkHandler_impl; - -struct TEMP_TLS_DATA { - FEXCore::Core::InternalThreadState* Thread {}; - ThunkHandler_impl* ThunkHandler {}; - FEXCore::Context::Context* CTX {}; -}; - -static thread_local TEMP_TLS_DATA ThreadData {}; +static thread_local FEX::HLE::ThreadStateObject* ThreadObject {}; struct ExportEntry { uint8_t* sha256; @@ -177,22 +170,25 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { Set arg0/1 to arg regs, use CTX::HandleCallback to handle the callback */ static void CallCallback(void* callback, void* arg0, void* arg1) { - if (!ThreadData.Thread) { + if (!ThreadObject) { ERROR_AND_DIE_FMT("Thunked library attempted to invoke guest callback asynchronously"); } - if (ThreadData.ThunkHandler->Is64BitMode()) { - ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0; - ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1; + auto CTX = static_cast(ThreadObject->Thread->CTX); + auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); + + if (ThunkHandler->Is64BitMode()) { + ThreadObject->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDI] = (uintptr_t)arg0; + ThreadObject->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSI] = (uintptr_t)arg1; } else { if ((reinterpret_cast(arg1) >> 32) != 0) { ERROR_AND_DIE_FMT("Tried to call guest function with arguments packed to a 64-bit address"); } - ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RCX] = (uintptr_t)arg0; - ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDX] = (uintptr_t)arg1; + ThreadObject->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RCX] = (uintptr_t)arg0; + ThreadObject->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RDX] = (uintptr_t)arg1; } - ThreadData.CTX->HandleCallback(ThreadData.Thread, (uintptr_t)callback); + CTX->HandleCallback(ThreadObject->Thread, (uintptr_t)callback); } FEXCore::ThunkedFunction* LookupThunk(const FEXCore::IR::SHA256Sum& sha256) override { @@ -208,10 +204,8 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { } } - void RegisterTLSState(FEXCore::Context::Context* CTX, FEX::HLE::ThreadStateObject* ThreadObject) override { - ThreadData.Thread = ThreadObject->Thread; - ThreadData.ThunkHandler = this; - ThreadData.CTX = CTX; + void RegisterTLSState(FEX::HLE::ThreadStateObject* _ThreadObject) override { + ThreadObject = _ThreadObject; } void AppendThunkDefinitions(std::span Definitions) override { @@ -230,10 +224,10 @@ namespace ThunkFunctions { auto Args = reinterpret_cast(ArgsV); std::string_view Name = Args->Name; + auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); auto SOName = - (ThreadData.ThunkHandler->Is64BitMode() ? ThreadData.ThunkHandler->ThunkHostLibsPath() : ThreadData.ThunkHandler->ThunkHostLibsPath32()) + - "/" + Name.data() + "-host.so"; + (ThunkHandler->Is64BitMode() ? ThunkHandler->ThunkHostLibsPath() : ThunkHandler->ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so"; LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName); @@ -262,13 +256,13 @@ namespace ThunkFunctions { } { - std::lock_guard lk(ThreadData.ThunkHandler->ThunksMutex); + std::lock_guard lk(ThunkHandler->ThunksMutex); - ThreadData.ThunkHandler->Libs.insert(fextl::string {Name}); + ThunkHandler->Libs.insert(fextl::string {Name}); int i; for (i = 0; Exports[i].sha256; i++) { - ThreadData.ThunkHandler->Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; + ThunkHandler->Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; } LogMan::Msg::DFmt("Loaded {} syms", i); @@ -282,10 +276,11 @@ namespace ThunkFunctions { }; auto& [Name, rv] = *reinterpret_cast(ArgsRV); + auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); { - std::shared_lock lk(ThreadData.ThunkHandler->ThunksMutex); - rv = ThreadData.ThunkHandler->Libs.contains(Name); + std::shared_lock lk(ThunkHandler->ThunksMutex); + rv = ThunkHandler->Libs.contains(Name); } } @@ -326,7 +321,8 @@ namespace ThunkFunctions { }; auto args = reinterpret_cast(argsv); - ThreadData.CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); + auto CTX = static_cast(ThreadObject->Thread->CTX); + CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); } /** @@ -378,7 +374,7 @@ FEX_DEFAULT_VISIBILITY HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) { LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function"); - const auto ThunkHandler = reinterpret_cast(ThreadData.ThunkHandler); + const auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); const GuestcallInfo gci = {GuestUnpacker, GuestTarget}; @@ -444,15 +440,15 @@ FEX_DEFAULT_VISIBILITY void FinalizeHostTrampolineForGuestFunction(HostToGuestTr } FEX_DEFAULT_VISIBILITY void* GetGuestStack() { - if (!ThreadData.Thread) { + if (!ThreadObject) { ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously"); } - return (void*)(uintptr_t)((ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP])); + return (void*)(uintptr_t)((ThreadObject->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP])); } FEX_DEFAULT_VISIBILITY void MoveGuestStack(uintptr_t NewAddress) { - if (!ThreadData.Thread) { + if (!ThreadObject) { ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously"); } @@ -460,7 +456,7 @@ FEX_DEFAULT_VISIBILITY void MoveGuestStack(uintptr_t NewAddress) { ERROR_AND_DIE_FMT("Tried to set stack pointer for 32-bit guest to a 64-bit address"); } - ThreadData.Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = NewAddress; + ThreadObject->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = NewAddress; } fextl::unique_ptr CreateThunkHandler() { diff --git a/Source/Tools/LinuxEmulation/Thunks.h b/Source/Tools/LinuxEmulation/Thunks.h index 5a3a9b2801..b0b28787cd 100644 --- a/Source/Tools/LinuxEmulation/Thunks.h +++ b/Source/Tools/LinuxEmulation/Thunks.h @@ -7,7 +7,7 @@ struct ThreadStateObject; class ThunkHandler : public FEXCore::ThunkHandler { public: - virtual void RegisterTLSState(FEXCore::Context::Context* CTX, FEX::HLE::ThreadStateObject* ThreadObject) = 0; + virtual void RegisterTLSState(FEX::HLE::ThreadStateObject* ThreadObject) = 0; /** * @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend. * From 926fa3c24cfcfd8be0683fd8b16dba38962257dc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 20 Sep 2024 00:56:19 -0700 Subject: [PATCH 9/9] Thunks: Just move code around for a minor cleanup NFC --- Source/Tools/LinuxEmulation/Thunks.cpp | 312 ++++++++++++------------- Source/Tools/LinuxEmulation/Thunks.h | 2 + 2 files changed, 158 insertions(+), 156 deletions(-) diff --git a/Source/Tools/LinuxEmulation/Thunks.cpp b/Source/Tools/LinuxEmulation/Thunks.cpp index fa8f487c77..323aad54fe 100644 --- a/Source/Tools/LinuxEmulation/Thunks.cpp +++ b/Source/Tools/LinuxEmulation/Thunks.cpp @@ -70,17 +70,8 @@ extern char __stop_HostToGuestTrampolineTemplate[]; namespace FEX::HLE { -struct LoadlibArgs { - const char* Name; -}; - static thread_local FEX::HLE::ThreadStateObject* ThreadObject {}; -struct ExportEntry { - uint8_t* sha256; - FEXCore::ThunkedFunction* Fn; -}; - struct TrampolineInstanceInfo { void* HostPacker; uintptr_t CallCallback; @@ -113,15 +104,6 @@ struct GuestcallInfoHash { } }; -// Bits in a SHA256 sum are already randomly distributed, so truncation yields a suitable hash function -struct TruncatingSHA256Hash { - size_t operator()(const FEXCore::IR::SHA256Sum& SHA256Sum) const noexcept { - return (const size_t&)SHA256Sum; - } -}; - -HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker); - namespace ThunkFunctions { void LoadLib(void* ArgsV); void IsLibLoaded(void* ArgsRV); @@ -133,29 +115,6 @@ namespace ThunkFunctions { struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { std::shared_mutex ThunksMutex; - fextl::unordered_map Thunks = { - {// sha256(fex:loadlib) - {0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, - 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80}, - &ThunkFunctions::LoadLib}, - {// sha256(fex:is_lib_loaded) - {0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, - 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff}, - &ThunkFunctions::IsLibLoaded}, - {// sha256(fex:is_host_heap_allocation) - {0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, - 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e}, - &ThunkFunctions::IsHostHeapAllocation}, - {// sha256(fex:link_address_to_function) - {0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, - 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77}, - &ThunkFunctions::LinkAddressToGuestFunction}, - {// sha256(fex:allocate_host_trampoline_for_guest_function) - {0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, - 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a}, - &ThunkFunctions::AllocateHostTrampolineForGuestFunction}, - }; - // Can't be a string_view. We need to keep a copy of the library name in-case string_view pointer goes away. // Ideally we track when a library has been unloaded and remove it from this set before the memory backing goes away. fextl::set Libs; @@ -165,7 +124,6 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { uint8_t* HostTrampolineInstanceDataPtr; size_t HostTrampolineInstanceDataAvailable = 0; - /* Set arg0/1 to arg regs, use CTX::HandleCallback to handle the callback */ @@ -214,138 +172,91 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler { } } - FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); - FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS); - FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32); -}; - -namespace ThunkFunctions { - void LoadLib(void* ArgsV) { - auto Args = reinterpret_cast(ArgsV); - - std::string_view Name = Args->Name; - auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); - - auto SOName = - (ThunkHandler->Is64BitMode() ? ThunkHandler->ThunkHostLibsPath() : ThunkHandler->ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so"; - - LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName); - - auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW); - if (!Handle) { - ERROR_AND_DIE_FMT("LoadLib: Failed to dlopen thunk library {}: {}", SOName, dlerror()); - } - - // Library names often include dashes, which may not be used in C++ identifiers. - // They are replaced with underscores hence. - auto InitSym = "fexthunks_exports_" + fextl::string {Name}; - std::replace(InitSym.begin(), InitSym.end(), '-', '_'); + void LoadLib(std::string_view Name); - ExportEntry* (*InitFN)(); - (void*&)InitFN = dlsym(Handle, InitSym.c_str()); - if (!InitFN) { - ERROR_AND_DIE_FMT("LoadLib: Failed to find export {}", InitSym); +private: + // Bits in a SHA256 sum are already randomly distributed, so truncation yields a suitable hash function + struct TruncatingSHA256Hash { + size_t operator()(const FEXCore::IR::SHA256Sum& SHA256Sum) const noexcept { + return (const size_t&)SHA256Sum; } + }; - auto Exports = InitFN(); - if (!Exports) { - ERROR_AND_DIE_FMT("LoadLib: Failed to initialize thunk library {}. " - "Check if the corresponding host library is installed " - "or disable thunking of this library.", - Name); - } + fextl::unordered_map Thunks = { + {// sha256(fex:loadlib) + {0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, + 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80}, + &ThunkFunctions::LoadLib}, + {// sha256(fex:is_lib_loaded) + {0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, + 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff}, + &ThunkFunctions::IsLibLoaded}, + {// sha256(fex:is_host_heap_allocation) + {0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, + 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e}, + &ThunkFunctions::IsHostHeapAllocation}, + {// sha256(fex:link_address_to_function) + {0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, + 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77}, + &ThunkFunctions::LinkAddressToGuestFunction}, + {// sha256(fex:allocate_host_trampoline_for_guest_function) + {0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, + 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a}, + &ThunkFunctions::AllocateHostTrampolineForGuestFunction}, + }; - { - std::lock_guard lk(ThunkHandler->ThunksMutex); + FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); + FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS); + FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32); +}; - ThunkHandler->Libs.insert(fextl::string {Name}); +void ThunkHandler_impl::LoadLib(std::string_view Name) { + auto SOName = (Is64BitMode() ? ThunkHostLibsPath() : ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so"; - int i; - for (i = 0; Exports[i].sha256; i++) { - ThunkHandler->Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; - } + LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName); - LogMan::Msg::DFmt("Loaded {} syms", i); - } + auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW); + if (!Handle) { + ERROR_AND_DIE_FMT("LoadLib: Failed to dlopen thunk library {}: {}", SOName, dlerror()); } - void IsLibLoaded(void* ArgsRV) { - struct ArgsRV_t { - const char* Name; - bool rv; - }; + // Library names often include dashes, which may not be used in C++ identifiers. + // They are replaced with underscores hence. + auto InitSym = "fexthunks_exports_" + fextl::string {Name}; + std::replace(InitSym.begin(), InitSym.end(), '-', '_'); - auto& [Name, rv] = *reinterpret_cast(ArgsRV); - auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); + struct ExportEntry { + uint8_t* sha256; + FEXCore::ThunkedFunction* Fn; + }; - { - std::shared_lock lk(ThunkHandler->ThunksMutex); - rv = ThunkHandler->Libs.contains(Name); - } + ExportEntry* (*InitFN)(); + (void*&)InitFN = dlsym(Handle, InitSym.c_str()); + if (!InitFN) { + ERROR_AND_DIE_FMT("LoadLib: Failed to find export {}", InitSym); } - /** - * Checks if the given pointer is allocated on the host heap. - * - * This is useful for thunking APIs that need to work with both guest - * and host heap pointers. - */ - void IsHostHeapAllocation(void* ArgsRV) { -#ifdef ENABLE_JEMALLOC_GLIBC - struct ArgsRV_t { - void* ptr; - bool rv; - }* args = reinterpret_cast(ArgsRV); - - args->rv = glibc_je_is_known_allocation(args->ptr); -#else - // Thunks usage without jemalloc isn't supported - ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information"); -#endif + auto Exports = InitFN(); + if (!Exports) { + ERROR_AND_DIE_FMT("LoadLib: Failed to initialize thunk library {}. " + "Check if the corresponding host library is installed " + "or disable thunking of this library.", + Name); } - /** - * Instructs the Core to redirect calls to functions at the given - * address to another function. The original callee address is passed - * to the target function through an implicit argument stored in r11. - * - * For 32-bit the implicit argument is stored in the lower 32-bits of mm0. - * - * The primary use case of this is ensuring that host function pointers - * returned from thunked APIs can safely be called by the guest. - */ - void LinkAddressToGuestFunction(void* argsv) { - struct args_t { - uintptr_t original_callee; - uintptr_t target_addr; // Guest function to call when branching to original_callee - }; + { + std::lock_guard lk(ThunksMutex); - auto args = reinterpret_cast(argsv); - auto CTX = static_cast(ThreadObject->Thread->CTX); - CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); - } + Libs.insert(fextl::string {Name}); - /** - * Guest-side helper to initiate creation of a host trampoline for - * calling guest functions. This must be followed by a host-side call - * to FinalizeHostTrampolineForGuestFunction to make the trampoline - * usable. - * - * This two-step initialization is equivalent to a host-side call to - * MakeHostTrampolineForGuestFunction. The split is needed if the - * host doesn't have all information needed to create the trampoline - * on its own. - */ - void AllocateHostTrampolineForGuestFunction(void* ArgsRV) { - struct ArgsRV_t { - uintptr_t GuestUnpacker; - uintptr_t GuestTarget; - uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo - }* args = reinterpret_cast(ArgsRV); + int i; + for (i = 0; Exports[i].sha256; i++) { + Thunks[*reinterpret_cast(Exports[i].sha256)] = Exports[i].Fn; + } - args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker); + LogMan::Msg::DFmt("Loaded {} syms", i); } -} // namespace ThunkFunctions +} /** * Generates a host-callable trampoline to call guest functions via the host ABI. @@ -423,7 +334,6 @@ MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uint } FEX_DEFAULT_VISIBILITY void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) { - if (TrampolineAddress == nullptr) { return; } @@ -439,6 +349,96 @@ FEX_DEFAULT_VISIBILITY void FinalizeHostTrampolineForGuestFunction(HostToGuestTr } } +namespace ThunkFunctions { + void LoadLib(void* ArgsV) { + struct LoadlibArgs { + const char* Name; + }; + + auto Args = reinterpret_cast(ArgsV); + auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); + + ThunkHandler->LoadLib(Args->Name); + } + + void IsLibLoaded(void* ArgsRV) { + struct ArgsRV_t { + const char* Name; + bool rv; + }; + + auto& [Name, rv] = *reinterpret_cast(ArgsRV); + auto ThunkHandler = reinterpret_cast(FEX::HLE::_SyscallHandler->GetThunkHandler()); + + { + std::shared_lock lk(ThunkHandler->ThunksMutex); + rv = ThunkHandler->Libs.contains(Name); + } + } + + /** + * Checks if the given pointer is allocated on the host heap. + * + * This is useful for thunking APIs that need to work with both guest + * and host heap pointers. + */ + void IsHostHeapAllocation(void* ArgsRV) { +#ifdef ENABLE_JEMALLOC_GLIBC + struct ArgsRV_t { + void* ptr; + bool rv; + }* args = reinterpret_cast(ArgsRV); + + args->rv = glibc_je_is_known_allocation(args->ptr); +#else + // Thunks usage without jemalloc isn't supported + ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information"); +#endif + } + + /** + * Instructs the Core to redirect calls to functions at the given + * address to another function. The original callee address is passed + * to the target function through an implicit argument stored in r11. + * + * For 32-bit the implicit argument is stored in the lower 32-bits of mm0. + * + * The primary use case of this is ensuring that host function pointers + * returned from thunked APIs can safely be called by the guest. + */ + void LinkAddressToGuestFunction(void* argsv) { + struct args_t { + uintptr_t original_callee; + uintptr_t target_addr; // Guest function to call when branching to original_callee + }; + + auto args = reinterpret_cast(argsv); + auto CTX = static_cast(ThreadObject->Thread->CTX); + CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr); + } + + /** + * Guest-side helper to initiate creation of a host trampoline for + * calling guest functions. This must be followed by a host-side call + * to FinalizeHostTrampolineForGuestFunction to make the trampoline + * usable. + * + * This two-step initialization is equivalent to a host-side call to + * MakeHostTrampolineForGuestFunction. The split is needed if the + * host doesn't have all information needed to create the trampoline + * on its own. + */ + void AllocateHostTrampolineForGuestFunction(void* ArgsRV) { + struct ArgsRV_t { + uintptr_t GuestUnpacker; + uintptr_t GuestTarget; + uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo + }* args = reinterpret_cast(ArgsRV); + + args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker); + } +} // namespace ThunkFunctions + FEX_DEFAULT_VISIBILITY void* GetGuestStack() { if (!ThreadObject) { ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously"); diff --git a/Source/Tools/LinuxEmulation/Thunks.h b/Source/Tools/LinuxEmulation/Thunks.h index b0b28787cd..163c6e0843 100644 --- a/Source/Tools/LinuxEmulation/Thunks.h +++ b/Source/Tools/LinuxEmulation/Thunks.h @@ -2,6 +2,8 @@ #include #include +#include + namespace FEX::HLE { struct ThreadStateObject;