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: 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,119 @@ 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; + + // TODO: This should be page size (0x2000/0x1000 depending on target). + LoadPhdr.p_align = ELFT::Is64Bits ? sizeof(uint64_t) : sizeof(uint32_t); +} + +/// 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. +/// +/// @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) { + // DynStr has a null character at minimum. + // TODO: this assumes old method that doesn't use a StringTableBuilder, and + // may be inaccurate. Update. + size_t Total = 1; + if (Stub.SoName.hasValue()) { + Total += Stub.SoName->size() + 1; + } + for (const std::string &LibName : Stub.NeededLibs) { + Total += LibName.size() + 1; // Include null terminator + } + return Total; +} + /// 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 +333,27 @@ /// @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; + size_t AlignedSum = sizeof(Elf_Ehdr); + // PT_LOAD and PT_DYNAMIC + AlignedSum += sizeof(Elf_Phdr) * 2; + // SHT_NULL, .dynstr, .dynamic, and .shstrtab headers. + 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. + AlignedSum += dynStrSize(Stub); + 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); + if (Stub.SoName.hasValue()) + AlignedSum += sizeof(Elf_Dyn); + AlignedSum += Stub.NeededLibs.size() * sizeof(Elf_Dyn); + // ShStrTab contents size (fixed for now) + // TODO: use a StringTableBuilder. + AlignedSum += 28; //0.dynstr0.shstrtab0.dynamic0 + return AlignedSum; } /// This initializes an ELF file header with information specific to a binary @@ -265,6 +393,157 @@ 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); + + // 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 +553,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()) { + StringRef Str(*Stub.SoName); + DynStrTab.add(Str); + } + 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(); }