fix(relinker): preserve executable exports and dynamic lookup tags
This commit is contained in:
parent
7ea423f78b
commit
ded8b4df23
|
|
@ -243,6 +243,8 @@ if(BUILD_TESTING)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_guest_runpath.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME executable_segments COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_executable_segments.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME linux_executable_exports COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_executable_exports.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME linux_entry_argv COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_linux_entry_argv.py $<TARGET_FILE:relinker>)
|
||||
add_test(NAME linux_program_header_order COMMAND ${Python3_EXECUTABLE}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <elfpatcher/general/SectionHeaderTableRequest.hpp>
|
||||
#include <codegen/x86/Amd64OnlySubstitutionTable.hpp>
|
||||
#include <codegen/x86/StubBodyBuilder.hpp>
|
||||
#include <io/BufferUtils.hpp>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
|
|
@ -112,6 +113,15 @@ std::vector<std::uint8_t> LinuxElfPatcher::Patch(
|
|||
buf.push_back(b);
|
||||
alignBuf(buf, kDynSymAlignment);
|
||||
|
||||
const auto hashOff = static_cast<std::uint64_t>(buf.size());
|
||||
const auto symbolCount = static_cast<std::uint32_t>(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<std::uint64_t>(buf.size());
|
||||
for (std::uint8_t b : dynSection.RelaData)
|
||||
buf.push_back(b);
|
||||
|
|
@ -138,6 +148,8 @@ std::vector<std::uint8_t> 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());
|
||||
|
|
|
|||
|
|
@ -294,6 +294,28 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& 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<std::uint32_t>(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<std::uint8_t>& buf, std::uint64_t offset, std::uint64_t info, std::int64_t addend) {
|
||||
std::size_t pos = buf.size();
|
||||
buf.resize(pos + 24);
|
||||
|
|
|
|||
|
|
@ -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 <dlfcn.h>\n"
|
||||
"#include <unistd.h>\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("<IBBHQQ", image, 0x4900, 0, 0, 0, 0, 0, 0)
|
||||
struct.pack_into("<IBBHQQ", image, 0x4918, 1, 0x12, 0, 1, 0x210, 6)
|
||||
tags = [(5, 0x600), (10, len(strings)), (6, 0x900), (11, 24), (7, 0x700), (8, 0), (9, 24), (0x6100003f, 48), (1, 32), (0, 0)]
|
||||
struct.pack_into("<IIQQQQQQ", image, 120, 2, 6, 0x4800, 0x800, 0x800, len(tags) * 16, len(tags) * 16, 8)
|
||||
for index, tag in enumerate(tags):
|
||||
struct.pack_into("<qQ", image, 0x4800 + index * 16, *tag)
|
||||
source = work / "input.elf"
|
||||
source.write_bytes(image)
|
||||
output = work / "output.elf"
|
||||
result = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(output)], capture_output=True, text=True)
|
||||
assert result.returncode == 0, (result.stdout, result.stderr)
|
||||
output.chmod(0o755)
|
||||
executed = subprocess.run([str(output)], capture_output=True)
|
||||
assert executed.returncode == 42, (executed.returncode, executed.stderr)
|
||||
struct.pack_into("<Q", image, 0x4800 + 7 * 16 + 8, 47)
|
||||
source.write_bytes(image)
|
||||
invalid = subprocess.run([str(relinker), "--skip-sce-module", str(source), str(work / "invalid.elf")], capture_output=True, text=True)
|
||||
assert invalid.returncode == 2 and "dynamic symbol table size" in invalid.stderr, invalid.stderr
|
||||
print("Linux executable exports test passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue