From 7d1ec7dae2ff4f2a912866b3022f244e93bedc48 Mon Sep 17 00:00:00 2001 From: squidbus <175574877+squidbus@users.noreply.github.com> Date: Thu, 19 Sep 2024 02:06:11 -0700 Subject: [PATCH] core: Swap TCB in and out of FS segment around guest code on Linux. --- src/core/cpu_patches.cpp | 11 +--- src/core/libraries/avplayer/avplayer.cpp | 2 +- .../libraries/avplayer/avplayer_state.cpp | 4 +- .../libraries/kernel/thread_management.cpp | 2 +- src/core/libraries/libs.h | 42 ++------------ src/core/libraries/network/net_ctl_obj.cpp | 4 +- src/core/linker.cpp | 4 +- src/core/linker.h | 56 +++++++++++++++++-- src/core/module.cpp | 2 +- src/core/tls.cpp | 18 ++++++ src/core/tls.h | 3 + 11 files changed, 88 insertions(+), 60 deletions(-) diff --git a/src/core/cpu_patches.cpp b/src/core/cpu_patches.cpp index 202cfbb85..9558a0377 100644 --- a/src/core/cpu_patches.cpp +++ b/src/core/cpu_patches.cpp @@ -881,11 +881,8 @@ struct PatchInfo { }; static const std::unordered_map Patches = { -#if defined(_WIN32) - // Windows needs a trampoline. +#ifdef _WIN32 {ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, true}}, -#elif !defined(__APPLE__) - {ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}}, #endif {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}}, @@ -1216,12 +1213,6 @@ void PrePatchInstructions(u64 segment_addr, u64 segment_size) { code_page += 0x1000; } } -#elif !defined(_WIN32) - // Linux and others have an FS segment pointing to valid memory, so continue to do full - // ahead-of-time patching for now until a better solution is worked out. - if (!Patches.empty()) { - TryPatchAot(reinterpret_cast(segment_addr), segment_size); - } #endif } diff --git a/src/core/libraries/avplayer/avplayer.cpp b/src/core/libraries/avplayer/avplayer.cpp index 60d68c4f7..c7476a1fc 100644 --- a/src/core/libraries/avplayer/avplayer.cpp +++ b/src/core/libraries/avplayer/avplayer.cpp @@ -309,7 +309,7 @@ void RegisterlibSceAvPlayer(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("XC9wM+xULz8", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerJumpToTime); LIB_FUNCTION("9y5v+fGN4Wk", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPause); LIB_FUNCTION("HD1YKVU26-M", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPostInit); - LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); + // LIB_FUNCTION("agig-iDRrTE", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerPrintf); LIB_FUNCTION("w5moABNwnRY", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerResume); LIB_FUNCTION("k-q+xOxdc3E", "libSceAvPlayer", 1, "libSceAvPlayer", 1, 0, sceAvPlayerSetAvSyncMode); diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index c4d666fce..b0e498479 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -91,7 +91,7 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer const auto callback = self->m_event_replacement.event_callback; const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { - auto* linker = Common::Singleton::Instance(); + const auto* linker = Common::Singleton::Instance(); linker->ExecuteGuest(callback, ptr, event_id, 0, event_data); } } @@ -367,7 +367,7 @@ void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { const auto callback = m_init_data.event_replacement.event_callback; if (callback) { const auto ptr = m_init_data.event_replacement.object_ptr; - auto* linker = Common::Singleton::Instance(); + const auto* linker = Common::Singleton::Instance(); linker->ExecuteGuest(callback, ptr, event_id, 0, event_data); } } diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index 455ac24b1..461f903e5 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -989,7 +989,7 @@ static void cleanup_thread(void* arg) { static void* run_thread(void* arg) { auto* thread = static_cast(arg); Common::SetCurrentThreadName(thread->name.c_str()); - auto* linker = Common::Singleton::Instance(); + const auto* linker = Common::Singleton::Instance(); void* ret = nullptr; g_pthread_self = thread; pthread_cleanup_push(cleanup_thread, thread); diff --git a/src/core/libraries/libs.h b/src/core/libraries/libs.h index ea928101e..2cc965c05 100644 --- a/src/core/libraries/libs.h +++ b/src/core/libraries/libs.h @@ -6,45 +6,12 @@ #include #include "common/logging/log.h" +#include "common/singleton.h" +#include "core/linker.h" #include "core/loader/elf.h" #include "core/loader/symbols_resolver.h" -template -struct StringLiteral { - constexpr StringLiteral(const char (&str)[N]) { - std::copy_n(str, N, value); - } - - char value[N]; -}; - -template -struct wrapper_impl; - -template -struct wrapper_impl { - static R PS4_SYSV_ABI wrap(Args... args) { - if (std::string_view(name.value) != "scePthreadEqual" && - std::string_view(name.value) != "sceUserServiceGetEvent") { - // LOG_WARNING(Core_Linker, "Function {} called", name.value); - } - if constexpr (std::is_same_v || std::is_same_v) { - const u32 ret = f(args...); - if (ret != 0 && std::string_view(name.value) != "scePthreadEqual") { - LOG_WARNING(Core_Linker, "Function {} returned {:#x}", name.value, ret); - } - return ret; - } - // stuff - return f(args...); - } -}; - -template -constexpr auto wrapper = wrapper_impl::wrap; - -// #define W(foo) wrapper<#foo, decltype(&foo), foo> -#define W(foo) foo +#define W(linker, foo) linker->WrapHost<#foo, decltype(&foo), foo>() #define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \ { \ @@ -56,7 +23,8 @@ constexpr auto wrapper = wrapper_impl::wrap; sr.module_version_major = moduleVersionMajor; \ sr.module_version_minor = moduleVersionMinor; \ sr.type = Core::Loader::SymbolType::Function; \ - auto func = reinterpret_cast(W(function)); \ + const auto* linker = Common::Singleton::Instance(); \ + auto func = reinterpret_cast(W(linker, function)); \ sym->AddSymbol(sr, func); \ } diff --git a/src/core/libraries/network/net_ctl_obj.cpp b/src/core/libraries/network/net_ctl_obj.cpp index 8193c684e..07381d676 100644 --- a/src/core/libraries/network/net_ctl_obj.cpp +++ b/src/core/libraries/network/net_ctl_obj.cpp @@ -59,7 +59,7 @@ s32 Libraries::NetCtl::NetCtlInternal::registerNpToolkitCallback( void Libraries::NetCtl::NetCtlInternal::checkCallback() { std::unique_lock lock{m_mutex}; - auto* linker = Common::Singleton::Instance(); + const auto* linker = Common::Singleton::Instance(); for (auto& callback : callbacks) { if (callback.func != nullptr) { linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, @@ -70,7 +70,7 @@ void Libraries::NetCtl::NetCtlInternal::checkCallback() { void Libraries::NetCtl::NetCtlInternal::checkNpToolkitCallback() { std::unique_lock lock{m_mutex}; - auto* linker = Common::Singleton::Instance(); + const auto* linker = Common::Singleton::Instance(); for (auto& callback : nptoolCallbacks) { if (callback.func != nullptr) { linker->ExecuteGuest(callback.func, ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED, diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 4e4fa28d2..aa781b2b4 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -334,7 +334,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) { thread_local std::once_flag init_tls_flag; -void Linker::EnsureThreadInitialized(bool is_primary) { +void Linker::EnsureThreadInitialized(bool is_primary) const { std::call_once(init_tls_flag, [this, is_primary] { #ifdef ARCH_X86_64 InitializeThreadPatchStack(); @@ -343,7 +343,7 @@ void Linker::EnsureThreadInitialized(bool is_primary) { }); } -void Linker::InitTlsForThread(bool is_primary) { +void Linker::InitTlsForThread(bool is_primary) const { static constexpr size_t TcbSize = 0x40; static constexpr size_t TlsAllocAlign = 0x20; const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize; diff --git a/src/core/linker.h b/src/core/linker.h index 18454f602..44e7ad390 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -7,6 +7,7 @@ #include #include #include "core/module.h" +#include "core/tls.h" namespace Core { @@ -109,16 +110,39 @@ class Linker { void DebugDump(); template - ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) { + ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), + CallArgs&&... args) const { // Make sure TLS is initialized for the thread before entering guest. EnsureThreadInitialized(); - return func(std::forward(args)...); + SwapTls(); + if constexpr (std::is_same_v) { + func(std::forward(args)...); + SwapTls(); + return; + } else { + ReturnType ret = func(std::forward(args)...); + SwapTls(); + return ret; + } + } + + template + struct StringLiteral { + constexpr StringLiteral(const char (&str)[N]) { + std::copy_n(str, N, value); + } + char value[N]; + }; + + template + FuncType WrapHost() const { + return host_wrapper_impl::wrap; } private: const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l); - void EnsureThreadInitialized(bool is_primary = false); - void InitTlsForThread(bool is_primary); + void EnsureThreadInitialized(bool is_primary = false) const; + void InitTlsForThread(bool is_primary) const; MemoryManager* memory; std::mutex mutex; @@ -129,6 +153,30 @@ class Linker { AppHeapAPI heap_api{}; std::vector> m_modules; Loader::SymbolsResolver m_hle_symbols{}; + + template + struct host_wrapper_impl; + + template + struct host_wrapper_impl { + static ReturnType PS4_SYSV_ABI wrap(FuncArgs... args) { + /*if (std::string_view(name.value) != "scePthreadEqual" && + std::string_view(name.value) != "sceUserServiceGetEvent") { + LOG_WARNING(Core_Linker, "Function {} called", name.value); + }*/ + SwapTls(); + if constexpr (std::is_same_v) { + func(args...); + SwapTls(); + return; + } else { + ReturnType ret = func(args...); + SwapTls(); + return ret; + } + } + }; }; } // namespace Core diff --git a/src/core/module.cpp b/src/core/module.cpp index e62c57785..5d3b40577 100644 --- a/src/core/module.cpp +++ b/src/core/module.cpp @@ -70,7 +70,7 @@ Module::~Module() = default; s32 Module::Start(size_t args, const void* argp, void* param) { LOG_INFO(Core_Linker, "Module started : {}", name); - auto* linker = Common::Singleton::Instance(); + const auto* linker = Common::Singleton::Instance(); const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); return linker->ExecuteGuest(reinterpret_cast(addr), args, argp, param); } diff --git a/src/core/tls.cpp b/src/core/tls.cpp index eb07e7a72..d1d031324 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -45,6 +45,8 @@ Tcb* GetTcbBase() { return reinterpret_cast(TlsGetValue(GetTcbKey())); } +void SwapTls() {} + #elif defined(__APPLE__) && defined(ARCH_X86_64) // Apple x86_64 @@ -139,6 +141,8 @@ Tcb* GetTcbBase() { return tcb; } +void SwapTls() {} + #elif defined(ARCH_X86_64) // Other POSIX x86_64 @@ -153,6 +157,18 @@ Tcb* GetTcbBase() { return tcb; } +void SwapTls() { + void* fsbase; + void* gsbase; + asm volatile("rdfsbase %0;" + "rdgsbase %1" + : "=r"(fsbase), "=r"(gsbase)::"memory"); + asm volatile("wrfsbase %0;" + "wrgsbase %1" ::"r"(gsbase), + "r"(fsbase) + : "memory"); +} + #else // POSIX non-x86_64 @@ -179,6 +195,8 @@ Tcb* GetTcbBase() { return static_cast(pthread_getspecific(GetTcbKey())); } +void SwapTls() {} + #endif } // namespace Core diff --git a/src/core/tls.h b/src/core/tls.h index f5bf33184..806e5d620 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -33,4 +33,7 @@ void SetTcbBase(void* image_address); /// Retrieves Tcb structure for the calling thread. Tcb* GetTcbBase(); +/// Swaps between the host and guest TLS area if needed. +void SwapTls(); + } // namespace Core