From b13b46eefedf0b1672c2d5de21b237ad03bb2976 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 12 Nov 2025 16:19:51 +0100 Subject: [PATCH] CodeCache: Delay cache loading for guest library wrappers until after LoadLib These libraries need to be initialized before relocating their caches, since the guest function hashes won't be registered before. --- .../Tools/FEXInterpreter/FEXInterpreter.cpp | 5 +++- .../LinuxEmulation/LinuxSyscalls/Syscalls.h | 1 + .../LinuxSyscalls/SyscallsSMCTracking.cpp | 26 ++++++++++++++++++- Source/Tools/LinuxEmulation/Thunks.cpp | 2 ++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp index 3356cb9d0..aedb07b4d 100644 --- a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp +++ b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp @@ -597,9 +597,12 @@ int main(int argc, char** argv, char** const envp) { SyscallHandler->DefaultProgramBreak(BRKInfo.Base, BRKInfo.Size); - // Request code cache generation if (FEXCore::Config::Get_ENABLECODECACHINGWIP()) { + // Request code cache generation FEXServerClient::PopulateCodeCache(FEXServerClient::GetServerFD(), Loader.GetMainElfFD(), FEXCore::Config::Get_MULTIBLOCK()); + + // Finalize code cache for libVDSO-guest.so. This needs to be done explicitly since VDSO doesn't use LoadLib. + SyscallHandler->TriggerGuestLibWrapperCodeCacheLoad(*ParentThread->Thread, reinterpret_cast(VDSOMapping.VDSOBase)); } // Pull RIP and stack pointer from loader and set the thread data to it. diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index 0e3f3aeb4..dd5b28bf9 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -292,6 +292,7 @@ public: std::optional LookupExecutableFileSection(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) final override; + void TriggerGuestLibWrapperCodeCacheLoad(FEXCore::Core::InternalThreadState&, uint64_t AnyAddr); int OpenCodeMapFile() override; FEXCore::HLE::ExecutableRangeInfo QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) override; diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index d6773a8a0..69bf51c4f 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -366,6 +366,26 @@ uint64_t SyscallHandler::GuestMremap(bool Is64Bit, FEXCore::Core::InternalThread return Result; } +void SyscallHandler::TriggerGuestLibWrapperCodeCacheLoad(FEXCore::Core::InternalThreadState& Thread, uint64_t AnyAddr) { + if (!EnableCodeCaching) { + return; + } + + // TODO: Instead of deferring the entire cache load, only delay applicance of sha256 relocations! + + auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, &Thread); + auto VMAEntry = VMATracking.FindVMAEntry(reinterpret_cast(AnyAddr)); + + for (auto* VMA = VMAEntry->second.Resource->FirstVMA; VMA; VMA = VMA->ResourceNextVMA) { + if (!VMA->Prot.Executable) { + continue; + } + + auto SectionInfo = BuildSectionInfo(*VMAEntry->second.Resource, VMA->Base, VMA->Length); + LoadCodeCache(Thread, SectionInfo, CodeCacheConfigId); + } +} + int SyscallHandler::OpenCodeMapFile() { // Query from FEXServer whether this is the first instance of this executable; if it is, also enable code dumping! FEX_CONFIG_OPT(RootFSPath, ROOTFS); @@ -623,7 +643,11 @@ SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t a // Load code cache if present. // FEXServer was requested to generate library caches on program launch. if (EnableCodeCaching && Resource && Resource->MappedFile && VMATracking::VMAProt::fromProt(prot).Executable) { - if (Thread) { + if (Resource->MappedFile->Filename.ends_with("-guest.so")) { + // For guest library wrappers, cache loading must be delayed until LoadLib is called. + // Before that, we can't patch up the SHA256 function identifiers. + LogMan::Msg::IFmt("Delaying code cache load for {}", Resource->MappedFile->Filename); + } else if (Thread) { if (!Resource->RequiresDelayedCacheLoad) { CachedSection.emplace(BuildSectionInfo(*Resource, addr, Size)); } else { diff --git a/Source/Tools/LinuxEmulation/Thunks.cpp b/Source/Tools/LinuxEmulation/Thunks.cpp index f77114fa7..9c018bc46 100644 --- a/Source/Tools/LinuxEmulation/Thunks.cpp +++ b/Source/Tools/LinuxEmulation/Thunks.cpp @@ -259,6 +259,8 @@ void ThunkHandler_impl::LoadLib(std::string_view Name) { LogMan::Msg::DFmt("Loaded {} syms", i); } + + _SyscallHandler->TriggerGuestLibWrapperCodeCacheLoad(*ThreadObject->Thread, ThreadObject->Thread->CurrentFrame->State.rip); } /**