#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Elfpatcher::Linux { namespace { constexpr std::uint64_t kLinuxUserAddressEnd = 0x800000000000; std::string Hex(const std::uint64_t value) { std::ostringstream text; text << "0x" << std::hex << value; return text.str(); } std::uint64_t Rebased(const std::uint64_t base, const std::uint64_t address, const std::string& what) { if (base > kLinuxUserAddressEnd || address > kLinuxUserAddressEnd - base) throw Domain::RelinkerException(what + " " + Hex(address) + " lies outside the Linux user address space at fixed base " + Hex(base)); return base + address; } void RequireWholeEntries(const std::vector& table, const std::uint64_t entrySize, const std::string& what) { if (table.size() % entrySize != 0) throw Domain::RelinkerException(what + " is not a whole number of entries"); } void RequireOnlyDependencies(const std::vector& entries) { RequireWholeEntries(entries, kDynEntrySize, "Executable dependency table"); for (std::size_t entry = 0; entry < entries.size(); entry += kDynEntrySize) { const auto tag = static_cast(Io::ReadU64(entries, entry)); if (tag != DT_NEEDED) throw Domain::RelinkerException("Dynamic tag " + Hex(static_cast(tag)) + " cannot be placed at a fixed Linux base"); } } std::vector RebasedSymbols(const std::vector& table, const std::uint64_t base) { RequireWholeEntries(table, kSymEntrySize, "Dynamic symbol table"); auto rebased = table; for (std::size_t entry = 0; entry < rebased.size(); entry += kSymEntrySize) { const auto section = Io::ReadU16(rebased, entry + 6); if (section == SHN_UNDEF || section == SHN_ABS || (rebased[entry + 4] & 0xf) == STT_TLS) continue; if (section >= SHN_LORESERVE) throw Domain::RelinkerException("Symbol section index " + Hex(section) + " cannot be placed at a fixed Linux base"); Io::WriteU64(rebased, entry + 8, Rebased(base, Io::ReadU64(rebased, entry + 8), "Symbol")); } return rebased; } std::vector RebasedRelocations(const std::vector& table, const std::uint64_t base) { RequireWholeEntries(table, kRelaEntrySize, "Relocation table"); auto rebased = table; for (std::size_t entry = 0; entry < rebased.size(); entry += kRelaEntrySize) { const auto type = static_cast(Io::ReadU64(rebased, entry + 8)); switch (type) { case R_X86_64_NONE: continue; case R_X86_64_RELATIVE: case R_X86_64_IRELATIVE: { const auto addend = static_cast(Io::ReadU64(rebased, entry + 16)); if (addend < 0) throw Domain::RelinkerException("Relocation target " + std::to_string(addend) + " lies below the image at a fixed Linux base"); Io::WriteU64(rebased, entry + 16, Rebased(base, static_cast(addend), "Relocation target")); break; } case R_X86_64_64: case R_X86_64_PC32: case R_X86_64_COPY: case R_X86_64_GLOB_DAT: case R_X86_64_JUMP_SLOT: case R_X86_64_32: case R_X86_64_32S: case R_X86_64_DTPMOD64: case R_X86_64_DTPOFF64: case R_X86_64_TPOFF64: break; default: throw Domain::RelinkerException("Relocation type " + std::to_string(type) + " cannot be placed at a fixed Linux base"); } Io::WriteU64(rebased, entry, Rebased(base, Io::ReadU64(rebased, entry), "Relocation offset")); } return rebased; } void RequireFileRange(const std::vector& buf, const Domain::ProgramHeader& load) { if (load.Offset > buf.size() || load.FileSize > buf.size() - load.Offset) throw Domain::RelinkerException("PT_LOAD at " + Hex(load.MappedAddress) + " has file bytes outside the input"); } void SeparateSharedLoadPages(std::vector& buf, std::vector& headers) { std::vector loads; for (std::size_t index = 0; index < headers.size(); ++index) { if (headers[index].Type == PT_LOAD && headers[index].MemorySize != 0) { RequireFileRange(buf, headers[index]); loads.push_back(index); } } std::stable_sort(loads.begin(), loads.end(), [&headers](const std::size_t left, const std::size_t right) { return headers[left].MappedAddress < headers[right].MappedAddress; }); for (std::size_t position = 0; position < loads.size(); ++position) { auto& load = headers[loads[position]]; const auto prefix = load.MappedAddress % kDefaultLoadAlignment; if (prefix == 0 || load.FileSize == 0 || load.Offset % kDefaultLoadAlignment != prefix) continue; const auto pageStart = load.MappedAddress - prefix; std::vector image(static_cast(prefix), 0); std::vector covered(static_cast(prefix), false); for (std::size_t below = 0; below < position; ++below) { const auto& other = headers[loads[below]]; const auto end = std::min(other.MappedAddress + other.MemorySize, load.MappedAddress); for (auto address = std::max(other.MappedAddress, pageStart); address < end; ++address) { const auto relative = address - other.MappedAddress; covered[static_cast(address - pageStart)] = true; image[static_cast(address - pageStart)] = relative < other.FileSize ? buf[static_cast(other.Offset + relative)] : 0; } } const auto mapped = load.Offset - prefix; bool differs = false; for (std::size_t index = 0; index < image.size() && !differs; ++index) differs = covered[index] && buf[static_cast(mapped) + index] != image[index]; if (!differs) continue; const auto alignment = std::max(load.Alignment, kDefaultLoadAlignment); while ((buf.size() + prefix) % alignment != load.MappedAddress % alignment) buf.push_back(0); buf.insert(buf.end(), image.begin(), image.end()); const auto offset = static_cast(buf.size()); buf.resize(static_cast(offset + load.FileSize)); std::copy_n(buf.begin() + static_cast(load.Offset), static_cast(load.FileSize), buf.begin() + static_cast(offset)); load.Offset = offset; } } struct TracedImport { std::uint64_t Slot; std::string Name; std::string Library; }; std::string ReadDynamicString(const std::vector& strings, const std::uint32_t offset) { if (offset >= strings.size()) throw Domain::RelinkerException("Import symbol name lies outside the dynamic string table"); std::string value; for (std::size_t index = offset; index < strings.size() && strings[index] != 0; ++index) value.push_back(static_cast(strings[index])); return value; } std::vector TracedImports(const Domain::SysVDynamicSection& dynSection) { std::vector imports; const auto collect = [&](const std::vector& table) { RequireWholeEntries(table, kRelaEntrySize, "Relocation table"); for (std::size_t entry = 0; entry < table.size(); entry += kRelaEntrySize) { const auto info = Io::ReadU64(table, entry + 8); const auto type = static_cast(info); const auto symbol = info >> 32; const auto target = Io::ReadU64(table, entry); if ((type != R_X86_64_GLOB_DAT && type != R_X86_64_JUMP_SLOT) || symbol == 0 || Io::ReadU64(table, entry + 16) != 0) continue; if (type == R_X86_64_GLOB_DAT && !dynSection.FunctionImports.contains(target)) continue; if (symbol >= dynSection.DynSymData.size() / kSymEntrySize) throw Domain::RelinkerException("Invalid import symbol index", target); const auto symbolOffset = static_cast(symbol * kSymEntrySize); if (Io::ReadU16(dynSection.DynSymData, symbolOffset + 6) != SHN_UNDEF || dynSection.DynSymData[symbolOffset + 4] >> 4 == STB_WEAK) continue; auto name = ReadDynamicString(dynSection.DynStrData, Io::ReadU32(dynSection.DynSymData, symbolOffset)); if (name.empty()) continue; const auto library = dynSection.ImportModules.find(target); imports.push_back({target, std::move(name), library == dynSection.ImportModules.end() ? std::string{} : library->second}); } }; collect(dynSection.RelaData); collect(dynSection.RelaPltData); return imports; } std::vector TracePrologue(const std::uint64_t vaddr, const std::uint64_t table, const std::uint64_t tableEnd, const std::uint64_t function) { std::vector code = {0x52, 0x53, 0x41, 0x54, 0x48, 0x83, 0xec, 0x08}; const auto rip = [&](std::initializer_list opcode, const std::uint64_t target) { code.insert(code.end(), opcode); const auto displacement = static_cast(target) - static_cast(vaddr + code.size() + 4); if (displacement < std::numeric_limits::min() || displacement > std::numeric_limits::max()) throw Domain::RelinkerException("Import trace table exceeds rel32 range"); Io::AppendU32(code, static_cast(displacement)); }; rip({0x48, 0x8d, 0x1d}, table); rip({0x4c, 0x8d, 0x25}, tableEnd); const auto loop = code.size(); code.insert(code.end(), {0x4c, 0x39, 0xe3, 0x73, 0x00}); const auto exit = code.size(); code.insert(code.end(), {0x48, 0x8b, 0x03, 0x48, 0x8b, 0x3c, 0x03, 0x48, 0x8b, 0x73, 0x08, 0x48, 0x01, 0xde, 0x48, 0x8b, 0x53, 0x10, 0x48, 0x01, 0xda}); rip({0xff, 0x15}, function); code.insert(code.end(), {0x48, 0x8b, 0x3b, 0x48, 0x01, 0xdf, 0x48, 0x81, 0xe7, 0x00, 0xf0, 0xff, 0xff, 0xbe, 0x00, 0x10, 0x00, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0x50, 0xb8, 0x0a, 0x00, 0x00, 0x00, 0x0f, 0x05, 0x58}); code.insert(code.end(), {0x48, 0x8b, 0x0b, 0x48, 0x89, 0x04, 0x0b, 0x48, 0x83, 0xc3, 0x18, 0xeb}); code.push_back(static_cast(static_cast(static_cast(loop) - static_cast(code.size() + 1)))); code[exit - 1] = static_cast(code.size() - exit); code.insert(code.end(), {0x48, 0x83, 0xc4, 0x08, 0x41, 0x5c, 0x5b, 0x5a}); return code; } void RebaseProgramHeaders(std::vector& buf, const std::uint64_t tableOffset, const std::uint16_t entrySize, const std::uint16_t count, const std::uint64_t base) { for (std::uint16_t index = 0; index < count; ++index) { const auto header = static_cast(tableOffset + static_cast(index) * entrySize); const auto type = Io::ReadU32(buf, header + kPhdrTypeOffset); if (type == PT_NULL || type == PT_GNU_STACK) continue; const auto address = Rebased(base, Io::ReadU64(buf, header + kPhdrVaddrOffset), "Program header address"); const auto physical = Rebased(base, Io::ReadU64(buf, header + kPhdrPaddrOffset), "Program header physical address"); if (type == PT_LOAD) { const auto alignment = Io::ReadU64(buf, header + kPhdrAlignOffset); if (alignment > 1 && base % alignment != 0) throw Domain::RelinkerException("Fixed Linux base " + Hex(base) + " is not a multiple of the PT_LOAD alignment " + Hex(alignment)); if (Io::ReadU64(buf, header + kPhdrMemSizeOffset) > kLinuxUserAddressEnd - address) throw Domain::RelinkerException("PT_LOAD at " + Hex(address) + " ends outside the Linux user address space"); } Io::WriteU64(buf, header + kPhdrVaddrOffset, address); Io::WriteU64(buf, header + kPhdrPaddrOffset, physical); } } } LinuxElfPatcher::LinuxElfPatcher( std::shared_ptr entryStubBuilder, std::shared_ptr programHeaderLayoutBuilder, std::shared_ptr sectionHeaderTableBuilder, std::shared_ptr byteWriter, std::optional fixedBase, bool traceImports ) : _entryStubBuilder(std::move(entryStubBuilder)) , _programHeaderLayoutBuilder(std::move(programHeaderLayoutBuilder)) , _sectionHeaderTableBuilder(std::move(sectionHeaderTableBuilder)) , _byteWriter(std::move(byteWriter)) , _fixedBase(fixedBase) , _traceImports(traceImports) { } void LinuxElfPatcher::_appendDynEntry(std::vector& buf, std::int64_t tag, std::uint64_t val) const { _byteWriter->AppendI64(buf, tag); _byteWriter->AppendU64(buf, val); } void LinuxElfPatcher::_appendTrampoline( std::vector& buf, const Codegen::TrampolineSite& site, const std::uint64_t extraBlockOffset, const std::function& vaddrOfExtraBlockOffset) const { using namespace Codegen::Amd64OnlySubstitutionTable; if (site.Length < kJmpRel32.Size || site.OriginalBytes.size() != site.Length || site.Body.size() < kJmpRel32.Size || site.ReturnBranchOffset > site.Body.size() - kJmpRel32.Size || site.Body[site.ReturnBranchOffset] != kJmpRel32.Bytes[0]) throw Domain::RelinkerException("Invalid AMD-only trampoline site", site.Offset); if (site.Offset > extraBlockOffset || site.Length > extraBlockOffset - site.Offset) throw Domain::RelinkerException("AMD-only instruction is outside the original image", site.Offset); if (!std::equal(site.OriginalBytes.begin(), site.OriginalBytes.end(), buf.begin() + static_cast(site.Offset))) throw Domain::RelinkerException("AMD-only site bytes changed before patching", site.Offset); while (buf.size() % kStubAlignment != 0) buf.push_back(kTrapFill); const auto bodyOff = static_cast(buf.size()); const auto bodyVaddr = vaddrOfExtraBlockOffset(bodyOff); for (const std::uint8_t b : site.Body) buf.push_back(b); Codegen::ApplyStubRelocations(std::span(buf.data() + bodyOff, site.ReturnBranchOffset), site.Relocations, site.Address, bodyVaddr, site.Offset); const auto inRange = [](const std::int64_t displacement) { return displacement >= std::numeric_limits::min() && displacement <= std::numeric_limits::max(); }; const auto returnDisplacement = static_cast(site.Address + site.Length) - static_cast(bodyVaddr + site.ReturnBranchOffset + kJmpRel32.Size); const auto jumpDisplacement = static_cast(bodyVaddr) - static_cast(site.Address + kJmpRel32.Size); if (!inRange(returnDisplacement) || !inRange(jumpDisplacement)) throw Domain::RelinkerException("AMD-only stub exceeds rel32 range", site.Offset); _byteWriter->WriteU32(buf, static_cast(bodyOff + site.ReturnBranchOffset + 1), static_cast(returnDisplacement)); std::fill_n(buf.begin() + static_cast(site.Offset), site.Length, kNop1.Bytes[0]); buf[static_cast(site.Offset)] = kJmpRel32.Bytes[0]; _byteWriter->WriteU32(buf, static_cast(site.Offset + 1), static_cast(jumpDisplacement)); } std::vector LinuxElfPatcher::Patch( std::vector sourceElf, const std::vector& originalHeaders, const Domain::SysVDynamicSection& dynSection, const std::uint64_t originalPltGotVaddr, const std::string& runPath, const bool lazyBinding, const bool dependencyDiagnostics, const std::vector& trampolines) { if (dependencyDiagnostics) throw Domain::RelinkerException("Linux target does not support --windows-diagnostics"); if (_fixedBase && lazyBinding) throw Domain::RelinkerException("A fixed Linux base requires eager binding; --lazy-binding is incompatible"); if (_traceImports && lazyBinding) throw Domain::RelinkerException("--trace-imports conflicts with --lazy-binding"); if (_traceImports && std::find(dynSection.NeededLibraries.begin(), dynSection.NeededLibraries.end(), "libc.prx") == dynSection.NeededLibraries.end()) throw Domain::RelinkerException("--trace-imports requires libc.prx among the libraries"); if (_fixedBase) RequireOnlyDependencies(dynSection.DynamicSegmentData); const auto traced = _traceImports ? TracedImports(dynSection) : std::vector{}; const std::uint64_t base = _fixedBase.value_or(0); const auto address = [this, base](const std::uint64_t vaddr) { return _fixedBase ? Rebased(base, vaddr, "Address") : vaddr; }; const auto table = [this, base](const std::vector& data, const bool relocations) { if (!_fixedBase) return data; return relocations ? RebasedRelocations(data, base) : RebasedSymbols(data, base); }; std::vector buf = std::move(sourceElf); const std::uint32_t type = _fixedBase ? ET_EXEC : ET_DYN; buf[kEhdrOsAbiOffset] = 0; buf[kEhdrAbiVersionOffset] = 0; buf[kEhdrTypeOffset] = static_cast(type & 0xFF); buf[kEhdrTypeOffset + 1] = static_cast((type >> 8) & 0xFF); _byteWriter->WriteU64(buf, kEhdrShOffOffset, 0); _byteWriter->WriteU16(buf, kEhdrShNumOffset, 0); _byteWriter->WriteU16(buf, kEhdrShStrNdxOffset, 0); const std::uint64_t phOff = *reinterpret_cast(buf.data() + kEhdrPhOffOffset); const std::uint16_t phEntSize = *reinterpret_cast(buf.data() + kEhdrPhEntSizeOffset); const std::uint16_t phNum = *reinterpret_cast(buf.data() + kEhdrPhNumOffset); const auto alignBuf = [](std::vector& b, std::size_t alignment) { while (b.size() % alignment != 0) b.push_back(0); }; const auto dynStrOff = static_cast(buf.size()); for (std::uint8_t b : dynSection.DynStrData) buf.push_back(b); const std::uint64_t runPathStrOff = static_cast(buf.size()) - dynStrOff; for (char c : runPath) buf.push_back(static_cast(c)); buf.push_back(0); const std::uint64_t traceSymbolStrOff = static_cast(buf.size()) - dynStrOff; if (_traceImports) { for (char c : std::string_view("TraceImport_nid_no_patch")) buf.push_back(static_cast(c)); buf.push_back(0); } const std::uint64_t dynStrSize = static_cast(buf.size()) - dynStrOff; alignBuf(buf, kDynStrAlignment); const auto dynSymOff = static_cast(buf.size()); auto symbols = table(dynSection.DynSymData, false); const auto traceSymbol = symbols.size() / kSymEntrySize; if (_traceImports) { Io::AppendU32(symbols, static_cast(traceSymbolStrOff)); symbols.push_back(static_cast((STB_GLOBAL << 4) | STT_FUNC)); symbols.push_back(0); Io::AppendU16(symbols, SHN_UNDEF); Io::AppendU64(symbols, 0); Io::AppendU64(symbols, 0); } for (std::uint8_t b : symbols) buf.push_back(b); alignBuf(buf, kDynSymAlignment); const auto hashOff = static_cast(buf.size()); const auto symbolCount = static_cast(symbols.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()); auto relocations = table(dynSection.RelaData, true); const auto traceRelocation = relocations.size(); if (_traceImports) { Io::AppendU64(relocations, 0); Io::AppendU64(relocations, (static_cast(traceSymbol) << 32) | R_X86_64_GLOB_DAT); Io::AppendU64(relocations, 0); } for (std::uint8_t b : relocations) buf.push_back(b); alignBuf(buf, kRelaAlignment); const auto relaPltOff = static_cast(buf.size()); const auto pltRelocations = table(dynSection.RelaPltData, true); for (std::uint8_t b : pltRelocations) buf.push_back(b); alignBuf(buf, kRelaPltAlignment); const std::uint64_t extraBlockOff = dynStrOff; const std::uint64_t extraBlockVaddr = _programHeaderLayoutBuilder->ComputeExtraBlockVaddr(originalHeaders, extraBlockOff); const auto vaddrOfExtraBlockOffset = [extraBlockOff, extraBlockVaddr](std::uint64_t fileOffset) -> std::uint64_t { if (fileOffset < extraBlockOff) throw Domain::RelinkerException("Extra block member offset lies before the extra block"); return extraBlockVaddr + (fileOffset - extraBlockOff); }; std::vector dynSegBuf; for (std::uint8_t b : dynSection.DynamicSegmentData) dynSegBuf.push_back(b); _appendDynEntry(dynSegBuf, DT_STRTAB, address(vaddrOfExtraBlockOffset(dynStrOff))); _appendDynEntry(dynSegBuf, DT_STRSZ, dynStrSize); _appendDynEntry(dynSegBuf, DT_SYMTAB, address(vaddrOfExtraBlockOffset(dynSymOff))); _appendDynEntry(dynSegBuf, DT_SYMENT, kSymEntrySize); _appendDynEntry(dynSegBuf, DT_HASH, address(vaddrOfExtraBlockOffset(hashOff))); _appendDynEntry(dynSegBuf, DT_DEBUG, 0); if (!relocations.empty()) { _appendDynEntry(dynSegBuf, DT_RELA, address(vaddrOfExtraBlockOffset(relaOff))); _appendDynEntry(dynSegBuf, DT_RELASZ, relocations.size()); _appendDynEntry(dynSegBuf, DT_RELAENT, kRelaEntrySize); } if (!dynSection.RelaPltData.empty()) { _appendDynEntry(dynSegBuf, DT_JMPREL, address(vaddrOfExtraBlockOffset(relaPltOff))); _appendDynEntry(dynSegBuf, DT_PLTRELSZ, dynSection.RelaPltData.size()); _appendDynEntry(dynSegBuf, DT_PLTREL, static_cast(DT_RELA)); _appendDynEntry(dynSegBuf, DT_PLTGOT, address(originalPltGotVaddr)); } if (!lazyBinding) _appendDynEntry(dynSegBuf, DT_FLAGS, DF_BIND_NOW); _appendDynEntry(dynSegBuf, DT_RUNPATH, runPathStrOff); _appendDynEntry(dynSegBuf, DT_NULL, 0); const auto dynSegOff = static_cast(buf.size()); for (std::uint8_t b : dynSegBuf) buf.push_back(b); std::uint64_t traceFunctionVaddr = 0; std::uint64_t traceTableVaddr = 0; std::uint64_t traceTableEndVaddr = 0; std::vector traceEntries; if (_traceImports) { alignBuf(buf, 8); const auto functionOff = static_cast(buf.size()); traceFunctionVaddr = vaddrOfExtraBlockOffset(functionOff); Io::AppendU64(buf, 0); Io::WriteU64(buf, static_cast(relaOff + traceRelocation), address(traceFunctionVaddr)); traceTableVaddr = vaddrOfExtraBlockOffset(buf.size()); for (std::size_t index = 0; index < traced.size(); ++index) { traceEntries.push_back(buf.size()); buf.resize(buf.size() + 24); } traceTableEndVaddr = vaddrOfExtraBlockOffset(buf.size()); std::map strings; const auto addString = [&](const std::string& value) { if (const auto existing = strings.find(value); existing != strings.end()) return existing->second; const auto vaddr = vaddrOfExtraBlockOffset(buf.size()); for (char c : value) buf.push_back(static_cast(c)); buf.push_back(0); strings.emplace(value, vaddr); return vaddr; }; for (std::size_t index = 0; index < traced.size(); ++index) { const auto entryVaddr = vaddrOfExtraBlockOffset(traceEntries[index]); const auto relative = [entryVaddr](const std::uint64_t vaddr) { return vaddr - entryVaddr; }; Io::WriteU64(buf, traceEntries[index], relative(traced[index].Slot)); Io::WriteU64(buf, traceEntries[index] + 8, relative(addString(traced[index].Name))); Io::WriteU64(buf, traceEntries[index] + 16, relative(addString(traced[index].Library))); } } const std::uint64_t realEntryVaddr = *reinterpret_cast(buf.data() + kEhdrEntryOffset); const auto stubOff = static_cast(buf.size()); const auto stubVaddr = vaddrOfExtraBlockOffset(stubOff); if (_traceImports) { for (std::uint8_t b : TracePrologue(stubVaddr, traceTableVaddr, traceTableEndVaddr, traceFunctionVaddr)) buf.push_back(b); } const auto entryStubVaddr = vaddrOfExtraBlockOffset(buf.size()); for (std::uint8_t b : _entryStubBuilder->BuildEntryStub(address(entryStubVaddr), address(realEntryVaddr))) buf.push_back(b); const auto stubSize = static_cast(buf.size()) - stubOff; _byteWriter->WriteU64(buf, kEhdrEntryOffset, address(stubVaddr)); for (const auto& site : trampolines) _appendTrampoline(buf, site, extraBlockOff, vaddrOfExtraBlockOffset); static constexpr char kInterp[] = "/lib64/ld-linux-x86-64.so.2"; const auto interpOff = static_cast(buf.size()); constexpr std::uint64_t interpSize = sizeof(kInterp); for (char i : kInterp) buf.push_back(static_cast(i)); std::uint64_t tableOff = phOff; std::uint16_t tableSlots = phNum; if (const auto required = _programHeaderLayoutBuilder->RequiredProgramHeaderCount(originalHeaders); required > phNum && required <= std::numeric_limits::max()) { alignBuf(buf, kPhdrHeaderAlignment); tableOff = static_cast(buf.size()); tableSlots = static_cast(required); buf.resize(buf.size() + required * phEntSize); _byteWriter->WriteU64(buf, kEhdrPhOffOffset, tableOff); } const std::uint64_t extraBlockSize = static_cast(buf.size()) - extraBlockOff; auto headers = originalHeaders; SeparateSharedLoadPages(buf, headers); ProgramHeaderLayoutRequest layoutRequest{}; layoutRequest.PhOff = tableOff; layoutRequest.PhEntSize = phEntSize; layoutRequest.PhNum = tableSlots; layoutRequest.OriginalHeaders = headers; layoutRequest.ExtraBlockOffset = extraBlockOff; layoutRequest.ExtraBlockVaddr = extraBlockVaddr; layoutRequest.ExtraBlockSize = extraBlockSize; layoutRequest.DynamicSegmentOffset = dynSegOff; layoutRequest.DynamicSegmentSize = dynSegBuf.size(); layoutRequest.InterpOffset = interpOff; layoutRequest.InterpSize = interpSize; const std::uint16_t writtenPh = _programHeaderLayoutBuilder->WriteLayout(buf, layoutRequest); _byteWriter->WriteU16(buf, kEhdrPhNumOffset, writtenPh); if (_fixedBase) RebaseProgramHeaders(buf, tableOff, phEntSize, writtenPh, base); SectionHeaderTableRequest sectionRequest{}; sectionRequest.DynStrOffset = dynStrOff; sectionRequest.DynStrSize = dynStrSize; sectionRequest.DynSymOffset = dynSymOff; sectionRequest.DynSymSize = symbols.size(); sectionRequest.DynamicSegmentOffset = dynSegOff; sectionRequest.DynamicSegmentSize = dynSegBuf.size(); sectionRequest.StubOffset = stubOff; sectionRequest.StubSize = stubSize; _sectionHeaderTableBuilder->WriteTable(buf, sectionRequest); return buf; } }