LinuxSyscalls: Enable delayed code cache load for ELF files

Specifically this is needed if any ELF relocations cover read-only code
sections, which is indicated in the ELF headers via DT_TEXTREL/DF_TEXTREL.
This commit is contained in:
Tony Wasserka committed 2026-03-18 11:59:41 +01:00
1 parent 226bd51afe
commit 1ea24f3d6c
2 files changed
+52 -1

No files matched your search

@@ -240,6 +240,54 @@ struct ELFParser {
return ReadElf(NewFD);
}
/**
* Checks if DT_TEXTREL/DF_TEXTREL exist in the PT_DYNAMIC segment.
*
* These indicate that the ELF has relocations that cover to read-only code
* pages. The dynamic loader will temporarily map these pages as writeable
* to apply the relocations.
*/
bool HasCodeRelocations() const {
if (fd == -1) {
return false;
}
auto phdr_it = std::ranges::find_if(phdrs, [](auto& phdr) { return phdr.p_type == PT_DYNAMIC; });
if (phdr_it == phdrs.end()) {
return false;
}
if (type == ::ELFLoader::ELFContainer::TYPE_X86_32) {
return HasCodeRelocations<Elf32_Dyn>(*phdr_it);
} else {
return HasCodeRelocations<Elf64_Dyn>(*phdr_it);
}
}
template<typename Elf_Dyn>
bool HasCodeRelocations(const Elf64_Phdr& phdr) const {
const size_t EntryCount = phdr.p_filesz / sizeof(Elf_Dyn);
fextl::vector<Elf_Dyn> Entries(EntryCount);
if (pread(fd, Entries.data(), phdr.p_filesz, phdr.p_offset) == -1) {
return false;
}
for (auto& Entry : Entries) {
if (Entry.d_tag == DT_NULL) {
break;
}
if (Entry.d_tag == DT_TEXTREL) {
return true;
}
if (Entry.d_tag == DT_FLAGS && (Entry.d_un.d_val & DF_TEXTREL)) {
return true;
}
}
return false;
}
/**
* Parses relocation sections (SHT_REL/SHT_RELA) and returns a map of
* offsets to relocations that FEX's JIT must know about.
@@ -205,6 +205,7 @@ FEXCore::HLE::ExecutableRangeInfo SyscallHandler::QueryGuestExecutableRange(FEXC
struct ReadELFHeadersResult {
fextl::vector<Elf64_Phdr> ProgramHeaders;
fextl::robin_map<uint32_t, FEXCore::GuestRelocationType> Relocations;
bool HasCodeRelocations;
};
static ReadELFHeadersResult ReadELFHeaders(int FD, std::span<std::byte> HeaderData = {}) {
@@ -226,7 +227,8 @@ static ReadELFHeadersResult ReadELFHeaders(int FD, std::span<std::byte> HeaderDa
LogMan::Msg::IFmt("Loaded ELF with {} relocations", Relocations.size());
}
return ReadELFHeadersResult {std::move(Parser.phdrs), std::move(Relocations)};
auto HasCodeRelocations = Parser.HasCodeRelocations();
return ReadELFHeadersResult {std::move(Parser.phdrs), std::move(Relocations), HasCodeRelocations};
}
static void LoadCodeCache(FEXCore::Core::InternalThreadState& Thread, FEXCore::ExecutableFileSectionInfo& Section, uint64_t CodeCacheConfigId) {
@@ -552,6 +554,7 @@ SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t a
auto ELFResult = ReadELFHeaders(fd, std::span {reinterpret_cast<std::byte*>(addr), length});
Resource->ProgramHeaders = std::move(ELFResult.ProgramHeaders);
Resource->MappedFile->Relocations = std::move(ELFResult.Relocations);
Resource->RequiresDelayedCacheLoad = ELFResult.HasCodeRelocations;
LOGMAN_THROW_A_FMT(Resource->ProgramHeaders.empty() || offset == 0, "Expected file offset 0 for the first mapping of an ELF "
"file");
}