Files
FEX-Emu--FEX/unittests/APITests/FileMappingBaseAddress.cpp
T
Tony Wasserka b62890f136 LinuxSyscalls: Fix incorrectly inferred base address observed in glxtest
At runtime, glxtest is mapped as follows:
0x000055fd9a030000 0x000055fd9a034000 0x4000  0x0     r--p  glxtest
0x000055fd9a034000 0x000055fd9a038000 0x4000  0x3000  r-xp  glxtest
0x000055fd9a038000 0x000055fd9a039000 0x1000  0x6000  rw-p  glxtest
0x000055fd9a039000 0x000055fd9a03a000 0x1000  0x6000  rw-p  glxtest

The problem here is that the last two sections can't be distinguished solely
by their mmap parameters. This would cause the wrong base address to be
inferred for the last mapping. To fix this, we can be more permissive by
allowing multiple candidates to be returned.

In practice, this only affects non-code sections, so it's not a big issue
either way.
2025-11-12 19:54:45 +01:00

112 lines
5.4 KiB
C++

// SPDX-License-Identifier: MIT
#include <catch2/catch_all.hpp>
#include <Common/FileMappingBaseAddress.h>
#include <FEXCore/fextl/vector.h>
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<Elf64_Phdr> 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<Mapping> 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 == fextl::vector<uint64_t> {BaseAddr});
}
}
// E.g. libX11-xcb
TEST_CASE("Access flags are checked") {
uint64_t BaseAddr = 0x123400000;
fextl::vector<Elf64_Phdr> 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<Mapping> 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 == fextl::vector<uint64_t> {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<Elf64_Phdr> 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<Mapping> 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 == fextl::vector<uint64_t> {BaseAddr});
}
}
// Some binaries have (e.g. glxtest) end up with two RW mappings at the end that trigger two mmap parameters that only differ in their
// virtual address. In such cases, multiple base addresses could be valid
TEST_CASE("Duplicate data page") {
uint64_t BaseAddr = 0x123400000;
fextl::vector<Elf64_Phdr> Headers = {
{.p_type = PT_LOAD, .p_flags = PF_R, .p_offset = 0x00000, .p_vaddr = 0x0000, .p_paddr = 0x00000, .p_filesz = 0x1000, .p_memsz = 0x1000},
{.p_type = PT_LOAD, .p_flags = PF_R | PF_X, .p_offset = 0x3910, .p_vaddr = 0x4910, .p_paddr = 0x4910, .p_filesz = 0x29d0, .p_memsz = 0x29d0},
{.p_type = PT_LOAD, .p_flags = PF_R | PF_W, .p_offset = 0x62e0, .p_vaddr = 0x82e0, .p_paddr = 0x82e0, .p_filesz = 0x6e0, .p_memsz = 0xd20},
{.p_type = PT_LOAD, .p_flags = PF_R | PF_W, .p_offset = 0x69c0, .p_vaddr = 0x99c0, .p_paddr = 0x99c0, .p_filesz = 0x018, .p_memsz = 0x068},
};
fextl::vector<Mapping> Mappings = {
{.Addr = BaseAddr, .Size = 0x4000, .FileOffset = 0x0000, .Flags = PF_R},
{.Addr = BaseAddr + 0x4000, .Size = 0x4000, .FileOffset = 0x3000, .Flags = PF_R | PF_X},
{.Addr = BaseAddr + 0x8000, .Size = 0x1000, .FileOffset = 0x6000, .Flags = PF_R | PF_W},
{.Addr = BaseAddr + 0x9000, .Size = 0x1000, .FileOffset = 0x6000, .Flags = PF_R | 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);
if (Mapping.Addr < BaseAddr + 0x8000) {
CHECK(DeducedBase == fextl::vector<uint64_t> {BaseAddr});
} else {
CHECK(DeducedBase == fextl::vector<uint64_t> {Mapping.Addr - 0x8000, Mapping.Addr - 0x9000});
}
}
}