| //===- ELFObjHandler.cpp --------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===-----------------------------------------------------------------------===/ |
| |
| #include "llvm/InterfaceStub/ELFObjHandler.h" |
| #include "llvm/InterfaceStub/IFSStub.h" |
| #include "llvm/MC/StringTableBuilder.h" |
| #include "llvm/Object/Binary.h" |
| #include "llvm/Object/ELFObjectFile.h" |
| #include "llvm/Object/ELFTypes.h" |
| #include "llvm/Support/Errc.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/FileOutputBuffer.h" |
| #include "llvm/Support/MathExtras.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include <optional> |
| |
| using llvm::object::ELFObjectFile; |
| |
| using namespace llvm; |
| using namespace llvm::object; |
| using namespace llvm::ELF; |
| |
| namespace llvm { |
| namespace ifs { |
| |
| // Simple struct to hold relevant .dynamic entries. |
| struct DynamicEntries { |
| uint64_t StrTabAddr = 0; |
| uint64_t StrSize = 0; |
| std::optional<uint64_t> SONameOffset; |
| std::vector<uint64_t> NeededLibNames; |
| // Symbol table: |
| uint64_t DynSymAddr = 0; |
| // Hash tables: |
| std::optional<uint64_t> ElfHash; |
| std::optional<uint64_t> GnuHash; |
| }; |
| |
| /// This initializes an ELF file header with information specific to a binary |
| /// 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 populate. |
| /// @param Machine Target architecture (e_machine from ELF specifications). |
| template <class ELFT> |
| static void initELFHeader(typename ELFT::Ehdr &ElfHeader, uint16_t Machine) { |
| memset(&ElfHeader, 0, sizeof(ElfHeader)); |
| // ELF identification. |
| ElfHeader.e_ident[EI_MAG0] = ElfMagic[EI_MAG0]; |
| ElfHeader.e_ident[EI_MAG1] = ElfMagic[EI_MAG1]; |
| ElfHeader.e_ident[EI_MAG2] = ElfMagic[EI_MAG2]; |
| ElfHeader.e_ident[EI_MAG3] = ElfMagic[EI_MAG3]; |
| ElfHeader.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; |
| bool IsLittleEndian = ELFT::TargetEndianness == support::little; |
| ElfHeader.e_ident[EI_DATA] = IsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; |
| ElfHeader.e_ident[EI_VERSION] = EV_CURRENT; |
| ElfHeader.e_ident[EI_OSABI] = ELFOSABI_NONE; |
| |
| // Remainder of ELF header. |
| ElfHeader.e_type = ET_DYN; |
| ElfHeader.e_machine = Machine; |
| ElfHeader.e_version = EV_CURRENT; |
| ElfHeader.e_ehsize = sizeof(typename ELFT::Ehdr); |
| ElfHeader.e_phentsize = sizeof(typename ELFT::Phdr); |
| ElfHeader.e_shentsize = sizeof(typename ELFT::Shdr); |
| } |
| |
| namespace { |
| template <class ELFT> struct OutputSection { |
| using Elf_Shdr = typename ELFT::Shdr; |
| std::string Name; |
| Elf_Shdr Shdr; |
| uint64_t Addr; |
| uint64_t Offset; |
| uint64_t Size; |
| uint64_t Align; |
| uint32_t Index; |
| bool NoBits = true; |
| }; |
| |
| template <class T, class ELFT> |
| struct ContentSection : public OutputSection<ELFT> { |
| T Content; |
| ContentSection() { this->NoBits = false; } |
| }; |
| |
| // This class just wraps StringTableBuilder for the purpose of adding a |
| // default constructor. |
| class ELFStringTableBuilder : public StringTableBuilder { |
| public: |
| ELFStringTableBuilder() : StringTableBuilder(StringTableBuilder::ELF) {} |
| }; |
| |
| template <class ELFT> class ELFSymbolTableBuilder { |
| public: |
| using Elf_Sym = typename ELFT::Sym; |
| |
| ELFSymbolTableBuilder() { Symbols.push_back({}); } |
| |
| void add(size_t StNameOffset, uint64_t StSize, uint8_t StBind, uint8_t StType, |
| uint8_t StOther, uint16_t StShndx) { |
| Elf_Sym S{}; |
| S.st_name = StNameOffset; |
| S.st_size = StSize; |
| S.st_info = (StBind << 4) | (StType & 0xf); |
| S.st_other = StOther; |
| S.st_shndx = StShndx; |
| Symbols.push_back(S); |
| } |
| |
| size_t getSize() const { return Symbols.size() * sizeof(Elf_Sym); } |
| |
| void write(uint8_t *Buf) const { |
| memcpy(Buf, Symbols.data(), sizeof(Elf_Sym) * Symbols.size()); |
| } |
| |
| private: |
| llvm::SmallVector<Elf_Sym, 8> Symbols; |
| }; |
| |
| template <class ELFT> class ELFDynamicTableBuilder { |
| public: |
| using Elf_Dyn = typename ELFT::Dyn; |
| |
| size_t addAddr(uint64_t Tag, uint64_t Addr) { |
| Elf_Dyn Entry; |
| Entry.d_tag = Tag; |
| Entry.d_un.d_ptr = Addr; |
| Entries.push_back(Entry); |
| return Entries.size() - 1; |
| } |
| |
| void modifyAddr(size_t Index, uint64_t Addr) { |
| Entries[Index].d_un.d_ptr = Addr; |
| } |
| |
| size_t addValue(uint64_t Tag, uint64_t Value) { |
| Elf_Dyn Entry; |
| Entry.d_tag = Tag; |
| Entry.d_un.d_val = Value; |
| Entries.push_back(Entry); |
| return Entries.size() - 1; |
| } |
| |
| void modifyValue(size_t Index, uint64_t Value) { |
| Entries[Index].d_un.d_val = Value; |
| } |
| |
| size_t getSize() const { |
| // Add DT_NULL entry at the end. |
| return (Entries.size() + 1) * sizeof(Elf_Dyn); |
| } |
| |
| void write(uint8_t *Buf) const { |
| memcpy(Buf, Entries.data(), sizeof(Elf_Dyn) * Entries.size()); |
| // Add DT_NULL entry at the end. |
| memset(Buf + sizeof(Elf_Dyn) * Entries.size(), 0, sizeof(Elf_Dyn)); |
| } |
| |
| private: |
| llvm::SmallVector<Elf_Dyn, 8> Entries; |
| }; |
| |
| template <class ELFT> class ELFStubBuilder { |
| public: |
| using Elf_Ehdr = typename ELFT::Ehdr; |
| using Elf_Shdr = typename ELFT::Shdr; |
| using Elf_Phdr = typename ELFT::Phdr; |
| using Elf_Sym = typename ELFT::Sym; |
| using Elf_Addr = typename ELFT::Addr; |
| using Elf_Dyn = typename ELFT::Dyn; |
| |
| ELFStubBuilder(const ELFStubBuilder &) = delete; |
| ELFStubBuilder(ELFStubBuilder &&) = default; |
| |
| explicit ELFStubBuilder(const IFSStub &Stub) { |
| DynSym.Name = ".dynsym"; |
| DynSym.Align = sizeof(Elf_Addr); |
| DynStr.Name = ".dynstr"; |
| DynStr.Align = 1; |
| DynTab.Name = ".dynamic"; |
| DynTab.Align = sizeof(Elf_Addr); |
| ShStrTab.Name = ".shstrtab"; |
| ShStrTab.Align = 1; |
| |
| // Populate string tables. |
| for (const IFSSymbol &Sym : Stub.Symbols) |
| DynStr.Content.add(Sym.Name); |
| for (const std::string &Lib : Stub.NeededLibs) |
| DynStr.Content.add(Lib); |
| if (Stub.SoName) |
| DynStr.Content.add(*Stub.SoName); |
| |
| std::vector<OutputSection<ELFT> *> Sections = {&DynSym, &DynStr, &DynTab, |
| &ShStrTab}; |
| const OutputSection<ELFT> *LastSection = Sections.back(); |
| // Now set the Index and put sections names into ".shstrtab". |
| uint64_t Index = 1; |
| for (OutputSection<ELFT> *Sec : Sections) { |
| Sec->Index = Index++; |
| ShStrTab.Content.add(Sec->Name); |
| } |
| ShStrTab.Content.finalize(); |
| ShStrTab.Size = ShStrTab.Content.getSize(); |
| DynStr.Content.finalize(); |
| DynStr.Size = DynStr.Content.getSize(); |
| |
| // Populate dynamic symbol table. |
| for (const IFSSymbol &Sym : Stub.Symbols) { |
| uint8_t Bind = Sym.Weak ? STB_WEAK : STB_GLOBAL; |
| // For non-undefined symbols, value of the shndx is not relevant at link |
| // time as long as it is not SHN_UNDEF. Set shndx to 1, which |
| // points to ".dynsym". |
| uint16_t Shndx = Sym.Undefined ? SHN_UNDEF : 1; |
| uint64_t Size = Sym.Size.value_or(0); |
| DynSym.Content.add(DynStr.Content.getOffset(Sym.Name), Size, Bind, |
| convertIFSSymbolTypeToELF(Sym.Type), 0, Shndx); |
| } |
| DynSym.Size = DynSym.Content.getSize(); |
| |
| // Poplulate dynamic table. |
| size_t DynSymIndex = DynTab.Content.addAddr(DT_SYMTAB, 0); |
| size_t DynStrIndex = DynTab.Content.addAddr(DT_STRTAB, 0); |
| DynTab.Content.addValue(DT_STRSZ, DynSym.Size); |
| for (const std::string &Lib : Stub.NeededLibs) |
| DynTab.Content.addValue(DT_NEEDED, DynStr.Content.getOffset(Lib)); |
| if (Stub.SoName) |
| DynTab.Content.addValue(DT_SONAME, |
| DynStr.Content.getOffset(*Stub.SoName)); |
| DynTab.Size = DynTab.Content.getSize(); |
| // Calculate sections' addresses and offsets. |
| uint64_t CurrentOffset = sizeof(Elf_Ehdr); |
| for (OutputSection<ELFT> *Sec : Sections) { |
| Sec->Offset = alignTo(CurrentOffset, Sec->Align); |
| Sec->Addr = Sec->Offset; |
| CurrentOffset = Sec->Offset + Sec->Size; |
| } |
| // Fill Addr back to dynamic table. |
| DynTab.Content.modifyAddr(DynSymIndex, DynSym.Addr); |
| DynTab.Content.modifyAddr(DynStrIndex, DynStr.Addr); |
| // Write section headers of string tables. |
| fillSymTabShdr(DynSym, SHT_DYNSYM); |
| fillStrTabShdr(DynStr, SHF_ALLOC); |
| fillDynTabShdr(DynTab); |
| fillStrTabShdr(ShStrTab); |
| |
| // Finish initializing the ELF header. |
| initELFHeader<ELFT>(ElfHeader, static_cast<uint16_t>(*Stub.Target.Arch)); |
| ElfHeader.e_shstrndx = ShStrTab.Index; |
| ElfHeader.e_shnum = LastSection->Index + 1; |
| ElfHeader.e_shoff = |
| alignTo(LastSection->Offset + LastSection->Size, sizeof(Elf_Addr)); |
| } |
| |
| size_t getSize() const { |
| return ElfHeader.e_shoff + ElfHeader.e_shnum * sizeof(Elf_Shdr); |
| } |
| |
| void write(uint8_t *Data) const { |
| write(Data, ElfHeader); |
| DynSym.Content.write(Data + DynSym.Shdr.sh_offset); |
| DynStr.Content.write(Data + DynStr.Shdr.sh_offset); |
| DynTab.Content.write(Data + DynTab.Shdr.sh_offset); |
| ShStrTab.Content.write(Data + ShStrTab.Shdr.sh_offset); |
| writeShdr(Data, DynSym); |
| writeShdr(Data, DynStr); |
| writeShdr(Data, DynTab); |
| writeShdr(Data, ShStrTab); |
| } |
| |
| private: |
| Elf_Ehdr ElfHeader; |
| ContentSection<ELFStringTableBuilder, ELFT> DynStr; |
| ContentSection<ELFStringTableBuilder, ELFT> ShStrTab; |
| ContentSection<ELFSymbolTableBuilder<ELFT>, ELFT> DynSym; |
| ContentSection<ELFDynamicTableBuilder<ELFT>, ELFT> DynTab; |
| |
| template <class T> static void write(uint8_t *Data, const T &Value) { |
| *reinterpret_cast<T *>(Data) = Value; |
| } |
| |
| void fillStrTabShdr(ContentSection<ELFStringTableBuilder, ELFT> &StrTab, |
| uint32_t ShFlags = 0) const { |
| StrTab.Shdr.sh_type = SHT_STRTAB; |
| StrTab.Shdr.sh_flags = ShFlags; |
| StrTab.Shdr.sh_addr = StrTab.Addr; |
| StrTab.Shdr.sh_offset = StrTab.Offset; |
| StrTab.Shdr.sh_info = 0; |
| StrTab.Shdr.sh_size = StrTab.Size; |
| StrTab.Shdr.sh_name = ShStrTab.Content.getOffset(StrTab.Name); |
| StrTab.Shdr.sh_addralign = StrTab.Align; |
| StrTab.Shdr.sh_entsize = 0; |
| StrTab.Shdr.sh_link = 0; |
| } |
| void fillSymTabShdr(ContentSection<ELFSymbolTableBuilder<ELFT>, ELFT> &SymTab, |
| uint32_t ShType) const { |
| SymTab.Shdr.sh_type = ShType; |
| SymTab.Shdr.sh_flags = SHF_ALLOC; |
| SymTab.Shdr.sh_addr = SymTab.Addr; |
| SymTab.Shdr.sh_offset = SymTab.Offset; |
| // Only non-local symbols are included in the tbe file, so .dynsym only |
| // contains 1 local symbol (the undefined symbol at index 0). The sh_info |
| // should always be 1. |
| SymTab.Shdr.sh_info = 1; |
| SymTab.Shdr.sh_size = SymTab.Size; |
| SymTab.Shdr.sh_name = this->ShStrTab.Content.getOffset(SymTab.Name); |
| SymTab.Shdr.sh_addralign = SymTab.Align; |
| SymTab.Shdr.sh_entsize = sizeof(Elf_Sym); |
| SymTab.Shdr.sh_link = this->DynStr.Index; |
| } |
| void fillDynTabShdr( |
| ContentSection<ELFDynamicTableBuilder<ELFT>, ELFT> &DynTab) const { |
| DynTab.Shdr.sh_type = SHT_DYNAMIC; |
| DynTab.Shdr.sh_flags = SHF_ALLOC; |
| DynTab.Shdr.sh_addr = DynTab.Addr; |
| DynTab.Shdr.sh_offset = DynTab.Offset; |
| DynTab.Shdr.sh_info = 0; |
| DynTab.Shdr.sh_size = DynTab.Size; |
| DynTab.Shdr.sh_name = this->ShStrTab.Content.getOffset(DynTab.Name); |
| DynTab.Shdr.sh_addralign = DynTab.Align; |
| DynTab.Shdr.sh_entsize = sizeof(Elf_Dyn); |
| DynTab.Shdr.sh_link = this->DynStr.Index; |
| } |
| uint64_t shdrOffset(const OutputSection<ELFT> &Sec) const { |
| return ElfHeader.e_shoff + Sec.Index * sizeof(Elf_Shdr); |
| } |
| |
| void writeShdr(uint8_t *Data, const OutputSection<ELFT> &Sec) const { |
| write(Data + shdrOffset(Sec), Sec.Shdr); |
| } |
| }; |
| |
| /// This function takes an error, and appends a string of text to the end of |
| /// that error. Since "appending" to an Error isn't supported behavior of an |
| /// Error, this function technically creates a new error with the combined |
| /// message and consumes the old error. |
| /// |
| /// @param Err Source error. |
| /// @param After Text to append at the end of Err's error message. |
| Error appendToError(Error Err, StringRef After) { |
| std::string Message; |
| raw_string_ostream Stream(Message); |
| Stream << Err; |
| Stream << " " << After; |
| consumeError(std::move(Err)); |
| return createError(Stream.str()); |
| } |
| |
| template <class ELFT> class DynSym { |
| using Elf_Shdr_Range = typename ELFT::ShdrRange; |
| using Elf_Shdr = typename ELFT::Shdr; |
| |
| public: |
| static Expected<DynSym> create(const ELFFile<ELFT> &ElfFile, |
| const DynamicEntries &DynEnt) { |
| Expected<Elf_Shdr_Range> Shdrs = ElfFile.sections(); |
| if (!Shdrs) |
| return Shdrs.takeError(); |
| return DynSym(ElfFile, DynEnt, *Shdrs); |
| } |
| |
| Expected<const uint8_t *> getDynSym() { |
| if (DynSymHdr) |
| return ElfFile.base() + DynSymHdr->sh_offset; |
| return getDynamicData(DynEnt.DynSymAddr, "dynamic symbol table"); |
| } |
| |
| Expected<StringRef> getDynStr() { |
| if (DynSymHdr) |
| return ElfFile.getStringTableForSymtab(*DynSymHdr, Shdrs); |
| Expected<const uint8_t *> DataOrErr = getDynamicData( |
| DynEnt.StrTabAddr, "dynamic string table", DynEnt.StrSize); |
| if (!DataOrErr) |
| return DataOrErr.takeError(); |
| return StringRef(reinterpret_cast<const char *>(*DataOrErr), |
| DynEnt.StrSize); |
| } |
| |
| private: |
| DynSym(const ELFFile<ELFT> &ElfFile, const DynamicEntries &DynEnt, |
| Elf_Shdr_Range Shdrs) |
| : ElfFile(ElfFile), DynEnt(DynEnt), Shdrs(Shdrs), |
| DynSymHdr(findDynSymHdr()) {} |
| |
| const Elf_Shdr *findDynSymHdr() { |
| for (const Elf_Shdr &Sec : Shdrs) |
| if (Sec.sh_type == SHT_DYNSYM) { |
| // If multiple .dynsym are present, use the first one. |
| // This behavior aligns with llvm::object::ELFFile::getDynSymtabSize() |
| return &Sec; |
| } |
| return nullptr; |
| } |
| |
| Expected<const uint8_t *> getDynamicData(uint64_t EntAddr, StringRef Name, |
| uint64_t Size = 0) { |
| Expected<const uint8_t *> SecPtr = ElfFile.toMappedAddr(EntAddr); |
| if (!SecPtr) |
| return appendToError( |
| SecPtr.takeError(), |
| ("when locating " + Name + " section contents").str()); |
| Expected<const uint8_t *> SecEndPtr = ElfFile.toMappedAddr(EntAddr + Size); |
| if (!SecEndPtr) |
| return appendToError( |
| SecEndPtr.takeError(), |
| ("when locating " + Name + " section contents").str()); |
| return *SecPtr; |
| } |
| |
| const ELFFile<ELFT> &ElfFile; |
| const DynamicEntries &DynEnt; |
| Elf_Shdr_Range Shdrs; |
| const Elf_Shdr *DynSymHdr; |
| }; |
| } // end anonymous namespace |
| |
| /// This function behaves similarly to StringRef::substr(), but attempts to |
| /// terminate the returned StringRef at the first null terminator. If no null |
| /// terminator is found, an error is returned. |
| /// |
| /// @param Str Source string to create a substring from. |
| /// @param Offset The start index of the desired substring. |
| static Expected<StringRef> terminatedSubstr(StringRef Str, size_t Offset) { |
| size_t StrEnd = Str.find('\0', Offset); |
| if (StrEnd == StringLiteral::npos) { |
| return createError( |
| "String overran bounds of string table (no null terminator)"); |
| } |
| |
| size_t StrLen = StrEnd - Offset; |
| return Str.substr(Offset, StrLen); |
| } |
| |
| /// This function populates a DynamicEntries struct using an ELFT::DynRange. |
| /// After populating the struct, the members are validated with |
| /// some basic correctness checks. |
| /// |
| /// @param Dyn Target DynamicEntries struct to populate. |
| /// @param DynTable Source dynamic table. |
| template <class ELFT> |
| static Error populateDynamic(DynamicEntries &Dyn, |
| typename ELFT::DynRange DynTable) { |
| if (DynTable.empty()) |
| return createError("No .dynamic section found"); |
| |
| // Search .dynamic for relevant entries. |
| bool FoundDynStr = false; |
| bool FoundDynStrSz = false; |
| bool FoundDynSym = false; |
| for (auto &Entry : DynTable) { |
| switch (Entry.d_tag) { |
| case DT_SONAME: |
| Dyn.SONameOffset = Entry.d_un.d_val; |
| break; |
| case DT_STRTAB: |
| Dyn.StrTabAddr = Entry.d_un.d_ptr; |
| FoundDynStr = true; |
| break; |
| case DT_STRSZ: |
| Dyn.StrSize = Entry.d_un.d_val; |
| FoundDynStrSz = true; |
| break; |
| case DT_NEEDED: |
| Dyn.NeededLibNames.push_back(Entry.d_un.d_val); |
| break; |
| case DT_SYMTAB: |
| Dyn.DynSymAddr = Entry.d_un.d_ptr; |
| FoundDynSym = true; |
| break; |
| case DT_HASH: |
| Dyn.ElfHash = Entry.d_un.d_ptr; |
| break; |
| case DT_GNU_HASH: |
| Dyn.GnuHash = Entry.d_un.d_ptr; |
| } |
| } |
| |
| if (!FoundDynStr) { |
| return createError( |
| "Couldn't locate dynamic string table (no DT_STRTAB entry)"); |
| } |
| if (!FoundDynStrSz) { |
| return createError( |
| "Couldn't determine dynamic string table size (no DT_STRSZ entry)"); |
| } |
| if (!FoundDynSym) { |
| return createError( |
| "Couldn't locate dynamic symbol table (no DT_SYMTAB entry)"); |
| } |
| if (Dyn.SONameOffset && *Dyn.SONameOffset >= Dyn.StrSize) { |
| return createStringError(object_error::parse_failed, |
| "DT_SONAME string offset (0x%016" PRIx64 |
| ") outside of dynamic string table", |
| *Dyn.SONameOffset); |
| } |
| for (uint64_t Offset : Dyn.NeededLibNames) { |
| if (Offset >= Dyn.StrSize) { |
| return createStringError(object_error::parse_failed, |
| "DT_NEEDED string offset (0x%016" PRIx64 |
| ") outside of dynamic string table", |
| Offset); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| /// This function creates an IFSSymbol and populates all members using |
| /// information from a binary ELFT::Sym. |
| /// |
| /// @param SymName The desired name of the IFSSymbol. |
| /// @param RawSym ELFT::Sym to extract symbol information from. |
| template <class ELFT> |
| static IFSSymbol createELFSym(StringRef SymName, |
| const typename ELFT::Sym &RawSym) { |
| IFSSymbol TargetSym{std::string(SymName)}; |
| uint8_t Binding = RawSym.getBinding(); |
| if (Binding == STB_WEAK) |
| TargetSym.Weak = true; |
| else |
| TargetSym.Weak = false; |
| |
| TargetSym.Undefined = RawSym.isUndefined(); |
| TargetSym.Type = convertELFSymbolTypeToIFS(RawSym.st_info); |
| |
| if (TargetSym.Type == IFSSymbolType::Func) { |
| TargetSym.Size = 0; |
| } else { |
| TargetSym.Size = RawSym.st_size; |
| } |
| return TargetSym; |
| } |
| |
| /// This function populates an IFSStub with symbols using information read |
| /// from an ELF binary. |
| /// |
| /// @param TargetStub IFSStub to add symbols to. |
| /// @param DynSym Range of dynamic symbols to add to TargetStub. |
| /// @param DynStr StringRef to the dynamic string table. |
| template <class ELFT> |
| static Error populateSymbols(IFSStub &TargetStub, |
| const typename ELFT::SymRange DynSym, |
| StringRef DynStr) { |
| // Skips the first symbol since it's the NULL symbol. |
| for (auto RawSym : DynSym.drop_front(1)) { |
| // If a symbol does not have global or weak binding, ignore it. |
| uint8_t Binding = RawSym.getBinding(); |
| if (!(Binding == STB_GLOBAL || Binding == STB_WEAK)) |
| continue; |
| // If a symbol doesn't have default or protected visibility, ignore it. |
| uint8_t Visibility = RawSym.getVisibility(); |
| if (!(Visibility == STV_DEFAULT || Visibility == STV_PROTECTED)) |
| continue; |
| // Create an IFSSymbol and populate it with information from the symbol |
| // table entry. |
| Expected<StringRef> SymName = terminatedSubstr(DynStr, RawSym.st_name); |
| if (!SymName) |
| return SymName.takeError(); |
| IFSSymbol Sym = createELFSym<ELFT>(*SymName, RawSym); |
| TargetStub.Symbols.push_back(std::move(Sym)); |
| // TODO: Populate symbol warning. |
| } |
| return Error::success(); |
| } |
| |
| /// Returns a new IFSStub with all members populated from an ELFObjectFile. |
| /// @param ElfObj Source ELFObjectFile. |
| template <class ELFT> |
| static Expected<std::unique_ptr<IFSStub>> |
| buildStub(const ELFObjectFile<ELFT> &ElfObj) { |
| using Elf_Dyn_Range = typename ELFT::DynRange; |
| using Elf_Sym_Range = typename ELFT::SymRange; |
| using Elf_Sym = typename ELFT::Sym; |
| std::unique_ptr<IFSStub> DestStub = std::make_unique<IFSStub>(); |
| const ELFFile<ELFT> &ElfFile = ElfObj.getELFFile(); |
| // Fetch .dynamic table. |
| Expected<Elf_Dyn_Range> DynTable = ElfFile.dynamicEntries(); |
| if (!DynTable) { |
| return DynTable.takeError(); |
| } |
| |
| // Collect relevant .dynamic entries. |
| DynamicEntries DynEnt; |
| if (Error Err = populateDynamic<ELFT>(DynEnt, *DynTable)) |
| return std::move(Err); |
| Expected<DynSym<ELFT>> EDynSym = DynSym<ELFT>::create(ElfFile, DynEnt); |
| if (!EDynSym) |
| return EDynSym.takeError(); |
| |
| Expected<StringRef> EDynStr = EDynSym->getDynStr(); |
| if (!EDynStr) |
| return EDynStr.takeError(); |
| |
| StringRef DynStr = *EDynStr; |
| |
| // Populate Arch from ELF header. |
| DestStub->Target.Arch = static_cast<IFSArch>(ElfFile.getHeader().e_machine); |
| DestStub->Target.BitWidth = |
| convertELFBitWidthToIFS(ElfFile.getHeader().e_ident[EI_CLASS]); |
| DestStub->Target.Endianness = |
| convertELFEndiannessToIFS(ElfFile.getHeader().e_ident[EI_DATA]); |
| DestStub->Target.ObjectFormat = "ELF"; |
| |
| // Populate SoName from .dynamic entries and dynamic string table. |
| if (DynEnt.SONameOffset) { |
| Expected<StringRef> NameOrErr = |
| terminatedSubstr(DynStr, *DynEnt.SONameOffset); |
| if (!NameOrErr) { |
| return appendToError(NameOrErr.takeError(), "when reading DT_SONAME"); |
| } |
| DestStub->SoName = std::string(*NameOrErr); |
| } |
| |
| // Populate NeededLibs from .dynamic entries and dynamic string table. |
| for (uint64_t NeededStrOffset : DynEnt.NeededLibNames) { |
| Expected<StringRef> LibNameOrErr = |
| terminatedSubstr(DynStr, NeededStrOffset); |
| if (!LibNameOrErr) { |
| return appendToError(LibNameOrErr.takeError(), "when reading DT_NEEDED"); |
| } |
| DestStub->NeededLibs.push_back(std::string(*LibNameOrErr)); |
| } |
| |
| // Populate Symbols from .dynsym table and dynamic string table. |
| Expected<uint64_t> SymCount = ElfFile.getDynSymtabSize(); |
| if (!SymCount) |
| return SymCount.takeError(); |
| if (*SymCount > 0) { |
| // Get pointer to in-memory location of .dynsym section. |
| Expected<const uint8_t *> DynSymPtr = EDynSym->getDynSym(); |
| if (!DynSymPtr) |
| return appendToError(DynSymPtr.takeError(), |
| "when locating .dynsym section contents"); |
| Elf_Sym_Range DynSyms = ArrayRef<Elf_Sym>( |
| reinterpret_cast<const Elf_Sym *>(*DynSymPtr), *SymCount); |
| Error SymReadError = populateSymbols<ELFT>(*DestStub, DynSyms, DynStr); |
| if (SymReadError) |
| return appendToError(std::move(SymReadError), |
| "when reading dynamic symbols"); |
| } |
| |
| return std::move(DestStub); |
| } |
| |
| /// This function opens a file for writing and then writes a binary ELF stub to |
| /// the file. |
| /// |
| /// @param FilePath File path for writing the ELF binary. |
| /// @param Stub Source InterFace Stub to generate a binary ELF stub from. |
| template <class ELFT> |
| static Error writeELFBinaryToFile(StringRef FilePath, const IFSStub &Stub, |
| bool WriteIfChanged) { |
| ELFStubBuilder<ELFT> Builder{Stub}; |
| // Write Stub to memory first. |
| std::vector<uint8_t> Buf(Builder.getSize()); |
| Builder.write(Buf.data()); |
| |
| if (WriteIfChanged) { |
| if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = |
| MemoryBuffer::getFile(FilePath)) { |
| // Compare Stub output with existing Stub file. |
| // If Stub file unchanged, abort updating. |
| if ((*BufOrError)->getBufferSize() == Builder.getSize() && |
| !memcmp((*BufOrError)->getBufferStart(), Buf.data(), |
| Builder.getSize())) |
| return Error::success(); |
| } |
| } |
| |
| Expected<std::unique_ptr<FileOutputBuffer>> BufOrError = |
| FileOutputBuffer::create(FilePath, Builder.getSize()); |
| if (!BufOrError) |
| return createStringError(errc::invalid_argument, |
| toString(BufOrError.takeError()) + |
| " when trying to open `" + FilePath + |
| "` for writing"); |
| |
| // Write binary to file. |
| std::unique_ptr<FileOutputBuffer> FileBuf = std::move(*BufOrError); |
| memcpy(FileBuf->getBufferStart(), Buf.data(), Buf.size()); |
| |
| return FileBuf->commit(); |
| } |
| |
| Expected<std::unique_ptr<IFSStub>> readELFFile(MemoryBufferRef Buf) { |
| Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buf); |
| if (!BinOrErr) { |
| return BinOrErr.takeError(); |
| } |
| |
| Binary *Bin = BinOrErr->get(); |
| if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { |
| return buildStub(*Obj); |
| } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { |
| return buildStub(*Obj); |
| } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { |
| return buildStub(*Obj); |
| } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { |
| return buildStub(*Obj); |
| } |
| return createStringError(errc::not_supported, "unsupported binary format"); |
| } |
| |
| // This function wraps the ELFT writeELFBinaryToFile() so writeBinaryStub() |
| // can be called without having to use ELFType templates directly. |
| Error writeBinaryStub(StringRef FilePath, const IFSStub &Stub, |
| bool WriteIfChanged) { |
| assert(Stub.Target.Arch); |
| assert(Stub.Target.BitWidth); |
| assert(Stub.Target.Endianness); |
| if (Stub.Target.BitWidth == IFSBitWidthType::IFS32) { |
| if (Stub.Target.Endianness == IFSEndiannessType::Little) { |
| return writeELFBinaryToFile<ELF32LE>(FilePath, Stub, WriteIfChanged); |
| } else { |
| return writeELFBinaryToFile<ELF32BE>(FilePath, Stub, WriteIfChanged); |
| } |
| } else { |
| if (Stub.Target.Endianness == IFSEndiannessType::Little) { |
| return writeELFBinaryToFile<ELF64LE>(FilePath, Stub, WriteIfChanged); |
| } else { |
| return writeELFBinaryToFile<ELF64BE>(FilePath, Stub, WriteIfChanged); |
| } |
| } |
| llvm_unreachable("invalid binary output target"); |
| } |
| |
| } // end namespace ifs |
| } // end namespace llvm |