From 6c77bc12c172f92e07d4957457517491ae8c8af1 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 16 Oct 2025 16:52:02 +0200 Subject: [PATCH 1/7] LinuxSyscalls: Don't skip MappedResource creation on path query failure MarkGuestExecutableRange requires a MappedResource to be available even when FEX doesn't do anything else with it. --- .../LinuxSyscalls/SyscallsSMCTracking.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 3e792acf4..2d70b81f7 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -388,14 +388,16 @@ std::optional SyscallHandler: char Tmp[PATH_MAX]; auto PathLength = FEX::get_fdpath(fd, Tmp); - if (PathLength != -1) { - auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); - Resource = &Iter->second; + auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); + Resource = &Iter->second; + if (Inserted) { + Resource->Iterator = Iter; + } + if (PathLength != -1) { if (Inserted) { Resource->MappedFile = fextl::make_unique(); Resource->MappedFile->Filename = fextl::string(Tmp, PathLength); - Resource->Iterator = Iter; } const fextl::string Filename = FHU::Filesystem::GetFilename(Resource->MappedFile->Filename); From 02c00a87dde59cd89f6d7be1db22ed068ae50e0c Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 16 Oct 2025 16:52:08 +0200 Subject: [PATCH 2/7] LinuxSyscalls: Track MappedResources only for regular files that are executable --- .../Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 2d70b81f7..6ba8d27da 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -394,7 +394,8 @@ std::optional SyscallHandler: Resource->Iterator = Iter; } - if (PathLength != -1) { + // Only handle FDs that are backed by regular files that are executable + if (PathLength != -1 && S_ISREG(buf.st_mode) && (buf.st_mode & S_IXUSR)) { if (Inserted) { Resource->MappedFile = fextl::make_unique(); Resource->MappedFile->Filename = fextl::string(Tmp, PathLength); From 92428e5cbc402566293e216a0eff8aa5f1beeb82 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 15 Oct 2025 22:53:29 +0200 Subject: [PATCH 3/7] LinuxSyscalls: Use a multimap to store MappedResources This allows for creating separate MappedResources when an ELF file is mapped multiple times at different base addresses. --- .../LinuxSyscalls/SyscallsSMCTracking.cpp | 27 ++++++++++++------- .../LinuxSyscalls/SyscallsVMATracking.h | 19 +++++++++++-- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 6ba8d27da..3e2c914ce 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -388,11 +388,15 @@ std::optional SyscallHandler: char Tmp[PATH_MAX]; auto PathLength = FEX::get_fdpath(fd, Tmp); - auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); - Resource = &Iter->second; - if (Inserted) { - Resource->Iterator = Iter; + auto [ResourceIt, ResourceEnd] = VMATracking.FindResources(mrid); + bool Inserted = false; + if (ResourceIt == ResourceEnd) { + // Create a new MappedResource for previously unseen file + ResourceIt = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); + ResourceIt->second.Iterator = ResourceIt; + Inserted = true; } + Resource = &ResourceIt->second; // Only handle FDs that are backed by regular files that are executable if (PathLength != -1 && S_ISREG(buf.st_mode) && (buf.st_mode & S_IXUSR)) { @@ -419,8 +423,10 @@ std::optional SyscallHandler: } else if (flags & MAP_SHARED) { VMATracking::MRID mrid {VMATracking::SpecialDev::Anon, AnonSharedId++}; - auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); - LOGMAN_THROW_A_FMT(Inserted == true, "VMA tracking error"); + auto [Iter, IterEnd] = VMATracking.FindResources(mrid); + LOGMAN_THROW_A_FMT(Iter == IterEnd, "VMA tracking error"); + + Iter = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); Resource = &Iter->second; Resource->Iterator = Iter; } else { @@ -480,11 +486,12 @@ void SyscallHandler::TrackMremap(FEXCore::Core::InternalThreadState* Thread, uin void SyscallHandler::TrackShmat(FEXCore::Core::InternalThreadState* Thread, int shmid, uint64_t shmaddr, int shmflg, uint64_t Length) { VMATracking::MRID mrid {VMATracking::SpecialDev::SHM, static_cast(shmid)}; - auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, Length}); - auto Resource = &Iter->second; - if (Inserted) { - Resource->Iterator = Iter; + auto [Iter, IterEnd] = VMATracking.FindResources(mrid); + if (Iter == IterEnd) { + Iter = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, Length}); + Iter->second.Iterator = Iter; } + auto Resource = &Iter->second; VMATracking.TrackVMARange(CTX, Resource, shmaddr, 0, Length, VMATracking::VMAFlags::fromFlags(MAP_SHARED), VMATracking::VMAProt::fromSHM(shmflg)); } diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h index 87932c821..02185c862 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h @@ -30,9 +30,16 @@ struct MRID { struct VMAEntry; -// Used to all MAP_SHARED VMAs of a system resource. +/** + * Meta data associated to one system resource. + * + * Typically there is one instance of this type per ELF/PE file or special device. + * However if an ELF/PE file is mapped multiple times at different base addresses, + * there will be one separate MappedResource for each base address. The MRID + * is the same in this case. + */ struct MappedResource { - using ContainerType = fextl::map; + using ContainerType = fextl::multimap; fextl::unique_ptr MappedFile; // Pointer to lowest memory range this file is mapped to @@ -116,6 +123,14 @@ struct VMATracking { return MappedResources.emplace(mrid, std::move(Resource)); } + // Returns an iterator pair spanning the range of all MappedResources matching the given MRID. + // Typically there is only one associated resource, however sometimes the same file gets mapped + // multiple times at different base addresses. In that case, each MappedResource will cover an + // exclusive set of VMAEntries that refer to a consistent base mapping address. + inline auto FindResources(const MRID& mrid) { + return MappedResources.equal_range(mrid); + } + private: MappedResource::ContainerType MappedResources; }; From ad93f272712a2f2a9c7ee0c6a90a257194ed4005 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 15 Oct 2025 23:01:22 +0200 Subject: [PATCH 4/7] Common: Add helper function to find the base virtual address corresponding to an mmap() call --- Source/Common/FileMappingBaseAddress.h | 46 +++++++++++ unittests/APITests/CMakeLists.txt | 9 ++- unittests/APITests/FileMappingBaseAddress.cpp | 81 +++++++++++++++++++ 3 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 Source/Common/FileMappingBaseAddress.h create mode 100644 unittests/APITests/FileMappingBaseAddress.cpp diff --git a/Source/Common/FileMappingBaseAddress.h b/Source/Common/FileMappingBaseAddress.h new file mode 100644 index 000000000..bec3a19cc --- /dev/null +++ b/Source/Common/FileMappingBaseAddress.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MIT +#pragma once + +#include + +#include +#include +#include + +#include + +namespace FEXCore { + +/** + * Infers the base virtual address from a file mapping (as described by parameters to a single + * call to mmap()). + * + * The file offset of any given mapping need not match its virtual address offset from the base + * mapping (file offset = 0). Instead, this function searches the corresponding ELF program headers + * for an entry that generated the given file mapping. + */ +inline std::optional +InferMappingBaseAddress(std::span ProgramHeaders, uint64_t Addr, uint64_t Size, uint64_t FileOffset, int AccessFlags) { + for (auto& phdr : ProgramHeaders) { + if (phdr.p_type != PT_LOAD) { + // Skip headers that don't trigger memory mappings + continue; + } + + if ((phdr.p_flags & (PF_X | PF_W | PF_R)) != (AccessFlags & (PF_X | PF_W | PF_R))) { + continue; + } + + // The mapped file offset must be included at the start of the section header + auto SegmentStartOffset = phdr.p_offset - (phdr.p_vaddr & 0xfff); + if (FileOffset >= SegmentStartOffset && FileOffset < SegmentStartOffset + phdr.p_filesz && + (FileOffset & Utils::FEX_PAGE_MASK) == (phdr.p_offset & Utils::FEX_PAGE_MASK)) { + // Compute VA offset relative to the base mapping + return Addr - (phdr.p_vaddr - (phdr.p_offset & 0xfff)) + (ProgramHeaders[0].p_vaddr - (ProgramHeaders[0].p_offset & 0xfff)) - + (FileOffset - SegmentStartOffset); + } + } + + return std::nullopt; +} +} // namespace FEXCore diff --git a/unittests/APITests/CMakeLists.txt b/unittests/APITests/CMakeLists.txt index 260f1be47..1ae70cbf7 100644 --- a/unittests/APITests/CMakeLists.txt +++ b/unittests/APITests/CMakeLists.txt @@ -1,11 +1,12 @@ set (TESTS Allocator ArgumentParser - InterruptableConditionVariable - Filesystem - StringUtils - fextl_function ExtendedVolatileMetadata + fextl_function + FileMappingBaseAddress + Filesystem + InterruptableConditionVariable + StringUtils ) list(APPEND LIBS Common FEXCore JemallocLibs) diff --git a/unittests/APITests/FileMappingBaseAddress.cpp b/unittests/APITests/FileMappingBaseAddress.cpp new file mode 100644 index 000000000..65ef78b65 --- /dev/null +++ b/unittests/APITests/FileMappingBaseAddress.cpp @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +#include +#include + +#include + +namespace { + +struct Mapping { + uint64_t Addr; + uint64_t Size; + uint64_t FileOffset; + int Flags; // PF_* +}; + +} // anonymous namespace + +TEST_CASE("libm") { + uint64_t BaseAddr = 0x123400000; + + fextl::vector Headers = { + {.p_type = PT_LOAD, .p_offset = 0x00000, .p_vaddr = 0x00000, .p_paddr = 0x00000, .p_filesz = 0x7bdd5, .p_memsz = 0x7bdd5}, + {.p_type = PT_LOAD, .p_offset = 0x7c000, .p_vaddr = 0x7c000, .p_paddr = 0x7c000, .p_filesz = 0x6f3a8, .p_memsz = 0x6f3a8}, + {.p_type = PT_LOAD, .p_offset = 0xebbd0, .p_vaddr = 0xecbd0, .p_paddr = 0xecbd0, .p_filesz = 0x434, .p_memsz = 0x440}, + }; + + fextl::vector Mappings = { + {.Addr = BaseAddr, .Size = 0x7c000, .FileOffset = 0x00000}, + {.Addr = BaseAddr + 0x7c000, .Size = 0x70000, .FileOffset = 0x7c000}, + {.Addr = BaseAddr + 0xec000, .Size = 0x2000, .FileOffset = 0xeb000}, + }; + + for (auto& Mapping : Mappings) { + INFO("Mapping to 0x" << std::hex << Mapping.Addr << "-0x" << Mapping.Addr + Mapping.Size << " from file offset 0x" << Mapping.FileOffset); + auto DeducedBase = FEXCore::InferMappingBaseAddress(Headers, Mapping.Addr, Mapping.Size, Mapping.FileOffset, Mapping.Flags); + CHECK(DeducedBase.value_or(0) == BaseAddr); + } +} + +// E.g. libX11-xcb +TEST_CASE("Access flags are checked") { + uint64_t BaseAddr = 0x123400000; + + fextl::vector Headers = { + {.p_type = PT_LOAD, .p_flags = PF_R | PF_X, .p_offset = 0x0000, .p_vaddr = 0x0000, .p_paddr = 0x0000, .p_filesz = 0x00040d, .p_memsz = 0x00040d}, + {.p_type = PT_LOAD, .p_flags = PF_R, .p_offset = 0x1000, .p_vaddr = 0x1000, .p_paddr = 0x1000, .p_filesz = 0x00036c, .p_memsz = 0x00036c}, + {.p_type = PT_LOAD, .p_flags = PF_W, .p_offset = 0x1dc8, .p_vaddr = 0x2dc8, .p_paddr = 0x2dc8, .p_filesz = 0x000238, .p_memsz = 0x000240}, + }; + + fextl::vector Mappings = { + {.Addr = BaseAddr + 0x1000, .Size = 0x1000, .FileOffset = 0x1000, .Flags = PF_R}, + {.Addr = BaseAddr + 0x2000, .Size = 0x1000, .FileOffset = 0x1000, .Flags = PF_W}, + }; + + for (auto& Mapping : Mappings) { + INFO("Mapping to 0x" << std::hex << Mapping.Addr << "-0x" << Mapping.Addr + Mapping.Size << " from file offset 0x" << Mapping.FileOffset); + auto DeducedBase = FEXCore::InferMappingBaseAddress(Headers, Mapping.Addr, Mapping.Size, Mapping.FileOffset, Mapping.Flags); + CHECK(DeducedBase.value_or(0) == BaseAddr); + } +} + +// Program headers that don't generate memory mappings can't be used to infer base addresses +TEST_CASE("Non-mapping program headers are ignored") { + uint64_t BaseAddr = 0x123400000; + + fextl::vector Headers = { + {.p_type = PT_LOAD, .p_offset = 0x00000, .p_vaddr = 0x0000, .p_paddr = 0x00000, .p_filesz = 0x1000, .p_memsz = 0x1000}, + {.p_type = PT_INTERP, .p_offset = 0x10000, .p_vaddr = 0xa000, .p_paddr = 0xa0000, .p_filesz = 0x1000, .p_memsz = 0x1000}, + {.p_type = PT_LOAD, .p_offset = 0x10000, .p_vaddr = 0x1000, .p_paddr = 0x10000, .p_filesz = 0x1000, .p_memsz = 0x1000}, + }; + + fextl::vector Mappings = { + {.Addr = BaseAddr + 0x1000, .Size = 0x1000, .FileOffset = 0x10000}, + }; + + for (auto& Mapping : Mappings) { + INFO("Mapping to 0x" << std::hex << Mapping.Addr << "-0x" << Mapping.Addr + Mapping.Size << " from file offset 0x" << Mapping.FileOffset); + auto DeducedBase = FEXCore::InferMappingBaseAddress(Headers, Mapping.Addr, Mapping.Size, Mapping.FileOffset, Mapping.Flags); + CHECK(DeducedBase.value_or(0) == BaseAddr); + } +} From cf6c0765faa04cedcf2ca43fb349ecb2326e77e8 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 15 Oct 2025 23:03:18 +0200 Subject: [PATCH 5/7] LinuxSyscalls: Create separated MappedResources for re-mappings of the same ELF file PE/ELF binaries are sometimes mapped multiple times in the same process. If this happens, there is no longer a unique base virtual address per file. This breaks assumptions required for code caching: Any time a file mapping is created, FEX must be able to unambiguously determine the base virtual address of the mapped library. This becomes possible by creating a separate MappedResource each time an ELF header is re-mapped. --- .../LinuxSyscalls/SyscallsSMCTracking.cpp | 50 +++++++++++++++++-- .../LinuxSyscalls/SyscallsVMATracking.h | 4 ++ 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 3e2c914ce..3cfb9f0ac 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -8,6 +8,7 @@ $end_info$ */ #include "Common/FDUtils.h" +#include "Common/FileMappingBaseAddress.h" #include #include @@ -23,6 +24,7 @@ $end_info$ #include #include #include +#include namespace FEX::HLE { // SMC interactions @@ -195,6 +197,22 @@ FEXCore::HLE::ExecutableRangeInfo SyscallHandler::QueryGuestExecutableRange(FEXC return {Entry->first, Entry->second.Length, Entry->second.Prot.Writable}; } +static fextl::vector ReadELFHeaders(int FD, std::span HeaderData = {}) { + std::string_view ELFMagic = ELFMAG; + if (HeaderData.data()) { + if (HeaderData.size_bytes() < ELFMagic.size() || std::memcmp(ELFMagic.data(), HeaderData.data(), ELFMagic.size()) != 0) { + // Not an ELF file + return {}; + } + } else { + // Read from FD in case the caller didn't have a mapped header available + } + + ELFParser Parser; + Parser.ReadElf(dup(FD)); + return std::move(Parser.phdrs); +} + void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState* Thread, void* addr, size_t length, int prot, int flags, int fd, off_t offset) { LOGMAN_THROW_A_FMT(Is64Bit || (length >> 32) == 0, "values must fit to 32 bits"); @@ -383,15 +401,17 @@ std::optional SyscallHandler: if (!(flags & MAP_ANONYMOUS)) { struct stat64 buf; fstat64(fd, &buf); - VMATracking::MRID mrid {buf.st_dev, buf.st_ino}; + + const VMATracking::MRID mrid {buf.st_dev, buf.st_ino}; char Tmp[PATH_MAX]; auto PathLength = FEX::get_fdpath(fd, Tmp); auto [ResourceIt, ResourceEnd] = VMATracking.FindResources(mrid); bool Inserted = false; - if (ResourceIt == ResourceEnd) { - // Create a new MappedResource for previously unseen file + const bool MappedELFHeaderAgain = ResourceIt != ResourceEnd && offset == 0 && !ResourceIt->second.ProgramHeaders.empty(); + if (ResourceIt == ResourceEnd || MappedELFHeaderAgain) { + // Create a new MappedResource for previously unseen file and for re-mappings of an ELF header ResourceIt = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); ResourceIt->second.Iterator = ResourceIt; Inserted = true; @@ -400,9 +420,31 @@ std::optional SyscallHandler: // Only handle FDs that are backed by regular files that are executable if (PathLength != -1 && S_ISREG(buf.st_mode) && (buf.st_mode & S_IXUSR)) { + // ELF files that are mapped multiple times get a separate MappedResource for each base virtual address if (Inserted) { Resource->MappedFile = fextl::make_unique(); Resource->MappedFile->Filename = fextl::string(Tmp, PathLength); + + // Read ELF headers if applicable + Resource->ProgramHeaders = ReadELFHeaders(fd, std::span {reinterpret_cast(addr), length}); + // If this assumption is broken, we can't reliably cluster subsequent mappings by their base address + LOGMAN_THROW_A_FMT(Resource->ProgramHeaders.empty() || offset == 0, "Expected file offset 0 for the first mapping of an ELF " + "file"); + } else if (ResourceIt->second.ProgramHeaders.empty()) { + // Not an ELF file, so we don't need to distinguish between different base addresses + } else { + // Mapped a non-header section of an ELF file. + // Look up the corresponding MappedResource using the expected base address. + + ResourceIt = std::find_if(ResourceIt, ResourceEnd, [&](const VMATracking::MappedResource::ContainerType::value_type& ResourcePair) { + auto& Resource = ResourcePair.second; + auto ExpectedBase = FEXCore::InferMappingBaseAddress( + Resource.ProgramHeaders, addr, Size, offset, + (ProtMapping.Executable ? PF_X : 0) | (ProtMapping.Writable ? PF_W : 0) | (ProtMapping.Readable ? PF_R : 0)); + return ExpectedBase == Resource.FirstVMA->Base; + }); + LOGMAN_THROW_A_FMT(ResourceIt != ResourceEnd, "ERROR: Could not find base for file mapping at {:#x} (offset {:#x})", addr, offset); + Resource = &ResourceIt->second; } const fextl::string Filename = FHU::Filesystem::GetFilename(Resource->MappedFile->Filename); @@ -429,8 +471,6 @@ std::optional SyscallHandler: Iter = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0}); Resource = &Iter->second; Resource->Iterator = Iter; - } else { - Resource = nullptr; } VMATracking.TrackVMARange(CTX, Resource, addr, offset, Size, VMATracking::VMAFlags::fromFlags(flags), ProtMapping); diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h index 02185c862..0c8ebd665 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsVMATracking.h @@ -8,6 +8,8 @@ #include #include +#include + namespace FEX::HLE::VMATracking { ///// VMA (Virtual Memory Area) tracking ///// @@ -46,6 +48,8 @@ struct MappedResource { VMAEntry* FirstVMA; uint64_t Length; // 0 if not fixed size ContainerType::iterator Iterator; + + fextl::vector ProgramHeaders; }; union VMAProt { From 2cfba8c6d02a4b30912cee7a3e0946656be2e389 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Wed, 15 Oct 2025 22:34:52 +0200 Subject: [PATCH 6/7] LinuxSyscalls: Fix LookupExecutableFileSection implementation The file offset of a file mapping doesn't necessarily match its address offset in virtual memory from the base file mapping. Indeed, most libraries violate this assumption. Now that the MappedResource::FirstVMA reliably identifies the base memory mapping for a given library (even when that library is mapped multiple times), this can easily be fixed. --- .../LinuxSyscalls/SyscallsSMCTracking.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 3cfb9f0ac..43da1c512 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -174,15 +174,13 @@ std::optional SyscallHandler::LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) { auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, &Thread); - // Get the first mapping after GuestAddr, or end - // GuestAddr is inclusive - // If the write spans two pages, they will be flushed one at a time (generating two faults) - auto Entry = VMATracking.FindVMAEntry(GuestAddr); - if (Entry == VMATracking.VMAs.end() || !Entry->second.Resource) { + auto EntryIt = VMATracking.FindVMAEntry(GuestAddr); + if (EntryIt == VMATracking.VMAs.end() || !EntryIt->second.Resource) { return std::nullopt; } - return FEXCore::ExecutableFileSectionInfo {*Entry->second.Resource->MappedFile, Entry->second.Base - Entry->second.Offset}; + auto& [MappingBaseAddr, Entry] = *EntryIt; + return FEXCore::ExecutableFileSectionInfo {*Entry.Resource->MappedFile, Entry.Resource->FirstVMA->Base}; } FEXCore::HLE::ExecutableRangeInfo SyscallHandler::QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) { From 32ddf790d5bf7219cfcdbf25a207635536378c6f Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 17 Oct 2025 15:48:11 +0200 Subject: [PATCH 7/7] LinuxSyscalls: Skip ELF checks for non-header mappings The full consistency check is only ran in assertion builds now. --- .../LinuxSyscalls/SyscallsSMCTracking.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index 43da1c512..ea4fbb53a 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -423,11 +423,17 @@ std::optional SyscallHandler: Resource->MappedFile = fextl::make_unique(); Resource->MappedFile->Filename = fextl::string(Tmp, PathLength); - // Read ELF headers if applicable - Resource->ProgramHeaders = ReadELFHeaders(fd, std::span {reinterpret_cast(addr), length}); - // If this assumption is broken, we can't reliably cluster subsequent mappings by their base address - LOGMAN_THROW_A_FMT(Resource->ProgramHeaders.empty() || offset == 0, "Expected file offset 0 for the first mapping of an ELF " - "file"); + // Read ELF headers if applicable. + // For performance, skip ELF checks if we're not mapping the file header + bool CheckForElfFile = (offset == 0); +#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED + CheckForElfFile = true; +#endif + if (CheckForElfFile) { + Resource->ProgramHeaders = ReadELFHeaders(fd, std::span {reinterpret_cast(addr), length}); + LOGMAN_THROW_A_FMT(Resource->ProgramHeaders.empty() || offset == 0, "Expected file offset 0 for the first mapping of an ELF " + "file"); + } } else if (ResourceIt->second.ProgramHeaders.empty()) { // Not an ELF file, so we don't need to distinguish between different base addresses } else {