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;