| //===------- ELFLinkGraphBuilder.h - ELF LinkGraph builder ------*- C++ -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Generic ELF LinkGraph building code. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LIB_EXECUTIONENGINE_JITLINK_ELFLINKGRAPHBUILDER_H |
| #define LIB_EXECUTIONENGINE_JITLINK_ELFLINKGRAPHBUILDER_H |
| |
| #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
| #include "llvm/Object/ELF.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/FormatVariadic.h" |
| |
| #define DEBUG_TYPE "jitlink" |
| |
| namespace llvm { |
| namespace jitlink { |
| |
| /// Common link-graph building code shared between all ELFFiles. |
| class ELFLinkGraphBuilderBase { |
| public: |
| ELFLinkGraphBuilderBase(std::unique_ptr<LinkGraph> G) : G(std::move(G)) {} |
| virtual ~ELFLinkGraphBuilderBase(); |
| |
| protected: |
| static bool isDwarfSection(StringRef SectionName) { |
| return llvm::is_contained(DwarfSectionNames, SectionName); |
| } |
| |
| Section &getCommonSection() { |
| if (!CommonSection) |
| CommonSection = &G->createSection( |
| CommonSectionName, orc::MemProt::Read | orc::MemProt::Write); |
| return *CommonSection; |
| } |
| |
| std::unique_ptr<LinkGraph> G; |
| |
| private: |
| static StringRef CommonSectionName; |
| static ArrayRef<const char *> DwarfSectionNames; |
| |
| Section *CommonSection = nullptr; |
| }; |
| |
| /// Ling-graph building code that's specific to the given ELFT, but common |
| /// across all architectures. |
| template <typename ELFT> |
| class ELFLinkGraphBuilder : public ELFLinkGraphBuilderBase { |
| using ELFFile = object::ELFFile<ELFT>; |
| |
| public: |
| ELFLinkGraphBuilder(const object::ELFFile<ELFT> &Obj, Triple TT, |
| StringRef FileName, |
| LinkGraph::GetEdgeKindNameFunction GetEdgeKindName); |
| |
| /// Attempt to construct and return the LinkGraph. |
| Expected<std::unique_ptr<LinkGraph>> buildGraph(); |
| |
| /// Call to derived class to handle relocations. These require |
| /// architecture specific knowledge to map to JITLink edge kinds. |
| virtual Error addRelocations() = 0; |
| |
| protected: |
| using ELFSectionIndex = unsigned; |
| using ELFSymbolIndex = unsigned; |
| |
| bool isRelocatable() const { |
| return Obj.getHeader().e_type == llvm::ELF::ET_REL; |
| } |
| |
| void setGraphBlock(ELFSectionIndex SecIndex, Block *B) { |
| assert(!GraphBlocks.count(SecIndex) && "Duplicate section at index"); |
| GraphBlocks[SecIndex] = B; |
| } |
| |
| Block *getGraphBlock(ELFSectionIndex SecIndex) { |
| auto I = GraphBlocks.find(SecIndex); |
| if (I == GraphBlocks.end()) |
| return nullptr; |
| return I->second; |
| } |
| |
| void setGraphSymbol(ELFSymbolIndex SymIndex, Symbol &Sym) { |
| assert(!GraphSymbols.count(SymIndex) && "Duplicate symbol at index"); |
| GraphSymbols[SymIndex] = &Sym; |
| } |
| |
| Symbol *getGraphSymbol(ELFSymbolIndex SymIndex) { |
| auto I = GraphSymbols.find(SymIndex); |
| if (I == GraphSymbols.end()) |
| return nullptr; |
| return I->second; |
| } |
| |
| Expected<std::pair<Linkage, Scope>> |
| getSymbolLinkageAndScope(const typename ELFT::Sym &Sym, StringRef Name); |
| |
| Error prepare(); |
| Error graphifySections(); |
| Error graphifySymbols(); |
| |
| /// Traverse all matching ELFT::Rela relocation records in the given section. |
| /// The handler function Func should be callable with this signature: |
| /// Error(const typename ELFT::Rela &, |
| /// const typename ELFT::Shdr &, Section &) |
| /// |
| template <typename RelocHandlerMethod> |
| Error forEachRelaRelocation(const typename ELFT::Shdr &RelSect, |
| RelocHandlerMethod &&Func, |
| bool ProcessDebugSections = false); |
| |
| /// Traverse all matching ELFT::Rel relocation records in the given section. |
| /// The handler function Func should be callable with this signature: |
| /// Error(const typename ELFT::Rel &, |
| /// const typename ELFT::Shdr &, Section &) |
| /// |
| template <typename RelocHandlerMethod> |
| Error forEachRelRelocation(const typename ELFT::Shdr &RelSect, |
| RelocHandlerMethod &&Func, |
| bool ProcessDebugSections = false); |
| |
| /// Traverse all matching rela relocation records in the given section. |
| /// Convenience wrapper to allow passing a member function for the handler. |
| /// |
| template <typename ClassT, typename RelocHandlerMethod> |
| Error forEachRelaRelocation(const typename ELFT::Shdr &RelSect, |
| ClassT *Instance, RelocHandlerMethod &&Method, |
| bool ProcessDebugSections = false) { |
| return forEachRelaRelocation( |
| RelSect, |
| [Instance, Method](const auto &Rel, const auto &Target, auto &GS) { |
| return (Instance->*Method)(Rel, Target, GS); |
| }, |
| ProcessDebugSections); |
| } |
| |
| /// Traverse all matching rel relocation records in the given section. |
| /// Convenience wrapper to allow passing a member function for the handler. |
| /// |
| template <typename ClassT, typename RelocHandlerMethod> |
| Error forEachRelRelocation(const typename ELFT::Shdr &RelSect, |
| ClassT *Instance, RelocHandlerMethod &&Method, |
| bool ProcessDebugSections = false) { |
| return forEachRelRelocation( |
| RelSect, |
| [Instance, Method](const auto &Rel, const auto &Target, auto &GS) { |
| return (Instance->*Method)(Rel, Target, GS); |
| }, |
| ProcessDebugSections); |
| } |
| |
| const ELFFile &Obj; |
| |
| typename ELFFile::Elf_Shdr_Range Sections; |
| const typename ELFFile::Elf_Shdr *SymTabSec = nullptr; |
| StringRef SectionStringTab; |
| |
| // Maps ELF section indexes to LinkGraph Blocks. |
| // Only SHF_ALLOC sections will have graph blocks. |
| DenseMap<ELFSectionIndex, Block *> GraphBlocks; |
| DenseMap<ELFSymbolIndex, Symbol *> GraphSymbols; |
| DenseMap<const typename ELFFile::Elf_Shdr *, |
| ArrayRef<typename ELFFile::Elf_Word>> |
| ShndxTables; |
| }; |
| |
| template <typename ELFT> |
| ELFLinkGraphBuilder<ELFT>::ELFLinkGraphBuilder( |
| const ELFFile &Obj, Triple TT, StringRef FileName, |
| LinkGraph::GetEdgeKindNameFunction GetEdgeKindName) |
| : ELFLinkGraphBuilderBase(std::make_unique<LinkGraph>( |
| FileName.str(), Triple(std::move(TT)), ELFT::Is64Bits ? 8 : 4, |
| support::endianness(ELFT::TargetEndianness), |
| std::move(GetEdgeKindName))), |
| Obj(Obj) { |
| LLVM_DEBUG( |
| { dbgs() << "Created ELFLinkGraphBuilder for \"" << FileName << "\""; }); |
| } |
| |
| template <typename ELFT> |
| Expected<std::unique_ptr<LinkGraph>> ELFLinkGraphBuilder<ELFT>::buildGraph() { |
| if (!isRelocatable()) |
| return make_error<JITLinkError>("Object is not a relocatable ELF file"); |
| |
| if (auto Err = prepare()) |
| return std::move(Err); |
| |
| if (auto Err = graphifySections()) |
| return std::move(Err); |
| |
| if (auto Err = graphifySymbols()) |
| return std::move(Err); |
| |
| if (auto Err = addRelocations()) |
| return std::move(Err); |
| |
| return std::move(G); |
| } |
| |
| template <typename ELFT> |
| Expected<std::pair<Linkage, Scope>> |
| ELFLinkGraphBuilder<ELFT>::getSymbolLinkageAndScope( |
| const typename ELFT::Sym &Sym, StringRef Name) { |
| Linkage L = Linkage::Strong; |
| Scope S = Scope::Default; |
| |
| switch (Sym.getBinding()) { |
| case ELF::STB_LOCAL: |
| S = Scope::Local; |
| break; |
| case ELF::STB_GLOBAL: |
| // Nothing to do here. |
| break; |
| case ELF::STB_WEAK: |
| case ELF::STB_GNU_UNIQUE: |
| L = Linkage::Weak; |
| break; |
| default: |
| return make_error<StringError>( |
| "Unrecognized symbol binding " + |
| Twine(static_cast<int>(Sym.getBinding())) + " for " + Name, |
| inconvertibleErrorCode()); |
| } |
| |
| switch (Sym.getVisibility()) { |
| case ELF::STV_DEFAULT: |
| case ELF::STV_PROTECTED: |
| // FIXME: Make STV_DEFAULT symbols pre-emptible? This probably needs |
| // Orc support. |
| // Otherwise nothing to do here. |
| break; |
| case ELF::STV_HIDDEN: |
| // Default scope -> Hidden scope. No effect on local scope. |
| if (S == Scope::Default) |
| S = Scope::Hidden; |
| break; |
| case ELF::STV_INTERNAL: |
| return make_error<StringError>( |
| "Unrecognized symbol visibility " + |
| Twine(static_cast<int>(Sym.getVisibility())) + " for " + Name, |
| inconvertibleErrorCode()); |
| } |
| |
| return std::make_pair(L, S); |
| } |
| |
| template <typename ELFT> Error ELFLinkGraphBuilder<ELFT>::prepare() { |
| LLVM_DEBUG(dbgs() << " Preparing to build...\n"); |
| |
| // Get the sections array. |
| if (auto SectionsOrErr = Obj.sections()) |
| Sections = *SectionsOrErr; |
| else |
| return SectionsOrErr.takeError(); |
| |
| // Get the section string table. |
| if (auto SectionStringTabOrErr = Obj.getSectionStringTable(Sections)) |
| SectionStringTab = *SectionStringTabOrErr; |
| else |
| return SectionStringTabOrErr.takeError(); |
| |
| // Get the SHT_SYMTAB section. |
| for (auto &Sec : Sections) { |
| if (Sec.sh_type == ELF::SHT_SYMTAB) { |
| if (!SymTabSec) |
| SymTabSec = &Sec; |
| else |
| return make_error<JITLinkError>("Multiple SHT_SYMTAB sections in " + |
| G->getName()); |
| } |
| |
| // Extended table. |
| if (Sec.sh_type == ELF::SHT_SYMTAB_SHNDX) { |
| uint32_t SymtabNdx = Sec.sh_link; |
| if (SymtabNdx >= Sections.size()) |
| return make_error<JITLinkError>("sh_link is out of bound"); |
| |
| auto ShndxTable = Obj.getSHNDXTable(Sec); |
| if (!ShndxTable) |
| return ShndxTable.takeError(); |
| |
| ShndxTables.insert({&Sections[SymtabNdx], *ShndxTable}); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| template <typename ELFT> Error ELFLinkGraphBuilder<ELFT>::graphifySections() { |
| LLVM_DEBUG(dbgs() << " Creating graph sections...\n"); |
| |
| // For each section... |
| for (ELFSectionIndex SecIndex = 0; SecIndex != Sections.size(); ++SecIndex) { |
| |
| auto &Sec = Sections[SecIndex]; |
| |
| // Start by getting the section name. |
| auto Name = Obj.getSectionName(Sec, SectionStringTab); |
| if (!Name) |
| return Name.takeError(); |
| |
| // If the name indicates that it's a debug section then skip it: We don't |
| // support those yet. |
| if (isDwarfSection(*Name)) { |
| LLVM_DEBUG({ |
| dbgs() << " " << SecIndex << ": \"" << *Name |
| << "\" is a debug section: " |
| "No graph section will be created.\n"; |
| }); |
| continue; |
| } |
| |
| // Skip non-SHF_ALLOC sections |
| if (!(Sec.sh_flags & ELF::SHF_ALLOC)) { |
| LLVM_DEBUG({ |
| dbgs() << " " << SecIndex << ": \"" << *Name |
| << "\" is not an SHF_ALLOC section: " |
| "No graph section will be created.\n"; |
| }); |
| continue; |
| } |
| |
| LLVM_DEBUG({ |
| dbgs() << " " << SecIndex << ": Creating section for \"" << *Name |
| << "\"\n"; |
| }); |
| |
| // Get the section's memory protection flags. |
| orc::MemProt Prot; |
| if (Sec.sh_flags & ELF::SHF_EXECINSTR) |
| Prot = orc::MemProt::Read | orc::MemProt::Exec; |
| else |
| Prot = orc::MemProt::Read | orc::MemProt::Write; |
| |
| // Look for existing sections first. |
| auto *GraphSec = G->findSectionByName(*Name); |
| if (!GraphSec) |
| GraphSec = &G->createSection(*Name, Prot); |
| assert(GraphSec->getMemProt() == Prot && "MemProt should match"); |
| |
| Block *B = nullptr; |
| if (Sec.sh_type != ELF::SHT_NOBITS) { |
| auto Data = Obj.template getSectionContentsAsArray<char>(Sec); |
| if (!Data) |
| return Data.takeError(); |
| |
| B = &G->createContentBlock(*GraphSec, *Data, |
| orc::ExecutorAddr(Sec.sh_addr), |
| Sec.sh_addralign, 0); |
| } else |
| B = &G->createZeroFillBlock(*GraphSec, Sec.sh_size, |
| orc::ExecutorAddr(Sec.sh_addr), |
| Sec.sh_addralign, 0); |
| |
| setGraphBlock(SecIndex, B); |
| } |
| |
| return Error::success(); |
| } |
| |
| template <typename ELFT> Error ELFLinkGraphBuilder<ELFT>::graphifySymbols() { |
| LLVM_DEBUG(dbgs() << " Creating graph symbols...\n"); |
| |
| // No SYMTAB -- Bail out early. |
| if (!SymTabSec) |
| return Error::success(); |
| |
| // Get the section content as a Symbols array. |
| auto Symbols = Obj.symbols(SymTabSec); |
| if (!Symbols) |
| return Symbols.takeError(); |
| |
| // Get the string table for this section. |
| auto StringTab = Obj.getStringTableForSymtab(*SymTabSec, Sections); |
| if (!StringTab) |
| return StringTab.takeError(); |
| |
| LLVM_DEBUG({ |
| StringRef SymTabName; |
| |
| if (auto SymTabNameOrErr = Obj.getSectionName(*SymTabSec, SectionStringTab)) |
| SymTabName = *SymTabNameOrErr; |
| else { |
| dbgs() << "Could not get ELF SHT_SYMTAB section name for logging: " |
| << toString(SymTabNameOrErr.takeError()) << "\n"; |
| SymTabName = "<SHT_SYMTAB section with invalid name>"; |
| } |
| |
| dbgs() << " Adding symbols from symtab section \"" << SymTabName |
| << "\"\n"; |
| }); |
| |
| for (ELFSymbolIndex SymIndex = 0; SymIndex != Symbols->size(); ++SymIndex) { |
| auto &Sym = (*Symbols)[SymIndex]; |
| |
| // Check symbol type. |
| switch (Sym.getType()) { |
| case ELF::STT_FILE: |
| LLVM_DEBUG({ |
| if (auto Name = Sym.getName(*StringTab)) |
| dbgs() << " " << SymIndex << ": Skipping STT_FILE symbol \"" |
| << *Name << "\"\n"; |
| else { |
| dbgs() << "Could not get STT_FILE symbol name: " |
| << toString(Name.takeError()) << "\n"; |
| dbgs() << " " << SymIndex |
| << ": Skipping STT_FILE symbol with invalid name\n"; |
| } |
| }); |
| continue; |
| break; |
| } |
| |
| // Get the symbol name. |
| auto Name = Sym.getName(*StringTab); |
| if (!Name) |
| return Name.takeError(); |
| |
| // Handle common symbols specially. |
| if (Sym.isCommon()) { |
| Symbol &GSym = G->addDefinedSymbol( |
| G->createZeroFillBlock(getCommonSection(), Sym.st_size, |
| orc::ExecutorAddr(), Sym.getValue(), 0), |
| 0, *Name, Sym.st_size, Linkage::Strong, Scope::Default, false, false); |
| setGraphSymbol(SymIndex, GSym); |
| continue; |
| } |
| |
| if (Sym.isDefined() && |
| (Sym.getType() == ELF::STT_NOTYPE || Sym.getType() == ELF::STT_FUNC || |
| Sym.getType() == ELF::STT_OBJECT || |
| Sym.getType() == ELF::STT_SECTION || Sym.getType() == ELF::STT_TLS)) { |
| |
| // Map Visibility and Binding to Scope and Linkage: |
| Linkage L; |
| Scope S; |
| if (auto LSOrErr = getSymbolLinkageAndScope(Sym, *Name)) |
| std::tie(L, S) = *LSOrErr; |
| else |
| return LSOrErr.takeError(); |
| |
| // Handle extended tables. |
| unsigned Shndx = Sym.st_shndx; |
| if (Shndx == ELF::SHN_XINDEX) { |
| auto ShndxTable = ShndxTables.find(SymTabSec); |
| if (ShndxTable == ShndxTables.end()) |
| continue; |
| auto NdxOrErr = object::getExtendedSymbolTableIndex<ELFT>( |
| Sym, SymIndex, ShndxTable->second); |
| if (!NdxOrErr) |
| return NdxOrErr.takeError(); |
| Shndx = *NdxOrErr; |
| } |
| if (auto *B = getGraphBlock(Shndx)) { |
| LLVM_DEBUG({ |
| dbgs() << " " << SymIndex |
| << ": Creating defined graph symbol for ELF symbol \"" << *Name |
| << "\"\n"; |
| }); |
| |
| // In RISCV, temporary symbols (Used to generate dwarf, eh_frame |
| // sections...) will appear in object code's symbol table, and LLVM does |
| // not use names on these temporary symbols (RISCV gnu toolchain uses |
| // names on these temporary symbols). If the symbol is unnamed, add an |
| // anonymous symbol. |
| auto &GSym = |
| Name->empty() |
| ? G->addAnonymousSymbol(*B, Sym.getValue(), Sym.st_size, |
| false, false) |
| : G->addDefinedSymbol(*B, Sym.getValue(), *Name, Sym.st_size, L, |
| S, Sym.getType() == ELF::STT_FUNC, false); |
| setGraphSymbol(SymIndex, GSym); |
| } |
| } else if (Sym.isUndefined() && Sym.isExternal()) { |
| LLVM_DEBUG({ |
| dbgs() << " " << SymIndex |
| << ": Creating external graph symbol for ELF symbol \"" << *Name |
| << "\"\n"; |
| }); |
| |
| if (Sym.getBinding() != ELF::STB_GLOBAL && |
| Sym.getBinding() != ELF::STB_WEAK) |
| return make_error<StringError>( |
| "Invalid symbol binding " + |
| Twine(static_cast<int>(Sym.getBinding())) + |
| " for external symbol " + *Name, |
| inconvertibleErrorCode()); |
| |
| // If L is Linkage::Weak that means this is a weakly referenced symbol. |
| auto &GSym = G->addExternalSymbol(*Name, Sym.st_size, |
| Sym.getBinding() == ELF::STB_WEAK); |
| setGraphSymbol(SymIndex, GSym); |
| } else { |
| LLVM_DEBUG({ |
| dbgs() << " " << SymIndex |
| << ": Not creating graph symbol for ELF symbol \"" << *Name |
| << "\" with unrecognized type\n"; |
| }); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| template <typename ELFT> |
| template <typename RelocHandlerFunction> |
| Error ELFLinkGraphBuilder<ELFT>::forEachRelaRelocation( |
| const typename ELFT::Shdr &RelSect, RelocHandlerFunction &&Func, |
| bool ProcessDebugSections) { |
| // Only look into sections that store relocation entries. |
| if (RelSect.sh_type != ELF::SHT_RELA) |
| return Error::success(); |
| |
| // sh_info contains the section header index of the target (FixupSection), |
| // which is the section to which all relocations in RelSect apply. |
| auto FixupSection = Obj.getSection(RelSect.sh_info); |
| if (!FixupSection) |
| return FixupSection.takeError(); |
| |
| // Target sections have names in valid ELF object files. |
| Expected<StringRef> Name = Obj.getSectionName(**FixupSection); |
| if (!Name) |
| return Name.takeError(); |
| LLVM_DEBUG(dbgs() << " " << *Name << ":\n"); |
| |
| // Consider skipping these relocations. |
| if (!ProcessDebugSections && isDwarfSection(*Name)) { |
| LLVM_DEBUG(dbgs() << " skipped (dwarf section)\n\n"); |
| return Error::success(); |
| } |
| |
| // Lookup the link-graph node corresponding to the target section name. |
| auto *BlockToFix = getGraphBlock(RelSect.sh_info); |
| if (!BlockToFix) |
| return make_error<StringError>( |
| "Refencing a section that wasn't added to the graph: " + *Name, |
| inconvertibleErrorCode()); |
| |
| auto RelEntries = Obj.relas(RelSect); |
| if (!RelEntries) |
| return RelEntries.takeError(); |
| |
| // Let the callee process relocation entries one by one. |
| for (const typename ELFT::Rela &R : *RelEntries) |
| if (Error Err = Func(R, **FixupSection, *BlockToFix)) |
| return Err; |
| |
| LLVM_DEBUG(dbgs() << "\n"); |
| return Error::success(); |
| } |
| |
| template <typename ELFT> |
| template <typename RelocHandlerFunction> |
| Error ELFLinkGraphBuilder<ELFT>::forEachRelRelocation( |
| const typename ELFT::Shdr &RelSect, RelocHandlerFunction &&Func, |
| bool ProcessDebugSections) { |
| // Only look into sections that store relocation entries. |
| if (RelSect.sh_type != ELF::SHT_REL) |
| return Error::success(); |
| |
| // sh_info contains the section header index of the target (FixupSection), |
| // which is the section to which all relocations in RelSect apply. |
| auto FixupSection = Obj.getSection(RelSect.sh_info); |
| if (!FixupSection) |
| return FixupSection.takeError(); |
| |
| // Target sections have names in valid ELF object files. |
| Expected<StringRef> Name = Obj.getSectionName(**FixupSection); |
| if (!Name) |
| return Name.takeError(); |
| LLVM_DEBUG(dbgs() << " " << *Name << ":\n"); |
| |
| // Consider skipping these relocations. |
| if (!ProcessDebugSections && isDwarfSection(*Name)) { |
| LLVM_DEBUG(dbgs() << " skipped (dwarf section)\n\n"); |
| return Error::success(); |
| } |
| |
| // Lookup the link-graph node corresponding to the target section name. |
| auto *BlockToFix = getGraphBlock(RelSect.sh_info); |
| if (!BlockToFix) |
| return make_error<StringError>( |
| "Refencing a section that wasn't added to the graph: " + *Name, |
| inconvertibleErrorCode()); |
| |
| auto RelEntries = Obj.rels(RelSect); |
| if (!RelEntries) |
| return RelEntries.takeError(); |
| |
| // Let the callee process relocation entries one by one. |
| for (const typename ELFT::Rel &R : *RelEntries) |
| if (Error Err = Func(R, **FixupSection, *BlockToFix)) |
| return Err; |
| |
| LLVM_DEBUG(dbgs() << "\n"); |
| return Error::success(); |
| } |
| |
| } // end namespace jitlink |
| } // end namespace llvm |
| |
| #undef DEBUG_TYPE |
| |
| #endif // LIB_EXECUTIONENGINE_JITLINK_ELFLINKGRAPHBUILDER_H |