diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 288dddef6..5514279eb 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 60da5e79f..615c7c774 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/linux/LinuxElfPatcher.cpp b/core/relinker/elfpatcher/src/linux/LinuxElfPatcher.cpp index 24746a4a1..1db4b2741 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 d631eea28..162d25956 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 000000000..fc878aefc --- /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("