FEXOfflineCompiler: Apply relocations to loaded ELF binaries

This commit is contained in:
Tony Wasserka committed 2026-03-18 11:57:38 +01:00
1 parent 152fe81d16
commit 293568be36
2 files changed
+85 -8

No files matched your search

@@ -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<Elf32_Rela> ReadRawRelocations32() {
if (fd == -1 || type != ::ELFLoader::ELFContainer::TYPE_X86_32 || !EnsureSectionHeadersLoaded()) {
return {};
}
// Load dynamic symbol table (find SHT_DYNSYM section)
fextl::vector<Elf32_Sym> 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<Elf32_Rela> 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<Elf32_Rel> 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<int32_t>(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<Elf32_Rela> 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);
+35 -8
View File
@@ -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<uintptr_t>(Ret);
}
@@ -113,8 +114,7 @@ static FEXCore::Core::InternalThreadState* SetupCompileThread(FEXCore::Context::
}
// Returns filename of generated cache on success
static std::optional<std::string>
GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set<uintptr_t> BlockList, std::string_view OutDir) {
static std::optional<std::string> GenerateSingleCache(FEXCore::ExecutableFileInfo& Binary, fextl::set<uintptr_t> BlockList, std::string_view OutDir) {
uint64_t CodeCacheConfigId = 0; // TODO: Make unique to active configuration
ELFCodeLoader Loader(Binary.Filename.c_str(), -1, "", fextl::vector<fextl::string> {Binary.Filename.c_str()},
@@ -123,7 +123,19 @@ GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set<uintpt
fmt::print("Invalid or unsupported ELF file.\n");
return std::nullopt;
}
const bool Is64Bit = Loader.Is64BitMode();
auto SyscallOSABI = Is64Bit ? FEXCore::HLE::SyscallOSABI::OS_LINUX64 : FEXCore::HLE::SyscallOSABI::OS_LINUX32;
auto SyscallHandler = std::make_unique<AOTSyscallHandler>(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<uintpt
auto CTX = FEXCore::Context::Context::CreateNewContext(HostFeatures);
auto SignalDelegation = std::make_unique<FEX::DummyHandlers::DummySignalDelegator>();
auto SyscallOSABI = Is64Bit ? FEXCore::HLE::SyscallOSABI::OS_LINUX64 : FEXCore::HLE::SyscallOSABI::OS_LINUX32;
auto SyscallHandler = std::make_unique<AOTSyscallHandler>(SyscallOSABI);
Loader.CalculateHWCaps(CTX.get());
auto SignalDelegation = std::make_unique<FEX::DummyHandlers::DummySignalDelegator>();
CTX->SetSignalDelegator(SignalDelegation.get());
CTX->SetSyscallHandler(SyscallHandler.get());
auto ThunkHandler = FEX::HLE::CreateThunkHandler();
@@ -166,6 +174,25 @@ GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set<uintpt
if (!ElfBase.has_value()) {
ERROR_AND_DIE_FMT("Failed to load ELF file {} ({})", Binary.Filename, Binary.FileId);
}
{
ELFParser RelocParser;
RelocParser.ReadElf(Binary.Filename);
auto relocs32 = RelocParser.ReadRawRelocations32();
for (auto& reloc : relocs32) {
if (ELF32_R_TYPE(reloc.r_info) == R_386_RELATIVE) {
// The FEX-relocation is applied on top of this during cache serialization, so this must be countered
uint32_t val = *reinterpret_cast<uint32_t*>(SyscallHandler->VAFileStart + reloc.r_offset) + SyscallHandler->VAFileStart;
memcpy(reinterpret_cast<uint32_t*>(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<uint32_t*>(SyscallHandler->VAFileStart + reloc.r_offset);
uint32_t val = *orig + reloc.r_addend + SyscallHandler->VAFileStart;
memcpy(orig, &val, sizeof(val));
}
}
}
}
CTX->GetCodeCache().InitiateCacheGeneration();