From 293568be363b70453eb2da8cbea47324ab6a99e2 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 11 Mar 2026 18:16:04 +0100 Subject: [PATCH] FEXOfflineCompiler: Apply relocations to loaded ELF binaries --- .../Tools/CommonTools/Linux/Utils/ELFParser.h | 50 +++++++++++++++++++ Source/Tools/FEXOfflineCompiler/Main.cpp | 43 +++++++++++++--- 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/Source/Tools/CommonTools/Linux/Utils/ELFParser.h b/Source/Tools/CommonTools/Linux/Utils/ELFParser.h index d33410255..5d350979e 100644 --- a/Source/Tools/CommonTools/Linux/Utils/ELFParser.h +++ b/Source/Tools/CommonTools/Linux/Utils/ELFParser.h @@ -304,6 +304,56 @@ struct ELFParser { return Relocations; } + /** + * Returns underlying 32-bit relocation entries. + * SHT_REL entries are implicitly converted to Elf32_Rela. + */ + fextl::vector ReadRawRelocations32() { + if (fd == -1 || type != ::ELFLoader::ELFContainer::TYPE_X86_32 || !EnsureSectionHeadersLoaded()) { + return {}; + } + + // Load dynamic symbol table (find SHT_DYNSYM section) + fextl::vector DynSyms; + auto DynsymHeader = std::ranges::find_if(*shdrs, [](auto& shdr) { return shdr.sh_type == SHT_DYNSYM; }); + if (DynsymHeader != shdrs->end()) { + size_t SymCount = DynsymHeader->sh_size / sizeof(Elf32_Sym); + DynSyms.resize(SymCount); + if (pread(fd, DynSyms.data(), DynsymHeader->sh_size, DynsymHeader->sh_offset) == -1) { + LOGMAN_MSG_A_FMT("Could not load DYNSYM section"); + } + } + + fextl::vector Result; + for (const auto& shdr : *shdrs) { + if (shdr.sh_entsize == 0) { + continue; + } + + const size_t EntryCount = shdr.sh_size / shdr.sh_entsize; + + if (shdr.sh_type == SHT_REL) { + fextl::vector Entries(EntryCount); + if (pread(fd, Entries.data(), shdr.sh_size, shdr.sh_offset) == -1) { + LOGMAN_MSG_A_FMT("Could not load REL section"); + } + for (auto& Entry : Entries) { + auto Sym = ELF32_R_SYM(Entry.r_info); + int32_t Addend = (Sym < DynSyms.size()) ? static_cast(DynSyms[Sym].st_value) : 0; + Result.push_back(Elf32_Rela {Entry.r_offset, Entry.r_info, Addend}); + } + } else if (shdr.sh_type == SHT_RELA) { + fextl::vector Entries(EntryCount); + if (pread(fd, Entries.data(), shdr.sh_size, shdr.sh_offset) == -1) { + LOGMAN_MSG_A_FMT("Could not load RELA section"); + } + Result.insert(Result.end(), Entries.begin(), Entries.end()); + } + } + + return Result; + } + void Closefd() { if (fd != -1) { close(fd); diff --git a/Source/Tools/FEXOfflineCompiler/Main.cpp b/Source/Tools/FEXOfflineCompiler/Main.cpp index da2f68e11..8b720d9bb 100644 --- a/Source/Tools/FEXOfflineCompiler/Main.cpp +++ b/Source/Tools/FEXOfflineCompiler/Main.cpp @@ -50,7 +50,8 @@ public: } void* GuestMmap(FEXCore::Core::InternalThreadState*, void* addr, size_t Size, int prot, int Flags, int fd, off_t offset) override { - auto Ret = mmap(addr, Size, prot, Flags, fd, offset); + // Force writeable to allow applying relocations + auto Ret = mmap(addr, Size, prot | PROT_WRITE, Flags, fd, offset); if (Ret != MAP_FAILED && VAFileStart == 0) { VAFileStart = reinterpret_cast(Ret); } @@ -113,8 +114,7 @@ static FEXCore::Core::InternalThreadState* SetupCompileThread(FEXCore::Context:: } // Returns filename of generated cache on success -static std::optional -GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set BlockList, std::string_view OutDir) { +static std::optional GenerateSingleCache(FEXCore::ExecutableFileInfo& Binary, fextl::set BlockList, std::string_view OutDir) { uint64_t CodeCacheConfigId = 0; // TODO: Make unique to active configuration ELFCodeLoader Loader(Binary.Filename.c_str(), -1, "", fextl::vector {Binary.Filename.c_str()}, @@ -123,7 +123,19 @@ GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set(SyscallOSABI); + + // Populate relocations from ELF file + { + ELFParser RelocParser; + RelocParser.ReadElf(Binary.Filename); + Binary.Relocations = RelocParser.PopulateRelocations(); + SyscallHandler->FileInfo.Relocations = Binary.Relocations; + } + FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Is64Bit ? "1" : "0"); // Load HostFeatures @@ -137,13 +149,9 @@ GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set(); - - auto SyscallOSABI = Is64Bit ? FEXCore::HLE::SyscallOSABI::OS_LINUX64 : FEXCore::HLE::SyscallOSABI::OS_LINUX32; - auto SyscallHandler = std::make_unique(SyscallOSABI); - Loader.CalculateHWCaps(CTX.get()); + auto SignalDelegation = std::make_unique(); CTX->SetSignalDelegator(SignalDelegation.get()); CTX->SetSyscallHandler(SyscallHandler.get()); auto ThunkHandler = FEX::HLE::CreateThunkHandler(); @@ -166,6 +174,25 @@ GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set(SyscallHandler->VAFileStart + reloc.r_offset) + SyscallHandler->VAFileStart; + memcpy(reinterpret_cast(SyscallHandler->VAFileStart + reloc.r_offset), &val, sizeof(val)); + } else if (ELF32_R_TYPE(reloc.r_info) == R_386_32) { + // The FEX-relocation is applied on top of this during cache serialization, so this must be countered + uint32_t* orig = reinterpret_cast(SyscallHandler->VAFileStart + reloc.r_offset); + uint32_t val = *orig + reloc.r_addend + SyscallHandler->VAFileStart; + memcpy(orig, &val, sizeof(val)); + } + } + } } CTX->GetCodeCache().InitiateCacheGeneration();