Index: llvm/test/tools/llvm-elfabi/binary-write-neededlibs.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-elfabi/binary-write-neededlibs.test @@ -0,0 +1,20 @@ +# This test ensures .dynamic strings are added to .dynstr with suffix matching. +# RUN: llvm-elfabi %s --output-target=elf64-little %t +# RUN: llvm-readobj --dynamic %t | FileCheck %s + +--- !tapi-tbe +TbeVersion: 1.0 +SoName: libsomething.so +Arch: x86_64 +NeededLibs: + - libc.so + - libclang.so + - thing.so +Symbols: {} +... + +# CHECK: STRSZ 37 (bytes) +# CHECK: SONAME Library soname: [libsomething.so] +# CHECK: NEEDED Shared library: [libc.so] +# CHECK: NEEDED Shared library: [libclang.so] +# CHECK: NEEDED Shared library: [thing.so] Index: llvm/test/tools/llvm-elfabi/binary-write-pheaders.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-elfabi/binary-write-pheaders.test @@ -0,0 +1,38 @@ +# RUN: llvm-elfabi %s --output-target=elf64-little %t +# RUN: llvm-readobj -h %t | FileCheck %s --check-prefix=ELFHEADER +# RUN: llvm-readobj -l %t | FileCheck %s --check-prefix=PHDRS + +--- !tapi-tbe +TbeVersion: 1.0 +Arch: x86_64 +Symbols: {} +... + +# ELFHEADER: ProgramHeaderCount: 2{{$}} + +# PHDRS: ProgramHeader { +# PHDRS-NEXT: Type: PT_LOAD (0x1) +# PHDRS-NEXT: Offset: 0x0{{$}} +# PHDRS-NEXT: VirtualAddress: 0x0{{$}} +# PHDRS-NEXT: PhysicalAddress: 0x0{{$}} +# PHDRS-NEXT: FileSize: {{.*$}} +# PHDRS-NEXT: MemSize: {{.*$}} +# PHDRS-NEXT: Flags [ (0x5) +# PHDRS-NEXT: PF_R (0x4) +# PHDRS-NEXT: PF_X (0x1) +# PHDRS-NEXT: ] +# PHDRS-NEXT: Alignment: 4096{{$}} +# PHDRS-NEXT: } +# PHDRS-NEXT: ProgramHeader { +# PHDRS-NEXT: Type: PT_DYNAMIC (0x2) +# PHDRS-NEXT: Offset: {{.*$}} +# PHDRS-NEXT: VirtualAddress: {{.*$}} +# PHDRS-NEXT: PhysicalAddress: {{.*$}} +# PHDRS-NEXT: FileSize: 48{{$}} +# PHDRS-NEXT: MemSize: 48{{$}} +# PHDRS-NEXT: Flags [ (0x6) +# PHDRS-NEXT: PF_R (0x4) +# PHDRS-NEXT: PF_W (0x2) +# PHDRS-NEXT: ] +# PHDRS-NEXT: Alignment: 8{{$}} +# PHDRS-NEXT: } Index: llvm/test/tools/llvm-elfabi/binary-write-sheaders.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-elfabi/binary-write-sheaders.test @@ -0,0 +1,71 @@ +# RUN: llvm-elfabi %s --output-target=elf64-little %t +# RUN: llvm-readobj -h %t | FileCheck %s --check-prefix=ELFHEADER +# RUN: llvm-readobj -S %t | FileCheck %s --check-prefix=SECTIONS + +--- !tapi-tbe +TbeVersion: 1.0 +Arch: x86_64 +Symbols: {} +... + +# ELFHEADER: SectionHeaderCount: 4{{$}} +# ELFHEADER: StringTableSectionIndex: 3{{$}} + +# SECTIONS: Section { +# SECTIONS-NEXT: Index: 0{{$}} +# SECTIONS-NEXT: Name: (0) +# SECTIONS-NEXT: Type: SHT_NULL (0x0) +# SECTIONS-NEXT: Flags [ (0x0) +# SECTIONS-NEXT: ] +# SECTIONS-NEXT: Address: 0x0{{$}} +# SECTIONS-NEXT: Offset: 0x0{{$}} +# SECTIONS-NEXT: Size: 4{{$}} +# SECTIONS-NEXT: Link: 3{{$}} +# SECTIONS-NEXT: Info: 0{{$}} +# SECTIONS-NEXT: AddressAlignment: 0{{$}} +# SECTIONS-NEXT: EntrySize: 0{{$}} +# SECTIONS-NEXT: } +# SECTIONS-NEXT: Section { +# SECTIONS-NEXT: Index: 1{{$}} +# SECTIONS-NEXT: Name: .dynstr (1) +# SECTIONS-NEXT: Type: SHT_STRTAB (0x3) +# SECTIONS-NEXT: Flags [ (0x2) +# SECTIONS-NEXT: SHF_ALLOC (0x2) +# SECTIONS-NEXT: ] +# SECTIONS-NEXT: Address: {{.*$}} +# SECTIONS-NEXT: Offset: {{.*$}} +# SECTIONS-NEXT: Size: 1{{$}} +# SECTIONS-NEXT: Link: 0{{$}} +# SECTIONS-NEXT: Info: 0{{$}} +# SECTIONS-NEXT: AddressAlignment: 1{{$}} +# SECTIONS-NEXT: EntrySize: 0{{$}} +# SECTIONS-NEXT: } +# SECTIONS-NEXT: Section { +# SECTIONS-NEXT: Index: 2{{$}} +# SECTIONS-NEXT: Name: .dynamic (9) +# SECTIONS-NEXT: Type: SHT_DYNAMIC (0x6) +# SECTIONS-NEXT: Flags [ (0x2) +# SECTIONS-NEXT: SHF_ALLOC (0x2) +# SECTIONS-NEXT: ] +# SECTIONS-NEXT: Address: {{.*$}} +# SECTIONS-NEXT: Offset: {{.*$}} +# SECTIONS-NEXT: Size: 48{{$}} +# SECTIONS-NEXT: Link: 1{{$}} +# SECTIONS-NEXT: Info: 0{{$}} +# SECTIONS-NEXT: AddressAlignment: 8{{$}} +# SECTIONS-NEXT: EntrySize: 16{{$}} +# SECTIONS-NEXT: } +# SECTIONS-NEXT: Section { +# SECTIONS-NEXT: Index: 3{{$}} +# SECTIONS-NEXT: Name: .shstrtab (18) +# SECTIONS-NEXT: Type: SHT_STRTAB (0x3) +# SECTIONS-NEXT: Flags [ (0x0) +# SECTIONS-NEXT: ] +# SECTIONS-NEXT: Address: 0x0{{$}} +# SECTIONS-NEXT: Offset: {{.*$}} +# SECTIONS-NEXT: Size: 28{{$}} +# SECTIONS-NEXT: Link: 0{{$}} +# SECTIONS-NEXT: Info: 0{{$}} +# SECTIONS-NEXT: AddressAlignment: 1{{$}} +# SECTIONS-NEXT: EntrySize: 0{{$}} +# SECTIONS-NEXT: } Index: llvm/test/tools/llvm-elfabi/binary-write-soname.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-elfabi/binary-write-soname.test @@ -0,0 +1,12 @@ +# RUN: llvm-elfabi %s --output-target=elf64-little %t +# RUN: llvm-readobj --dynamic %t | FileCheck %s + +--- !tapi-tbe +TbeVersion: 1.0 +SoName: somelib.so +Arch: x86_64 +Symbols: {} +... + +# CHECK: STRSZ 12 (bytes) +# CHECK: SONAME Library soname: [somelib.so] Index: llvm/tools/llvm-elfabi/ELFObjHandler.cpp =================================================================== --- llvm/tools/llvm-elfabi/ELFObjHandler.cpp +++ llvm/tools/llvm-elfabi/ELFObjHandler.cpp @@ -7,6 +7,7 @@ //===-----------------------------------------------------------------------===/ #include "ELFObjHandler.h" +#include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/Binary.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" @@ -211,6 +212,130 @@ return createStringError(errc::not_supported, "Unsupported binary format"); } +/// A simple function to initialize members of the .dynamic section header. +/// This function doesn't populate all of the section header members. The +/// members not explicitly populated are zero-initialized, and some members +/// (e.g. sh_size, sh_offset, and more) are populated later. +template +static void initDynamicShdr(typename ELFT::Shdr &Dynamic) { + memset(&Dynamic, 0, sizeof(typename ELFT::Shdr)); + Dynamic.sh_type = SHT_DYNAMIC; + Dynamic.sh_flags = SHF_ALLOC; + Dynamic.sh_addralign = ELFT::Is64Bits ? sizeof(uint64_t) : sizeof(uint32_t); + Dynamic.sh_entsize = sizeof(typename ELFT::Dyn); + +} + +/// A simple function to initialize members of the .dynstr section header. +/// This function doesn't populate all of the section header members. The +/// members not explicitly populated are zero-initialized, and some members +/// (e.g. sh_size, sh_offset, and more) are populated later. +template +static void initDynStrShdr(typename ELFT::Shdr &DynStr) { + memset(&DynStr, 0, sizeof(typename ELFT::Shdr)); + DynStr.sh_type = SHT_STRTAB; + DynStr.sh_flags = SHF_ALLOC; + DynStr.sh_addralign = 1; +} + +/// A simple function to initialize members of the .shstrtab section header. +/// This function doesn't populate all of the section header members. The +/// members not explicitly populated are zero-initialized, and some members +/// (e.g. sh_size, sh_offset, and more) are populated later. +template +static void initShStrTabShdr(typename ELFT::Shdr &ShStrTabHdr) { + memset(&ShStrTabHdr, 0, sizeof(typename ELFT::Shdr)); + ShStrTabHdr.sh_type = SHT_STRTAB; + ShStrTabHdr.sh_addralign = 1; +} + +/// A simple function to initialize members of the PT_DYNAMIC program header. +/// This function doesn't populate all of the program header members. The +/// members not explicitly populated are zero-initialized, and some members +/// (e.g. p_filesz, p_offset, and more) are populated later. +template +static void initDynamicPhdr(typename ELFT::Phdr &DynPhdr) { + memset(&DynPhdr, 0, sizeof(typename ELFT::Phdr)); + DynPhdr.p_type = PT_DYNAMIC; + DynPhdr.p_flags = PF_W | PF_R; + DynPhdr.p_align = ELFT::Is64Bits ? sizeof(uint64_t) : sizeof(uint32_t); +} + +/// A simple function to initialize members of the first PT_LOAD program header. +/// This function doesn't populate all of the program header members. The +/// members not explicitly populated are zero-initialized, and some members +/// (e.g. p_filesz and p_memsz) are populated later. +template +static void initLoadPhdr(typename ELFT::Phdr &LoadPhdr) { + memset(&LoadPhdr, 0, sizeof(typename ELFT::Phdr)); + LoadPhdr.p_type = PT_LOAD; + LoadPhdr.p_flags = PF_X | PF_R; + + // Using default max page size from lld. + // TODO: Infer from architecture, or add flag to configure. + LoadPhdr.p_align = 0x1000; +} + +/// A simple helper function to align an offset/address. +/// +/// @return Aligned offset. +static size_t alignTo(size_t Offset, size_t Align) { + // Only allow powers of two. + assert((Align & (Align - 1)) == 0 && "Alignment must be power of two"); + // Handle zero. + if (Align == 0) + Align = 1; + + uint64_t BitMask = Align - 1;; + return (Offset + BitMask) & (~BitMask); +} + +/// This function ensures the offset in the destination buffer is aligned +/// before copying. +/// Copies from Src to Dest at the nearest offset after DestOffset such that the +/// alignment requirement is satisfied. +/// +/// @return The byte offset immediately following the copied content. +static size_t alignAndCopy(uint8_t *Dest, const void *Src, size_t DestOffset, + size_t Length, size_t Align) { + if (Length == 0) + return DestOffset; + + uint64_t AlignedOffset = alignTo(DestOffset, Align); + uint64_t Pad = AlignedOffset - DestOffset; + + // Add padding, update destination offset. + if (Pad != 0) { + memset(Dest + DestOffset, 0, Pad); + } + + std::memcpy(Dest + AlignedOffset, Src, Length); + return AlignedOffset + Length; +} + +/// Calculates the size of the .dynstr section. +static size_t dynStrSize(const ELFStub &Stub) { + StringTableBuilder DynStrTab(StringTableBuilder::ELF); + if (Stub.SoName.hasValue()) { + DynStrTab.add(*Stub.SoName); + } + for (const std::string &LibName : Stub.NeededLibs) { + DynStrTab.add(LibName); + } + DynStrTab.finalize(); + return DynStrTab.getSize(); +} + +/// Calculates the size of the .shstrtab section. +static size_t shStrSize(const ELFStub &Stub) { + StringTableBuilder ShStrTab(StringTableBuilder::ELF); + ShStrTab.add(".dynstr"); + ShStrTab.add(".dynamic"); + ShStrTab.add(".shstrtab"); + ShStrTab.finalize(); + return ShStrTab.getSize(); +} + /// This function calculates the size a binary ELF stub will be. /// `Stub` is used to determine the exact binary size. This calculation includes /// padding that may be added between sections to ensure proper alignment. @@ -219,13 +344,36 @@ /// @return Size (in bytes) of the final binary stub. template static size_t getBinarySize(const ELFStub &Stub) { using Elf_Ehdr = typename ELFT::Ehdr; - return sizeof(Elf_Ehdr); - // TODO: Calculate size of section headers. - // TODO: Calculate size of program headers. + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Phdr = typename ELFT::Phdr; + using Elf_Dyn = typename ELFT::Dyn; + // Start with size of ELF header. + size_t AlignedSum = sizeof(Elf_Ehdr); + // Calculate size of program headers. (PT_LOAD and PT_DYNAMIC) + AlignedSum += sizeof(Elf_Phdr) * 2; + + // Calculate size of section headers. (SHT_NULL, .dynstr, .dynamic, and + // .shstrtab) + AlignedSum += 4 * sizeof(Elf_Shdr); + // TODO: Calculate size of .dynsym section. - // TODO: Calculate size of .dynstr section. - // TODO: Calculate size of .dynamic section. - // TODO: Calculate size of .shstrtab section. + + // Calculate size of .dynstr. + AlignedSum += dynStrSize(Stub); + + // Calculate size of dynamic section. + size_t DynAlign = ELFT::Is64Bits ? sizeof(uint64_t) : sizeof(uint32_t); + AlignedSum = alignTo(AlignedSum, DynAlign); + // DT_STRTAB, DT_STRSZ, and DT_NULL entries. + AlignedSum += 3 * sizeof(Elf_Dyn); + // Other dynamic entries. + if (Stub.SoName.hasValue()) + AlignedSum += sizeof(Elf_Dyn); + AlignedSum += Stub.NeededLibs.size() * sizeof(Elf_Dyn); + + // Calculate size of .shstrtab. + AlignedSum += shStrSize(Stub); + return AlignedSum; } /// This initializes an ELF file header with information specific to a binary @@ -265,6 +413,159 @@ ElfHeader.e_shentsize = sizeof(Elf_Shdr); } +/// A simple struct that holds pointers to section headers. +/// This is necessary so section headers can be updated later . +template +struct SectionHeaders { + typename ELFT::Shdr *NullShdr = nullptr; + typename ELFT::Shdr *DotDynstr = nullptr; + typename ELFT::Shdr *DotDynamic = nullptr; + typename ELFT::Shdr *DotShstrtab = nullptr; +}; + +/// This initializes required section headers and writes them to a buffer. +/// dynamic shared object. +/// Offsets, indexes, links, etc. for section and program headers are just +/// zero-initialized as they will be updated elsewhere. +/// +/// @param ElfHeader Target ELFT::Ehdr to update as entries are added. +/// @param ShStrTab String table that will hold section names. +/// @param Base Pointer to the beginning of the target buffer. +/// @param WriteOffset The offset in `Base` to begin writing the headers. +/// @return New SectionHeaders object with all header pointers populated. +template +static SectionHeaders writeShdrs(typename ELFT::Ehdr &ElfHeader, + StringTableBuilder &ShStrTab, + uint8_t *Base, + size_t WriteOffset) { + using Elf_Shdr = typename ELFT::Shdr; + SectionHeaders Shdrs; + ElfHeader.e_shoff = WriteOffset; + + // Undefined section. + Shdrs.NullShdr = reinterpret_cast(Base + WriteOffset); + memset(Shdrs.NullShdr, 0, sizeof(Elf_Shdr)); + ElfHeader.e_shnum += 1; + WriteOffset += sizeof(Elf_Shdr); + + // DynStr section. + Shdrs.DotDynstr = reinterpret_cast(Base + WriteOffset); + initDynStrShdr(*Shdrs.DotDynstr); + ShStrTab.add(".dynstr"); + uint64_t DynStrIdx = ElfHeader.e_shnum; + ElfHeader.e_shnum += 1; + WriteOffset += sizeof(Elf_Shdr); + + // Dynamic section. + Shdrs.DotDynamic = reinterpret_cast(Base + WriteOffset); + initDynamicShdr(*Shdrs.DotDynamic); + ShStrTab.add(".dynamic"); + Shdrs.DotDynamic->sh_link = DynStrIdx; + ElfHeader.e_shnum += 1; + WriteOffset += sizeof(Elf_Shdr); + + // TODO: Add SHT_NOBITS .def section. + + // ShStrTab section. + Shdrs.DotShstrtab = reinterpret_cast(Base + WriteOffset); + initShStrTabShdr(*Shdrs.DotShstrtab); + ShStrTab.add(".shstrtab"); + uint64_t ShStrTabIdx = ElfHeader.e_shnum; + Shdrs.NullShdr->sh_link = ShStrTabIdx; + ElfHeader.e_shstrndx = ShStrTabIdx; + ElfHeader.e_shnum += 1; + WriteOffset += sizeof(Elf_Shdr); + // Update SHT null: + Shdrs.NullShdr->sh_size = ElfHeader.e_shnum; + + ShStrTab.finalize(); + Shdrs.DotDynstr->sh_name = ShStrTab.getOffset(".dynstr"); + Shdrs.DotDynamic->sh_name = ShStrTab.getOffset(".dynamic"); + Shdrs.DotShstrtab->sh_name = ShStrTab.getOffset(".shstrtab"); + + // Warning sections. + // TODO: add warning sections. + return Shdrs; +} + +/// This function generates, aligns, and writes the .dynamic section. +/// dynamic shared object. +/// Offsets, indexes, links, etc. for section and program headers are just +/// zero-initialized as they will be updated elsewhere. +/// +/// @param Stub The source ELFStub to determine what dynamic entries are needed. +/// @param Shdrs Used to update .dynamic section header and retrieve .dynstr +/// address. +/// @param DynStrTab Used to determine the offsets of strings in .dynstr. +/// @param Base Pointer to the beginning of the target buffer. +/// @param WriteOffset The offset in `Base` to begin writing the headers. +/// @return The byte offset immediately following the dynamic section. +template +static size_t writeDynamic(const ELFStub &Stub, + SectionHeaders &Shdrs, + StringTableBuilder &DynStrTab, + uint8_t *Base, + size_t WriteOffset) { + using Elf_Dyn = typename ELFT::Dyn; + size_t DynAlign = ELFT::Is64Bits ? sizeof(uint64_t) : sizeof(uint32_t); + std::vector DynEntries; + + // Add DT_STRTAB entry. + Elf_Dyn DynStrTabEnt; + DynStrTabEnt.d_tag = DT_STRTAB; + DynStrTabEnt.d_un.d_ptr = Shdrs.DotDynstr->sh_addr; + DynEntries.push_back(DynStrTabEnt); + + // Add DT_STRSZ entry. + Elf_Dyn DynStrSzEnt; + DynStrSzEnt.d_tag = DT_STRSZ; + DynStrSzEnt.d_un.d_val = DynStrTab.getSize(); + DynEntries.push_back(DynStrSzEnt); + + // Add DT_SONAME entry. + if (Stub.SoName.hasValue()) { + StringRef Str(*Stub.SoName); + Elf_Dyn DynSOName; + DynSOName.d_tag = DT_SONAME; + DynSOName.d_un.d_val = DynStrTab.getOffset(Str); + DynEntries.push_back(DynSOName); + } + + // Add DT_NEEDED entries. + for (const std::string &LibName : Stub.NeededLibs) { + Elf_Dyn NeededEntry; + NeededEntry.d_tag = DT_NEEDED; + NeededEntry.d_un.d_val = DynStrTab.getOffset(LibName); + DynEntries.push_back(NeededEntry); + } + + // Add DT_NULL entry. + Elf_Dyn DynNullEnt; + DynNullEnt.d_tag = DT_NULL; + DynNullEnt.d_un.d_val = 0; + DynEntries.push_back(DynNullEnt); + + WriteOffset = alignTo(WriteOffset, DynAlign); + Shdrs.DotDynamic->sh_offset = WriteOffset; + Shdrs.DotDynamic->sh_addr = WriteOffset; + Shdrs.DotDynamic->sh_size = sizeof(Elf_Dyn) * DynEntries.size(); + + // Write DT_NEEDED and DT_SONAME. + return alignAndCopy(Base, DynEntries.data(), WriteOffset, + sizeof(Elf_Dyn) * DynEntries.size(), DynAlign); +} + +/// This updates the PT_DYNAMIC entry to reflect the .dynamic section header. +template +static void updatePTDynEntry(const typename ELFT::Shdr &Dynamic, + typename ELFT::Phdr &PTDynPhdr) { + PTDynPhdr.p_offset = Dynamic.sh_offset; + PTDynPhdr.p_vaddr = Dynamic.sh_addr; + PTDynPhdr.p_paddr = Dynamic.sh_addr; + PTDynPhdr.p_filesz = Dynamic.sh_size; + PTDynPhdr.p_memsz = Dynamic.sh_size; +} + /// This function uses an ELFStub to generate an ELF binary stub that is written /// to a buffer. /// @@ -274,15 +575,71 @@ static Error writeELFBinaryToBuffer(const ELFStub &Stub, MutableArrayRef BufRef) { using Elf_Ehdr = typename ELFT::Ehdr; + using Elf_Shdr = typename ELFT::Shdr; + using Elf_Phdr = typename ELFT::Phdr; uint8_t *Buf = BufRef.data(); Elf_Ehdr *ElfHeader = reinterpret_cast(Buf); initELFHeader(*ElfHeader, Stub.Arch); - // TODO: Write section headers. - // TODO: Write program headers. - // TODO: Write .dynsym section. - // TODO: Write .dynstr section. - // TODO: Write .dynamic section. - // TODO: Write .shstrtab section. + size_t WriteOffset = sizeof(Elf_Ehdr); + + // Initialize program headers: + // PT_LOAD for .dynstr, .dynsym, and .dynamic. + Elf_Phdr* PTLoadDynPhdr = reinterpret_cast(Buf + WriteOffset); + initLoadPhdr(*PTLoadDynPhdr); + ElfHeader->e_phoff = WriteOffset; + ElfHeader->e_phnum += 1; + WriteOffset += sizeof(Elf_Phdr); + // PT_DYNAMIC for .dynamic. + Elf_Phdr* PTDynPhdr = reinterpret_cast(Buf + WriteOffset); + initDynamicPhdr(*PTDynPhdr); + ElfHeader->e_phnum += 1; + WriteOffset += sizeof(Elf_Phdr); + + // Section headers: + StringTableBuilder ShStrTab(StringTableBuilder::ELF); + SectionHeaders Shdrs = + writeShdrs(*ElfHeader, ShStrTab, Buf, WriteOffset); + WriteOffset += sizeof(Elf_Shdr) * ElfHeader->e_shnum; + + // Add SoName and NeededLib list to .dynstr. + StringTableBuilder DynStrTab(StringTableBuilder::ELF); + if (Stub.SoName.hasValue()) { + DynStrTab.add(*Stub.SoName); + } + for (const std::string &LibName : Stub.NeededLibs) { + DynStrTab.add(LibName); + } + + // TODO: Add symbol names to .dynstr. + DynStrTab.finalize(); + + // TODO: Write .dynsym contents. + // TODO: Update size of SHT_NOBITS section. + + // Write .dynstr contents: + DynStrTab.write(Buf + WriteOffset); + Shdrs.DotDynstr->sh_offset = WriteOffset; + Shdrs.DotDynstr->sh_addr = WriteOffset; + Shdrs.DotDynstr->sh_size = DynStrTab.getSize(); + WriteOffset += DynStrTab.getSize(); + + // Write .dynamic contents: + WriteOffset = + writeDynamic(Stub, Shdrs, DynStrTab, Buf, WriteOffset); + updatePTDynEntry(*Shdrs.DotDynamic, *PTDynPhdr); + + // Mark end of PT_LOAD. + PTLoadDynPhdr->p_filesz = WriteOffset; + // TODO: p_memsz should be WriteOffset + alignment + sizeof(.def). + PTLoadDynPhdr->p_memsz = WriteOffset; + + // Write ShStrTab contents: + Shdrs.DotShstrtab->sh_offset = WriteOffset; + ShStrTab.write(Buf + WriteOffset); + Shdrs.DotShstrtab->sh_offset = WriteOffset; + Shdrs.DotShstrtab->sh_size = ShStrTab.getSize(); + WriteOffset += ShStrTab.getSize(); + return Error::success(); }