diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 288dddef6968..5514279eb92d 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -243,6 +243,8 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_runpath.py $) add_test(NAME executable_segments COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_executable_segments.py $) + add_test(NAME linux_executable_exports COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_executable_exports.py $) add_test(NAME linux_entry_argv COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_entry_argv.py $) add_test(NAME linux_program_header_order COMMAND ${Python3_EXECUTABLE} diff --git a/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp b/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp index 60da5e79f91d..615c7c7742d3 100644 --- a/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp +++ b/core/relinker/elfpatcher/include/elfpatcher/general/ElfConstants.hpp @@ -76,10 +76,12 @@ inline constexpr std::uint32_t PT_OS_RELRO = 0x61000010; inline constexpr std::uint32_t PT_LOOS = 0x61000000; inline constexpr std::uint32_t PT_HIOS = 0x6fffffff; +inline constexpr std::int64_t DT_DEBUG = 21; inline constexpr std::int64_t DT_NULL = 0; inline constexpr std::int64_t DT_NEEDED = 1; inline constexpr std::int64_t DT_PLTRELSZ = 2; inline constexpr std::int64_t DT_PLTGOT = 3; +inline constexpr std::int64_t DT_HASH = 4; inline constexpr std::int64_t DT_STRTAB = 5; inline constexpr std::int64_t DT_SYMTAB = 6; inline constexpr std::int64_t DT_RELA = 7; diff --git a/core/relinker/elfpatcher/src/general/SectionHeaderTableBuilder.cpp b/core/relinker/elfpatcher/src/general/SectionHeaderTableBuilder.cpp index c403a058e108..e9ee612df6d4 100644 --- a/core/relinker/elfpatcher/src/general/SectionHeaderTableBuilder.cpp +++ b/core/relinker/elfpatcher/src/general/SectionHeaderTableBuilder.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include namespace Elfpatcher { @@ -38,6 +40,21 @@ void SectionHeaderTableBuilder::WriteTable( std::vector& buf, const SectionHeaderTableRequest& request ) const { + const auto phOffset = Io::ReadU64(buf, kEhdrPhOffOffset); + const auto phCount = Io::ReadU16(buf, kEhdrPhNumOffset); + const auto phStride = Io::ReadU16(buf, kEhdrPhEntSizeOffset); + const auto mappedAddress = [&](std::uint64_t offset) { + for (std::uint16_t index = 0; index < phCount; ++index) { + const auto header = phOffset + index * phStride; + if (Io::ReadU32(buf, header) != PT_LOAD) continue; + const auto start = Io::ReadU64(buf, header + 8); + const auto size = Io::ReadU64(buf, header + 32); + if (offset >= start && offset - start < size) + return Io::ReadU64(buf, header + 16) + offset - start; + } + throw std::runtime_error("Allocated output section is outside load segments"); + }; + const auto shStrTabOff = static_cast(buf.size()); constexpr std::uint64_t shStrTabSize = sizeof(kShStrTabBlob); for (const char i : kShStrTabBlob) @@ -48,10 +65,10 @@ void SectionHeaderTableBuilder::WriteTable( _writeSectionHeader(buf, shOff + kShdrEntrySize * 0, 0, SHT_NULL, 0, 0, 0, 0, 0, 0, 0, 0); _writeSectionHeader(buf, shOff + kShdrEntrySize * 1, kShStrTabNameOffset, SHT_STRTAB, 0, shStrTabOff, shStrTabOff, shStrTabSize, 0, 0, kNoSpecialAlignment, 0); - _writeSectionHeader(buf, shOff + kShdrEntrySize * 2, kDynStrNameOffset, SHT_STRTAB, SHF_ALLOC, request.DynStrOffset, request.DynStrOffset, request.DynStrSize, 0, 0, kNoSpecialAlignment, 0); - _writeSectionHeader(buf, shOff + kShdrEntrySize * 3, kDynSymNameOffset, SHT_DYNSYM, SHF_ALLOC, request.DynSymOffset, request.DynSymOffset, request.DynSymSize, kDynSymLinkToStrTab, kDynSymInfoFirstGlobal, kDynSymAlign, kSymEntrySize); - _writeSectionHeader(buf, shOff + kShdrEntrySize * 4, kDynamicNameOffset, SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE, request.DynamicSegmentOffset, request.DynamicSegmentOffset, request.DynamicSegmentSize, kDynamicLinkToStrTab, 0, kDynSymAlign, kDynEntrySize); - _writeSectionHeader(buf, shOff + kShdrEntrySize * 5, kTextNameOffset, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, request.StubOffset, request.StubOffset, request.StubSize, 0, 0, kNoSpecialAlignment, 0); + _writeSectionHeader(buf, shOff + kShdrEntrySize * 2, kDynStrNameOffset, SHT_STRTAB, SHF_ALLOC, mappedAddress(request.DynStrOffset), request.DynStrOffset, request.DynStrSize, 0, 0, kNoSpecialAlignment, 0); + _writeSectionHeader(buf, shOff + kShdrEntrySize * 3, kDynSymNameOffset, SHT_DYNSYM, SHF_ALLOC, mappedAddress(request.DynSymOffset), request.DynSymOffset, request.DynSymSize, kDynSymLinkToStrTab, kDynSymInfoFirstGlobal, kDynSymAlign, kSymEntrySize); + _writeSectionHeader(buf, shOff + kShdrEntrySize * 4, kDynamicNameOffset, SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE, mappedAddress(request.DynamicSegmentOffset), request.DynamicSegmentOffset, request.DynamicSegmentSize, kDynamicLinkToStrTab, 0, kDynSymAlign, kDynEntrySize); + _writeSectionHeader(buf, shOff + kShdrEntrySize * 5, kTextNameOffset, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, mappedAddress(request.StubOffset), request.StubOffset, request.StubSize, 0, 0, kNoSpecialAlignment, 0); _byteWriter->WriteU64(buf, kEhdrShOffOffset, shOff); _byteWriter->WriteU16(buf, kEhdrShEntSizeOffset, static_cast(kShdrEntrySize)); diff --git a/core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp b/core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp index 24746a4a18c9..1db4b27413d7 100644 --- a/core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp +++ b/core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,15 @@ std::vector LinuxElfPatcher::Patch( buf.push_back(b); alignBuf(buf, kDynSymAlignment); + const auto hashOff = static_cast(buf.size()); + const auto symbolCount = static_cast(dynSection.DynSymData.size() / kSymEntrySize); + Io::AppendU32(buf, 1); + Io::AppendU32(buf, symbolCount); + Io::AppendU32(buf, symbolCount > 1 ? 1 : 0); + for (std::uint32_t index = 0; index < symbolCount; ++index) + Io::AppendU32(buf, index != 0 && index + 1 < symbolCount ? index + 1 : 0); + alignBuf(buf, kDynSymAlignment); + const auto relaOff = static_cast(buf.size()); for (std::uint8_t b : dynSection.RelaData) buf.push_back(b); @@ -138,6 +148,8 @@ std::vector LinuxElfPatcher::Patch( _appendDynEntry(dynSegBuf, DT_STRSZ, dynStrSize); _appendDynEntry(dynSegBuf, DT_SYMTAB, vaddrOfExtraBlockOffset(dynSymOff)); _appendDynEntry(dynSegBuf, DT_SYMENT, kSymEntrySize); + _appendDynEntry(dynSegBuf, DT_HASH, vaddrOfExtraBlockOffset(hashOff)); + _appendDynEntry(dynSegBuf, DT_DEBUG, 0); if (!dynSection.RelaData.empty()) { _appendDynEntry(dynSegBuf, DT_RELA, vaddrOfExtraBlockOffset(relaOff)); _appendDynEntry(dynSegBuf, DT_RELASZ, dynSection.RelaData.size()); diff --git a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp index d631eea28f5f..162d2595675c 100644 --- a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp +++ b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp @@ -294,6 +294,28 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf } auto dynSection = _dynamicSectionBuilder->BuildDynamicSection(dynamicRefs, neededLibraries, dynJmpRelOffset, pltCount); + if (hasTag(DT_OS_SYMTABSZ)) { + const auto symbolBytes = getTagValue(DT_OS_SYMTABSZ); + if (symbolBytes % symEntSize != 0 || dynSymTabOffset > raw.size() || symbolBytes > raw.size() - dynSymTabOffset) + throw RelinkerException("Invalid executable dynamic symbol table size"); + for (ByteCount off = symEntSize; off < symbolBytes; off += symEntSize) { + const auto* symbol = raw.data() + dynSymTabOffset + off; + std::uint16_t section = 0; + std::memcpy(§ion, symbol + 6, sizeof(section)); + if (section == 0 || (symbol[4] >> 4) == 0 || (symbol[5] & 3) == 1 || (symbol[5] & 3) == 2) continue; + std::uint32_t sourceName = 0; + std::memcpy(&sourceName, symbol, sizeof(sourceName)); + auto name = readCStr(sourceName); + name.resize(name.find('#') == std::string::npos ? name.size() : name.find('#')); + const auto nameOffset = static_cast(dynSection.DynStrData.size()); + dynSection.DynStrData.insert(dynSection.DynStrData.end(), name.begin(), name.end()); + dynSection.DynStrData.push_back(0); + const auto position = dynSection.DynSymData.size(); + dynSection.DynSymData.insert(dynSection.DynSymData.end(), symbol, symbol + symEntSize); + std::memcpy(dynSection.DynSymData.data() + position, &nameOffset, sizeof(nameOffset)); + } + } + auto appendRela = [&](std::vector& buf, std::uint64_t offset, std::uint64_t info, std::int64_t addend) { std::size_t pos = buf.size(); buf.resize(pos + 24); diff --git a/core/relinker/relinker/tests/test_linux_executable_exports.py b/core/relinker/relinker/tests/test_linux_executable_exports.py new file mode 100644 index 000000000000..fc878aefcdd2 --- /dev/null +++ b/core/relinker/relinker/tests/test_linux_executable_exports.py @@ -0,0 +1,53 @@ +import os +from pathlib import Path +import platform +import struct +import subprocess +import sys +import tempfile +from test_linux_load_alignment import fixture + + +def main(): + if sys.platform != "linux" or platform.machine() != "x86_64": + return + relinker = Path(sys.argv[1]).resolve() + with tempfile.TemporaryDirectory(prefix="anyps5-main-export-") as directory: + work = Path(directory) + probe = work / "probe.c" + probe.write_text( + "#include \n" + "#include \n" + "void check(void) {\n" + " int (*function)(void) = dlsym(RTLD_DEFAULT, \"FixtureExport\");\n" + " _exit(function && function() == 42 ? 42 : 1);\n" + "}\n") + library = work / "probe.so" + subprocess.run([os.environ.get("CC", "cc"), "-shared", "-fPIC", str(probe), "-o", str(library), "-ldl", "-Wl,-init,check"], check=True) + image = fixture() + strings = b"\0FixtureExport#A#B\0".ljust(32, b"\0") + str(library).encode() + b"\0" + image[0x4600:0x4600 + len(strings)] = strings + image[0x4210:0x4216] = bytes.fromhex("b82a000000c3") + struct.pack_into("