Merge pull request #5262 from NaitikJoshiPro/pr/relinker-executable-tls
feat(relinker): executable TLS relocations (TPOFF64, DTPOFF64, DTPMOD64)
This commit is contained in:
commit
a1e1b4d161
|
|
@ -432,6 +432,8 @@ if(BUILD_TESTING)
|
|||
${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 executable_tls_relocations COMMAND ${Python3_EXECUTABLE}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_executable_tls_relocations.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}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ inline constexpr std::uint32_t PT_DYNAMIC = 2;
|
|||
inline constexpr std::uint32_t PT_INTERP = 3;
|
||||
inline constexpr std::uint32_t PT_NOTE = 4;
|
||||
inline constexpr std::uint32_t PT_PHDR = 6;
|
||||
inline constexpr std::uint32_t PT_TLS = 7;
|
||||
inline constexpr std::uint32_t PT_GNU_EH_FRAME = 0x6474e550;
|
||||
inline constexpr std::uint32_t PT_GNU_STACK = 0x6474e551;
|
||||
inline constexpr std::uint32_t PT_GNU_RELRO = 0x6474e552;
|
||||
|
|
@ -137,6 +138,9 @@ inline constexpr std::uint32_t R_X86_64_RELATIVE = 8;
|
|||
inline constexpr std::uint32_t R_X86_64_GOTPCREL = 9;
|
||||
inline constexpr std::uint32_t R_X86_64_32 = 10;
|
||||
inline constexpr std::uint32_t R_X86_64_32S = 11;
|
||||
inline constexpr std::uint32_t R_X86_64_DTPMOD64 = 16;
|
||||
inline constexpr std::uint32_t R_X86_64_DTPOFF64 = 17;
|
||||
inline constexpr std::uint32_t R_X86_64_TPOFF64 = 18;
|
||||
inline constexpr std::uint32_t R_X86_64_GOTPCRELX = 41;
|
||||
inline constexpr std::uint32_t R_X86_64_REX_GOTPCRELX = 42;
|
||||
|
||||
|
|
@ -144,6 +148,7 @@ inline constexpr std::uint8_t STB_GLOBAL = 1;
|
|||
inline constexpr std::uint8_t STB_WEAK = 2;
|
||||
inline constexpr std::uint8_t STT_NOTYPE = 0;
|
||||
inline constexpr std::uint8_t STT_FUNC = 2;
|
||||
inline constexpr std::uint8_t STT_TLS = 6;
|
||||
inline constexpr std::uint8_t STV_DEFAULT = 0;
|
||||
inline constexpr std::uint16_t SHN_UNDEF = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ namespace Elfpatcher::Windows {
|
|||
|
||||
class WindowsRelocationBuilder {
|
||||
public:
|
||||
PeRelocations Apply(WindowsLoadImage& image, const Domain::SysVDynamicSection& dynamicSection) const;
|
||||
PeRelocations Apply(WindowsLoadImage& image, const Domain::SysVDynamicSection& dynamicSection,
|
||||
const std::vector<Domain::ProgramHeader>& headers) const;
|
||||
std::vector<std::uint8_t> BuildBaseRelocations(const std::vector<std::uint32_t>& targets) const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ std::vector<std::uint8_t> MacOsMachOPatcher::Patch(const std::vector<std::uint8_
|
|||
Windows::WindowsLoadImage image(sourceElf, originalHeaders);
|
||||
if (originalPltGotVaddr != 0)
|
||||
image.GetRva(originalPltGotVaddr, 8);
|
||||
const auto relocations = Windows::WindowsRelocationBuilder().Apply(image, dynamicSection);
|
||||
const auto relocations = Windows::WindowsRelocationBuilder().Apply(image, dynamicSection, originalHeaders);
|
||||
|
||||
MacOsImageInput input;
|
||||
input.Executable = true;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ std::vector<std::uint8_t> WindowsPePatcher::Patch(const std::vector<std::uint8_t
|
|||
image.GetRva(originalPltGotVaddr, 8);
|
||||
report(500);
|
||||
const WindowsRelocationBuilder relocationBuilder;
|
||||
auto relocations = relocationBuilder.Apply(image, dynamicSection);
|
||||
auto relocations = relocationBuilder.Apply(image, dynamicSection, originalHeaders);
|
||||
report(1000);
|
||||
auto sections = image.BuildSections();
|
||||
report(1500);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
#include <elfpatcher/windows/WindowsRelocationBuilder.hpp>
|
||||
#include <elfpatcher/general/ElfConstants.hpp>
|
||||
#include <io/BufferUtils.hpp>
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace Elfpatcher::Windows {
|
||||
|
||||
PeRelocations WindowsRelocationBuilder::Apply(WindowsLoadImage& image, const Domain::SysVDynamicSection& dynamicSection) const {
|
||||
PeRelocations WindowsRelocationBuilder::Apply(WindowsLoadImage& image, const Domain::SysVDynamicSection& dynamicSection,
|
||||
const std::vector<Domain::ProgramHeader>& headers) const {
|
||||
if (dynamicSection.DynSymData.empty() || dynamicSection.DynSymData.size() % 24 != 0)
|
||||
throw Domain::RelinkerException("Invalid ELF dynamic symbol table size");
|
||||
PeRelocations result;
|
||||
|
|
@ -19,7 +23,7 @@ PeRelocations WindowsRelocationBuilder::Apply(WindowsLoadImage& image, const Dom
|
|||
const auto addend = Io::ReadU64(table, offset + 16);
|
||||
const auto type = static_cast<std::uint32_t>(info);
|
||||
const auto symbol = info >> 32;
|
||||
if ((plt && type != 7) || (type != 1 && type != 6 && type != 7 && type != 8))
|
||||
if ((plt && type != 7) || (type != 1 && type != 6 && type != 7 && type != 8 && type != 16 && type != 17 && type != 18))
|
||||
throw Domain::RelinkerException("Unsupported Windows relocation type " + std::to_string(type), target);
|
||||
const auto rva = image.GetRva(target, 8);
|
||||
const auto next = ranges.lower_bound(target);
|
||||
|
|
@ -33,6 +37,21 @@ PeRelocations WindowsRelocationBuilder::Apply(WindowsLoadImage& image, const Dom
|
|||
result.BaseRelocations.push_back(rva);
|
||||
continue;
|
||||
}
|
||||
if (type == 16 || type == 17 || type == 18) {
|
||||
if (symbol != 0)
|
||||
throw Domain::RelinkerException("Imported TLS relocation " + std::to_string(type) + " is not supported for this target", target);
|
||||
if (type == 16)
|
||||
throw Domain::RelinkerException("Executable DTPMOD64 relocation is not supported for this target", target);
|
||||
const auto tls = std::find_if(headers.begin(), headers.end(), [](const auto& header) { return header.Type == 7; });
|
||||
if (tls == headers.end() || tls->MemorySize == 0)
|
||||
throw Domain::RelinkerException("TLS relocation without a non-empty PT_TLS segment", target);
|
||||
const auto alignment = std::max<std::uint64_t>(tls->Alignment, 1);
|
||||
if (!std::has_single_bit(alignment) || tls->MemorySize > 0x7fff0000u)
|
||||
throw Domain::RelinkerException("Invalid or unsupported ELF TLS layout", tls->Offset);
|
||||
image.RequireWritable(target, 8);
|
||||
image.WritePointer(target, type == 17 ? addend : addend - Io::AlignUp64(tls->MemorySize, alignment));
|
||||
continue;
|
||||
}
|
||||
if (symbol == 0 || symbol >= dynamicSection.DynSymData.size() / 24)
|
||||
throw Domain::RelinkerException("Invalid import symbol index", target);
|
||||
const auto symbolOffset = static_cast<std::size_t>(symbol) * 24;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ void ValidationPolicy::_initializeSupportedRelocationTypes() {
|
|||
R_X86_64_GOTPCREL,
|
||||
R_X86_64_32,
|
||||
R_X86_64_32S,
|
||||
R_X86_64_DTPMOD64,
|
||||
R_X86_64_DTPOFF64,
|
||||
R_X86_64_TPOFF64,
|
||||
R_X86_64_GOTPCRELX,
|
||||
R_X86_64_REX_GOTPCRELX,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -84,7 +84,9 @@ SysVDynamicSection SysVDynamicSectionBuilder::BuildDynamicSection(
|
|||
|
||||
auto symbolInfo = [](const NidReference& ref) -> std::uint8_t {
|
||||
if (ref.Nid.empty()) return static_cast<std::uint8_t>((STB_WEAK << 4) | STT_NOTYPE);
|
||||
return static_cast<std::uint8_t>(((ref.Weak ? STB_WEAK : STB_GLOBAL) << 4) | STT_FUNC);
|
||||
const bool tls = ref.RelocationTypeValue == R_X86_64_DTPMOD64 || ref.RelocationTypeValue == R_X86_64_DTPOFF64
|
||||
|| ref.RelocationTypeValue == R_X86_64_TPOFF64;
|
||||
return static_cast<std::uint8_t>(((ref.Weak ? STB_WEAK : STB_GLOBAL) << 4) | (tls ? STT_TLS : STT_FUNC));
|
||||
};
|
||||
|
||||
static constexpr std::size_t kRelaEntSize = 24;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ std::string RelinkerPipeline::relocationTypeName(std::uint32_t type) {
|
|||
case 6: return "R_X86_64_GLOB_DAT";
|
||||
case 7: return "R_X86_64_JUMP_SLOT";
|
||||
case 10: return "R_X86_64_32";
|
||||
case 16: return "R_X86_64_DTPMOD64";
|
||||
case 17: return "R_X86_64_DTPOFF64";
|
||||
case 18: return "R_X86_64_TPOFF64";
|
||||
default: {
|
||||
std::ostringstream oss;
|
||||
oss << "UNKNOWN(" << type << ")";
|
||||
|
|
@ -272,6 +275,31 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
|||
return (raw[symOff + 4] & 0xf) == 3 && value == 0;
|
||||
};
|
||||
|
||||
const auto isTlsRelocation = [](const std::uint32_t type) {
|
||||
return type == R_X86_64_DTPMOD64 || type == R_X86_64_DTPOFF64 || type == R_X86_64_TPOFF64;
|
||||
};
|
||||
|
||||
const bool hasTlsSegment = std::any_of(programHeaders.begin(), programHeaders.end(),
|
||||
[](const ProgramHeader& ph) { return ph.Type == PT_TLS; });
|
||||
|
||||
const auto definedTlsSymbolValue = [&](const std::uint32_t symIdx, const FileByteOffset pos) -> std::optional<std::uint64_t> {
|
||||
const FileByteOffset symOff = dynSymTabOffset + static_cast<FileByteOffset>(symIdx) * symEntSize;
|
||||
if (symOff < dynSymTabOffset || symOff > raw.size() || raw.size() - symOff < symEntSize)
|
||||
throw RelinkerException("Symbol table entry out of bounds", symOff);
|
||||
std::uint16_t section = 0;
|
||||
std::memcpy(§ion, raw.data() + symOff + 6, sizeof(section));
|
||||
if (section == SHN_UNDEF)
|
||||
return std::nullopt;
|
||||
if ((raw[symOff + 4] & 15) != STT_TLS)
|
||||
throw RelinkerException("TLS relocation names a defined symbol that is not STT_TLS", pos);
|
||||
std::uint64_t value = 0;
|
||||
std::memcpy(&value, raw.data() + symOff + 8, sizeof(value));
|
||||
return value;
|
||||
};
|
||||
const auto definedTlsSymbol = [&](const std::uint32_t symIdx, const FileByteOffset pos) {
|
||||
return definedTlsSymbolValue(symIdx, pos).has_value();
|
||||
};
|
||||
|
||||
auto extractRela = [&](const FileByteOffset relaOff, const ByteCount relaSize) {
|
||||
for (ByteCount off = 0; relaSize >= relaEntSize && off <= relaSize - relaEntSize; off += relaEntSize) {
|
||||
const FileByteOffset pos = relaEntryPos(relaOff, off);
|
||||
|
|
@ -294,6 +322,9 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
|||
continue;
|
||||
}
|
||||
|
||||
if (isTlsRelocation(relType) && (symIdx == 0 || definedTlsSymbol(symIdx, pos)))
|
||||
continue;
|
||||
|
||||
const FileByteOffset symOff = dynSymTabOffset + static_cast<FileByteOffset>(symIdx) * symEntSize;
|
||||
if (symOff < dynSymTabOffset || symOff > raw.size() || raw.size() - symOff < 5)
|
||||
throw RelinkerException("Symbol table entry out of bounds", symOff);
|
||||
|
|
@ -457,7 +488,7 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
|||
std::memcpy(buf.data() + pos + 16, &addend, 8);
|
||||
};
|
||||
|
||||
auto extractRelative = [&](const FileByteOffset relaOff, const ByteCount relaSize) {
|
||||
auto extractLocal = [&](const FileByteOffset relaOff, const ByteCount relaSize) {
|
||||
for (ByteCount off = 0; relaSize >= relaEntSize && off <= relaSize - relaEntSize; off += relaEntSize) {
|
||||
const FileByteOffset pos = relaEntryPos(relaOff, off);
|
||||
std::uint64_t rOffset = 0, rInfo = 0;
|
||||
|
|
@ -465,14 +496,32 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
|||
std::memcpy(&rOffset, raw.data() + pos, 8);
|
||||
std::memcpy(&rInfo, raw.data() + pos + 8, 8);
|
||||
std::memcpy(&rAddend, raw.data() + pos + 16, 8);
|
||||
const std::uint32_t symIdx = static_cast<std::uint32_t>(rInfo >> 32);
|
||||
const std::uint32_t relType = static_cast<std::uint32_t>(rInfo & 0xffffffff);
|
||||
if (relType == R_X86_64_RELATIVE)
|
||||
if (relType == R_X86_64_RELATIVE) {
|
||||
appendRela(dynSection.RelaData, rOffset, static_cast<std::uint64_t>(R_X86_64_RELATIVE), rAddend);
|
||||
continue;
|
||||
}
|
||||
if (!isTlsRelocation(relType))
|
||||
continue;
|
||||
std::uint64_t offset = 0;
|
||||
if (symIdx != 0) {
|
||||
const auto value = definedTlsSymbolValue(symIdx, pos);
|
||||
if (!value)
|
||||
continue;
|
||||
offset = *value;
|
||||
}
|
||||
if (!hasTlsSegment)
|
||||
throw RelinkerException("TLS relocation in an executable without a PT_TLS segment", pos);
|
||||
if (relType == R_X86_64_DTPMOD64 && rAddend != 0)
|
||||
throw RelinkerException("DTPMOD64 relocation has a nonzero addend", pos);
|
||||
const auto addend = relType == R_X86_64_DTPMOD64 ? 0 : static_cast<std::int64_t>(offset + static_cast<std::uint64_t>(rAddend));
|
||||
appendRela(dynSection.RelaData, rOffset, static_cast<std::uint64_t>(relType), addend);
|
||||
}
|
||||
};
|
||||
|
||||
extractRelative(dynRelaOffset, dynRelaSize);
|
||||
extractRelative(dynJmpRelOffset, dynJmpRelSize);
|
||||
extractLocal(dynRelaOffset, dynRelaSize);
|
||||
extractLocal(dynJmpRelOffset, dynJmpRelSize);
|
||||
|
||||
std::vector<CallRegistryEntry> entries;
|
||||
entries.reserve(nidRefs.size());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
"""Initial-exec TLS relocations in the executable (R_X86_64_TPOFF64, DTPOFF64, DTPMOD64), as a PS5 eboot
|
||||
has them in its GOT. The Linux ELF keeps them dynamic and symbolless, with the variable's offset in the
|
||||
TLS block as the addend, for the dynamic loader to resolve against where it puts the block; a named one
|
||||
against a defined STT_TLS symbol is folded the same way, and one against an import stays named, typed
|
||||
STT_TLS. The Windows and macOS images resolve TPOFF64 and DTPOFF64 statically against their block,
|
||||
AlignUp(p_memsz, p_align) below the thread pointer, and reject what they cannot express."""
|
||||
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from test_linux_load_alignment import fixture
|
||||
from test_macos_output import MachO
|
||||
from test_tls_function_coverage import pe_bytes_at
|
||||
|
||||
PT_LOAD = 1
|
||||
PT_TLS = 7
|
||||
DTPMOD64, DTPOFF64, TPOFF64, TPOFF32 = 16, 17, 18, 23
|
||||
TLS_MEMSZ, TLS_ALIGN = 0x24, 16
|
||||
TLS_BLOCK = 0x30
|
||||
STRINGS = b"\0Tls\0Data\0ImportedTls\0"
|
||||
TLS_NAME, DATA_NAME, IMPORT_NAME = 1, 5, 10
|
||||
|
||||
NULL, TLS_SYMBOL, DATA_SYMBOL, IMPORTED = 0, 1, 2, 3
|
||||
|
||||
|
||||
def image(relocations, tls=True):
|
||||
"""The load-alignment fixture with a writable segment (vaddr 0x1000) holding a GOT and an 8-byte
|
||||
TLS template, a symbol table and the given (offset, type, symbol, addend) relocations."""
|
||||
data = fixture()
|
||||
struct.pack_into("<Q", data, 24, 0x200)
|
||||
data[0x4200:0x4206] = b"\xb8\x2a\x00\x00\x00\xc3"
|
||||
struct.pack_into("<IIQQQQQQ", data, 64 + 2 * 56, PT_LOAD, 6, 0x5000, 0x1000, 0x1000, 0x1000, 0x1000, 0x4000)
|
||||
if tls:
|
||||
struct.pack_into("<IIQQQQQQ", data, 64 + 3 * 56, PT_TLS, 4, 0x5800, 0x1800, 0x1800, 8, TLS_MEMSZ, TLS_ALIGN)
|
||||
data[0x5800:0x5808] = bytes(range(1, 9))
|
||||
data[0x4600:0x4600 + len(STRINGS)] = STRINGS
|
||||
struct.pack_into("<IBBHQQ", data, 0x4900 + TLS_SYMBOL * 24, TLS_NAME, 0x16, 0, 1, 0x10, 4)
|
||||
struct.pack_into("<IBBHQQ", data, 0x4900 + DATA_SYMBOL * 24, DATA_NAME, 0x11, 0, 1, 0x1100, 8)
|
||||
struct.pack_into("<IBBHQQ", data, 0x4900 + IMPORTED * 24, IMPORT_NAME, 0x16, 0, 0, 0, 0)
|
||||
for index, (offset, kind, symbol, addend) in enumerate(relocations):
|
||||
struct.pack_into("<QQq", data, 0x4700 + index * 24, offset, symbol << 32 | kind, addend)
|
||||
tags = [(5, 0x600), (10, len(STRINGS)), (6, 0x900), (11, 24), (7, 0x700), (8, len(relocations) * 24), (9, 24), (0, 0)]
|
||||
struct.pack_into("<IIQQQQQQ", data, 120, 2, 6, 0x4800, 0x800, 0x800, len(tags) * 16, len(tags) * 16, 8)
|
||||
for index, tag in enumerate(tags):
|
||||
struct.pack_into("<qQ", data, 0x4800 + index * 16, *tag)
|
||||
return data
|
||||
|
||||
|
||||
def relink(relinker, directory, name, data, *flags):
|
||||
source = Path(directory) / (name + ".elf")
|
||||
output = Path(directory) / (name + ".out")
|
||||
source.write_bytes(data)
|
||||
result = subprocess.run([str(relinker), "--skip-sce-module", *flags, str(source), str(output)],
|
||||
capture_output=True, text=True, timeout=20)
|
||||
return result, output
|
||||
|
||||
|
||||
def dynamic_relocations(elf):
|
||||
"""The output's (offset, type, symbol name, symbol type, addend) relocations from DT_RELA."""
|
||||
phoff, = struct.unpack_from("<Q", elf, 0x20)
|
||||
phentsize, phnum = struct.unpack_from("<HH", elf, 0x36)
|
||||
headers = [struct.unpack_from("<IIQQQQQQ", elf, phoff + index * phentsize) for index in range(phnum)]
|
||||
|
||||
def offset(address):
|
||||
for kind, _, file_offset, vaddr, _, size, _, _ in headers:
|
||||
if kind == PT_LOAD and vaddr <= address < vaddr + size:
|
||||
return file_offset + address - vaddr
|
||||
raise AssertionError(f"unmapped address {address:#x}")
|
||||
|
||||
dynamic = next(header for header in headers if header[0] == 2)
|
||||
tags = {}
|
||||
for position in range(dynamic[2], dynamic[2] + dynamic[5], 16):
|
||||
tag, value = struct.unpack_from("<qQ", elf, position)
|
||||
if tag == 0:
|
||||
break
|
||||
tags.setdefault(tag, value)
|
||||
symbols, strings = offset(tags[6]), offset(tags[5])
|
||||
result = []
|
||||
for position in range(offset(tags[7]), offset(tags[7]) + tags[8], 24):
|
||||
target, info, addend = struct.unpack_from("<QQq", elf, position)
|
||||
name, symbol_info = struct.unpack_from("<IB", elf, symbols + (info >> 32) * 24)
|
||||
end = elf.index(b"\0", strings + name)
|
||||
result.append((target, info & 0xffffffff, elf[strings + name:end].decode(), symbol_info & 15, addend))
|
||||
return result
|
||||
|
||||
|
||||
def macho_bytes_at(data, address, size):
|
||||
for segment in MachO(data).segments:
|
||||
if segment["vmaddr"] <= address and address + size <= segment["vmaddr"] + segment["filesize"]:
|
||||
offset = segment["fileoff"] + address - segment["vmaddr"]
|
||||
return data[offset:offset + size]
|
||||
raise AssertionError(f"unmapped address {address:#x}")
|
||||
|
||||
|
||||
def main():
|
||||
relinker = Path(sys.argv[1]).resolve()
|
||||
with tempfile.TemporaryDirectory(prefix="anyps5-exec-tls-") as directory:
|
||||
local = [(0x1000, TPOFF64, NULL, 4), (0x1008, DTPOFF64, NULL, 8), (0x1010, TPOFF64, TLS_SYMBOL, 2)]
|
||||
result, output = relink(relinker, directory, "linux", image(local + [(0x1018, DTPMOD64, NULL, 0),
|
||||
(0x1020, TPOFF64, IMPORTED, 0)]))
|
||||
assert result.returncode == 0, (result.stdout, result.stderr)
|
||||
elf = output.read_bytes()
|
||||
relocations = dynamic_relocations(elf)
|
||||
tls = sorted(relocation for relocation in relocations if relocation[1] in (DTPMOD64, DTPOFF64, TPOFF64))
|
||||
assert tls == [(0x1000, TPOFF64, "", 0, 4), (0x1008, DTPOFF64, "", 0, 8), (0x1010, TPOFF64, "", 0, 0x12),
|
||||
(0x1018, DTPMOD64, "", 0, 0), (0x1020, TPOFF64, "ImportedTls", 6, 0)], relocations
|
||||
assert any(struct.unpack_from("<I", elf, 64 + index * 56)[0] == PT_TLS
|
||||
for index in range(struct.unpack_from("<H", elf, 0x38)[0])), "PT_TLS must be kept"
|
||||
|
||||
result, output = relink(relinker, directory, "windows", image(local), "--windows")
|
||||
assert result.returncode == 0, (result.stdout, result.stderr)
|
||||
pe = output.read_bytes()
|
||||
values = [struct.unpack("<q", pe_bytes_at(pe, 0x10000 + address, 8))[0] for address in (0x1000, 0x1008, 0x1010)]
|
||||
assert values == [4 - TLS_BLOCK, 8, 0x12 - TLS_BLOCK], values
|
||||
|
||||
result, output = relink(relinker, directory, "macos", image(local), "--macos")
|
||||
assert result.returncode == 0, (result.stdout, result.stderr)
|
||||
macho = output.read_bytes()
|
||||
values = [struct.unpack("<q", macho_bytes_at(macho, 0x140010000 + address, 8))[0] for address in (0x1000, 0x1008, 0x1010)]
|
||||
assert values == [4 - TLS_BLOCK, 8, 0x12 - TLS_BLOCK], values
|
||||
|
||||
failures = [
|
||||
("tpoff32", [(0x1000, TPOFF32, NULL, 0)], True, (), "Unsupported relocation type 0x17"),
|
||||
("not-tls-symbol", [(0x1000, TPOFF64, DATA_SYMBOL, 0)], True, (), "not STT_TLS"),
|
||||
("no-tls-segment", [(0x1000, TPOFF64, NULL, 0)], False, (), "without a PT_TLS segment"),
|
||||
("dtpmod-addend", [(0x1000, DTPMOD64, NULL, 1)], True, (), "DTPMOD64 relocation has a nonzero addend"),
|
||||
("windows-dtpmod", [(0x1000, DTPMOD64, NULL, 0)], True, ("--windows",), "DTPMOD64 relocation is not supported"),
|
||||
("windows-import", [(0x1000, TPOFF64, IMPORTED, 0)], True, ("--windows",), "Imported TLS relocation"),
|
||||
("macos-dtpmod", [(0x1000, DTPMOD64, NULL, 0)], True, ("--macos",), "DTPMOD64 relocation is not supported"),
|
||||
("macos-import", [(0x1000, TPOFF64, IMPORTED, 0)], True, ("--macos",), "Imported TLS relocation"),
|
||||
]
|
||||
for name, relocations, has_tls, flags, error in failures:
|
||||
result, output = relink(relinker, directory, name, image(relocations, has_tls), *flags)
|
||||
assert result.returncode == 2 and error in result.stderr, (name, result.returncode, result.stderr)
|
||||
assert not output.exists(), name
|
||||
print("Executable TLS relocation test passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue