diff --git a/FEXCore/Source/Interface/Context/Context.h b/FEXCore/Source/Interface/Context/Context.h index e17160997d..137f8a2bea 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 f39d95fc6e..93b2e1f1fe 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -1004,6 +1004,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 8921955ce8..fdb9ee528f 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: };