| 1 | //===----- COFFLinkGraphBuilder.h - COFF LinkGraph builder ----*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Generic COFF LinkGraph building code. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #ifndef LIB_EXECUTIONENGINE_JITLINK_COFFLINKGRAPHBUILDER_H |
| 14 | #define LIB_EXECUTIONENGINE_JITLINK_COFFLINKGRAPHBUILDER_H |
| 15 | |
| 16 | #include "llvm/ADT/DenseMap.h" |
| 17 | #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
| 18 | #include "llvm/Object/COFF.h" |
| 19 | |
| 20 | #include "COFFDirectiveParser.h" |
| 21 | #include "EHFrameSupportImpl.h" |
| 22 | #include "JITLinkGeneric.h" |
| 23 | |
| 24 | #define DEBUG_TYPE "jitlink" |
| 25 | |
| 26 | #include <list> |
| 27 | |
| 28 | namespace llvm { |
| 29 | namespace jitlink { |
| 30 | |
| 31 | class COFFLinkGraphBuilder { |
| 32 | public: |
| 33 | virtual ~COFFLinkGraphBuilder(); |
| 34 | Expected<std::unique_ptr<LinkGraph>> buildGraph(); |
| 35 | |
| 36 | protected: |
| 37 | using COFFSectionIndex = int32_t; |
| 38 | using COFFSymbolIndex = int32_t; |
| 39 | |
| 40 | COFFLinkGraphBuilder(const object::COFFObjectFile &Obj, |
| 41 | std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT, |
| 42 | SubtargetFeatures Features, |
| 43 | LinkGraph::GetEdgeKindNameFunction GetEdgeKindName); |
| 44 | |
| 45 | LinkGraph &getGraph() const { return *G; } |
| 46 | |
| 47 | const object::COFFObjectFile &getObject() const { return Obj; } |
| 48 | |
| 49 | virtual Error addRelocations() = 0; |
| 50 | |
| 51 | Error graphifySections(); |
| 52 | Error graphifySymbols(); |
| 53 | |
| 54 | void setGraphSymbol(COFFSectionIndex SecIndex, COFFSymbolIndex SymIndex, |
| 55 | Symbol &Sym) { |
| 56 | assert(!GraphSymbols[SymIndex] && "Duplicate symbol at index" ); |
| 57 | GraphSymbols[SymIndex] = &Sym; |
| 58 | if (!COFF::isReservedSectionNumber(SectionNumber: SecIndex)) |
| 59 | SymbolSets[SecIndex].insert(x: {Sym.getOffset(), &Sym}); |
| 60 | } |
| 61 | |
| 62 | Symbol *getGraphSymbol(COFFSymbolIndex SymIndex) const { |
| 63 | if (SymIndex < 0 || |
| 64 | SymIndex >= static_cast<COFFSymbolIndex>(GraphSymbols.size())) |
| 65 | return nullptr; |
| 66 | return GraphSymbols[SymIndex]; |
| 67 | } |
| 68 | |
| 69 | void setGraphBlock(COFFSectionIndex SecIndex, Block *B) { |
| 70 | assert(!GraphBlocks[SecIndex] && "Duplicate section at index" ); |
| 71 | assert(!COFF::isReservedSectionNumber(SecIndex) && "Invalid section index" ); |
| 72 | GraphBlocks[SecIndex] = B; |
| 73 | } |
| 74 | |
| 75 | Block *getGraphBlock(COFFSectionIndex SecIndex) const { |
| 76 | if (SecIndex <= 0 || |
| 77 | SecIndex >= static_cast<COFFSectionIndex>(GraphSymbols.size())) |
| 78 | return nullptr; |
| 79 | return GraphBlocks[SecIndex]; |
| 80 | } |
| 81 | |
| 82 | Symbol &addImageBaseSymbol(StringRef Name = "__ImageBase" ) { |
| 83 | auto &ImageBase = G->addExternalSymbol(Name: G->intern(SymbolName: Name), Size: 0, IsWeaklyReferenced: true); |
| 84 | ImageBase.setLive(true); |
| 85 | return ImageBase; |
| 86 | } |
| 87 | |
| 88 | object::COFFObjectFile::section_iterator_range sections() const { |
| 89 | return Obj.sections(); |
| 90 | } |
| 91 | |
| 92 | /// Traverse all matching relocation records in the given section. The handler |
| 93 | /// function Func should be callable with this signature: |
| 94 | /// Error(const object::RelocationRef&, |
| 95 | /// const object::SectionRef&, Section &) |
| 96 | /// |
| 97 | template <typename RelocHandlerFunction> |
| 98 | Error forEachRelocation(const object::SectionRef &RelSec, |
| 99 | RelocHandlerFunction &&Func, |
| 100 | bool ProcessDebugSections = false); |
| 101 | |
| 102 | /// Traverse all matching relocation records in the given section. Convenience |
| 103 | /// wrapper to allow passing a member function for the handler. |
| 104 | /// |
| 105 | template <typename ClassT, typename RelocHandlerMethod> |
| 106 | Error forEachRelocation(const object::SectionRef &RelSec, ClassT *Instance, |
| 107 | RelocHandlerMethod &&Method, |
| 108 | bool ProcessDebugSections = false) { |
| 109 | return forEachRelocation( |
| 110 | RelSec, |
| 111 | [Instance, Method](const auto &Rel, const auto &Target, auto &GS) { |
| 112 | return (Instance->*Method)(Rel, Target, GS); |
| 113 | }, |
| 114 | ProcessDebugSections); |
| 115 | } |
| 116 | |
| 117 | private: |
| 118 | // Pending comdat symbol export that is initiated by the first symbol of |
| 119 | // COMDAT sequence. |
| 120 | struct ComdatExportRequest { |
| 121 | COFFSymbolIndex SymbolIndex; |
| 122 | jitlink::Linkage Linkage; |
| 123 | orc::ExecutorAddrDiff Size; |
| 124 | }; |
| 125 | std::vector<std::optional<ComdatExportRequest>> PendingComdatExports; |
| 126 | |
| 127 | // This represents a pending request to create a weak external symbol with a |
| 128 | // name. |
| 129 | struct WeakExternalRequest { |
| 130 | COFFSymbolIndex Alias; |
| 131 | COFFSymbolIndex Target; |
| 132 | uint32_t Characteristics; |
| 133 | StringRef SymbolName; |
| 134 | }; |
| 135 | std::vector<WeakExternalRequest> WeakExternalRequests; |
| 136 | |
| 137 | // Per COFF section jitlink symbol set sorted by offset. |
| 138 | // Used for calculating implicit size of defined symbols. |
| 139 | using SymbolSet = std::set<std::pair<orc::ExecutorAddrDiff, Symbol *>>; |
| 140 | std::vector<SymbolSet> SymbolSets; |
| 141 | |
| 142 | Section &getCommonSection(); |
| 143 | |
| 144 | Symbol *createExternalSymbol(COFFSymbolIndex SymIndex, |
| 145 | orc::SymbolStringPtr SymbolName, |
| 146 | object::COFFSymbolRef Symbol, |
| 147 | const object::coff_section *Section); |
| 148 | Expected<Symbol *> createAliasSymbol(orc::SymbolStringPtr SymbolName, |
| 149 | Linkage L, Scope S, Symbol &Target); |
| 150 | Expected<Symbol *> createDefinedSymbol(COFFSymbolIndex SymIndex, |
| 151 | orc::SymbolStringPtr SymbolName, |
| 152 | object::COFFSymbolRef Symbol, |
| 153 | const object::coff_section *Section); |
| 154 | Expected<Symbol *> createCOMDATExportRequest( |
| 155 | COFFSymbolIndex SymIndex, object::COFFSymbolRef Symbol, |
| 156 | const object::coff_aux_section_definition *Definition); |
| 157 | Expected<Symbol *> exportCOMDATSymbol(COFFSymbolIndex SymIndex, |
| 158 | orc::SymbolStringPtr SymbolName, |
| 159 | object::COFFSymbolRef Symbol); |
| 160 | |
| 161 | Error handleDirectiveSection(StringRef Str); |
| 162 | Error flushWeakAliasRequests(); |
| 163 | Error handleAlternateNames(); |
| 164 | Error calculateImplicitSizeOfSymbols(); |
| 165 | |
| 166 | static uint64_t getSectionAddress(const object::COFFObjectFile &Obj, |
| 167 | const object::coff_section *Section); |
| 168 | static uint64_t getSectionSize(const object::COFFObjectFile &Obj, |
| 169 | const object::coff_section *Section); |
| 170 | static bool isComdatSection(const object::coff_section *Section); |
| 171 | static unsigned getPointerSize(const object::COFFObjectFile &Obj); |
| 172 | static llvm::endianness getEndianness(const object::COFFObjectFile &Obj); |
| 173 | static StringRef getDLLImportStubPrefix() { return "__imp_" ; } |
| 174 | static StringRef getDirectiveSectionName() { return ".drectve" ; } |
| 175 | StringRef getCOFFSectionName(COFFSectionIndex SectionIndex, |
| 176 | const object::coff_section *Sec, |
| 177 | object::COFFSymbolRef Sym); |
| 178 | |
| 179 | const object::COFFObjectFile &Obj; |
| 180 | std::unique_ptr<LinkGraph> G; |
| 181 | COFFDirectiveParser DirectiveParser; |
| 182 | |
| 183 | Section *CommonSection = nullptr; |
| 184 | std::vector<Block *> GraphBlocks; |
| 185 | std::vector<Symbol *> GraphSymbols; |
| 186 | |
| 187 | DenseMap<orc::SymbolStringPtr, orc::SymbolStringPtr> AlternateNames; |
| 188 | DenseMap<orc::SymbolStringPtr, Symbol *> ExternalSymbols; |
| 189 | DenseMap<orc::SymbolStringPtr, Symbol *> DefinedSymbols; |
| 190 | }; |
| 191 | |
| 192 | template <typename RelocHandlerFunction> |
| 193 | Error COFFLinkGraphBuilder::forEachRelocation(const object::SectionRef &RelSec, |
| 194 | RelocHandlerFunction &&Func, |
| 195 | bool ProcessDebugSections) { |
| 196 | |
| 197 | auto COFFRelSect = Obj.getCOFFSection(Section: RelSec); |
| 198 | |
| 199 | // Target sections have names in valid COFF object files. |
| 200 | Expected<StringRef> Name = Obj.getSectionName(Sec: COFFRelSect); |
| 201 | if (!Name) |
| 202 | return Name.takeError(); |
| 203 | |
| 204 | // Skip the unhandled metadata sections. |
| 205 | if (*Name == ".voltbl" ) |
| 206 | return Error::success(); |
| 207 | LLVM_DEBUG(dbgs() << " " << *Name << ":\n" ); |
| 208 | |
| 209 | // Lookup the link-graph node corresponding to the target section name. |
| 210 | auto *BlockToFix = getGraphBlock(SecIndex: RelSec.getIndex() + 1); |
| 211 | if (!BlockToFix) |
| 212 | return make_error<StringError>( |
| 213 | Args: "Referencing a section that wasn't added to the graph: " + *Name, |
| 214 | Args: inconvertibleErrorCode()); |
| 215 | |
| 216 | // Let the callee process relocation entries one by one. |
| 217 | for (const auto &R : RelSec.relocations()) |
| 218 | if (Error Err = Func(R, RelSec, *BlockToFix)) |
| 219 | return Err; |
| 220 | |
| 221 | LLVM_DEBUG(dbgs() << "\n" ); |
| 222 | return Error::success(); |
| 223 | } |
| 224 | |
| 225 | class GetImageBaseSymbol { |
| 226 | public: |
| 227 | GetImageBaseSymbol(StringRef ImageBaseName = "__ImageBase" ) |
| 228 | : ImageBaseName(ImageBaseName) {} |
| 229 | Symbol *operator()(LinkGraph &G); |
| 230 | void reset() { ImageBase = std::nullopt; } |
| 231 | |
| 232 | private: |
| 233 | StringRef ImageBaseName; |
| 234 | std::optional<Symbol *> ImageBase; |
| 235 | }; |
| 236 | |
| 237 | } // end namespace jitlink |
| 238 | } // end namespace llvm |
| 239 | |
| 240 | #endif // LIB_EXECUTIONENGINE_JITLINK_COFFLINKGRAPHBUILDER_H |
| 241 | |