1 //===------ JITLinkGeneric.h - Generic JIT linker utilities -----*- 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 JITLinker utilities. E.g. graph pruning, eh-frame parsing. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LIB_EXECUTIONENGINE_JITLINK_JITLINKGENERIC_H 14 #define LIB_EXECUTIONENGINE_JITLINK_JITLINKGENERIC_H 15 16 #include "llvm/ADT/DenseSet.h" 17 #include "llvm/ExecutionEngine/JITLink/JITLink.h" 18 19 #define DEBUG_TYPE "jitlink" 20 21 namespace llvm { 22 23 class MemoryBufferRef; 24 25 namespace jitlink { 26 27 /// Base class for a JIT linker. 28 /// 29 /// A JITLinkerBase instance links one object file into an ongoing JIT 30 /// session. Symbol resolution and finalization operations are pluggable, 31 /// and called using continuation passing (passing a continuation for the 32 /// remaining linker work) to allow them to be performed asynchronously. 33 class JITLinkerBase { 34 public: 35 JITLinkerBase(std::unique_ptr<JITLinkContext> Ctx, PassConfiguration Passes) 36 : Ctx(std::move(Ctx)), Passes(std::move(Passes)) { 37 assert(this->Ctx && "Ctx can not be null"); 38 } 39 40 virtual ~JITLinkerBase(); 41 42 protected: 43 struct SegmentLayout { 44 using SectionAtomsList = std::vector<DefinedAtom *>; 45 struct SectionLayout { 46 SectionLayout(Section &S) : S(&S) {} 47 48 Section *S; 49 SectionAtomsList Atoms; 50 }; 51 52 using SectionLayoutList = std::vector<SectionLayout>; 53 54 SectionLayoutList ContentSections; 55 SectionLayoutList ZeroFillSections; 56 }; 57 58 using SegmentLayoutMap = DenseMap<unsigned, SegmentLayout>; 59 60 // Phase 1: 61 // 1.1: Build atom graph 62 // 1.2: Run pre-prune passes 63 // 1.2: Prune graph 64 // 1.3: Run post-prune passes 65 // 1.4: Sort atoms into segments 66 // 1.5: Allocate segment memory 67 // 1.6: Identify externals and make an async call to resolve function 68 void linkPhase1(std::unique_ptr<JITLinkerBase> Self); 69 70 // Phase 2: 71 // 2.1: Apply resolution results 72 // 2.2: Fix up atom contents 73 // 2.3: Call OnResolved callback 74 // 2.3: Make an async call to transfer and finalize memory. 75 void linkPhase2(std::unique_ptr<JITLinkerBase> Self, 76 Expected<AsyncLookupResult> LookupResult); 77 78 // Phase 3: 79 // 3.1: Call OnFinalized callback, handing off allocation. 80 void linkPhase3(std::unique_ptr<JITLinkerBase> Self, Error Err); 81 82 // Build a graph from the given object buffer. 83 // To be implemented by the client. 84 virtual Expected<std::unique_ptr<AtomGraph>> 85 buildGraph(MemoryBufferRef ObjBuffer) = 0; 86 87 // For debug dumping of the atom graph. 88 virtual StringRef getEdgeKindName(Edge::Kind K) const = 0; 89 90 private: 91 // Run all passes in the given pass list, bailing out immediately if any pass 92 // returns an error. 93 Error runPasses(AtomGraphPassList &Passes, AtomGraph &G); 94 95 // Copy atom contents and apply relocations. 96 // Implemented in JITLinker. 97 virtual Error 98 copyAndFixUpAllAtoms(const SegmentLayoutMap &Layout, 99 JITLinkMemoryManager::Allocation &Alloc) const = 0; 100 101 void layOutAtoms(); 102 Error allocateSegments(const SegmentLayoutMap &Layout); 103 DenseSet<StringRef> getExternalSymbolNames() const; 104 void applyLookupResult(AsyncLookupResult LR); 105 void deallocateAndBailOut(Error Err); 106 107 void dumpGraph(raw_ostream &OS); 108 109 std::unique_ptr<JITLinkContext> Ctx; 110 PassConfiguration Passes; 111 std::unique_ptr<AtomGraph> G; 112 SegmentLayoutMap Layout; 113 std::unique_ptr<JITLinkMemoryManager::Allocation> Alloc; 114 }; 115 116 template <typename LinkerImpl> class JITLinker : public JITLinkerBase { 117 public: 118 using JITLinkerBase::JITLinkerBase; 119 120 /// Link constructs a LinkerImpl instance and calls linkPhase1. 121 /// Link should be called with the constructor arguments for LinkerImpl, which 122 /// will be forwarded to the constructor. 123 template <typename... ArgTs> static void link(ArgTs &&... Args) { 124 auto L = llvm::make_unique<LinkerImpl>(std::forward<ArgTs>(Args)...); 125 126 // Ownership of the linker is passed into the linker's doLink function to 127 // allow it to be passed on to async continuations. 128 // 129 // FIXME: Remove LTmp once we have c++17. 130 // C++17 sequencing rules guarantee that function name expressions are 131 // sequenced before arguments, so L->linkPhase1(std::move(L), ...) will be 132 // well formed. 133 auto <mp = *L; 134 LTmp.linkPhase1(std::move(L)); 135 } 136 137 private: 138 const LinkerImpl &impl() const { 139 return static_cast<const LinkerImpl &>(*this); 140 } 141 142 Error 143 copyAndFixUpAllAtoms(const SegmentLayoutMap &Layout, 144 JITLinkMemoryManager::Allocation &Alloc) const override { 145 LLVM_DEBUG(dbgs() << "Copying and fixing up atoms:\n"); 146 for (auto &KV : Layout) { 147 auto &Prot = KV.first; 148 auto &SegLayout = KV.second; 149 150 auto SegMem = Alloc.getWorkingMemory( 151 static_cast<sys::Memory::ProtectionFlags>(Prot)); 152 char *LastAtomEnd = SegMem.data(); 153 char *AtomDataPtr = LastAtomEnd; 154 155 LLVM_DEBUG({ 156 dbgs() << " Processing segment " 157 << static_cast<sys::Memory::ProtectionFlags>(Prot) << " [ " 158 << (const void *)SegMem.data() << " .. " 159 << (const void *)((char *)SegMem.data() + SegMem.size()) 160 << " ]\n Processing content sections:\n"; 161 }); 162 163 for (auto &SI : SegLayout.ContentSections) { 164 LLVM_DEBUG(dbgs() << " " << SI.S->getName() << ":\n"); 165 166 AtomDataPtr += alignmentAdjustment(AtomDataPtr, SI.S->getAlignment()); 167 168 LLVM_DEBUG({ 169 dbgs() << " Bumped atom pointer to " << (const void *)AtomDataPtr 170 << " to meet section alignment " 171 << " of " << SI.S->getAlignment() << "\n"; 172 }); 173 174 for (auto *DA : SI.Atoms) { 175 176 // Align. 177 AtomDataPtr += alignmentAdjustment(AtomDataPtr, DA->getAlignment()); 178 LLVM_DEBUG({ 179 dbgs() << " Bumped atom pointer to " 180 << (const void *)AtomDataPtr << " to meet alignment of " 181 << DA->getAlignment() << "\n"; 182 }); 183 184 // Zero pad up to alignment. 185 LLVM_DEBUG({ 186 if (LastAtomEnd != AtomDataPtr) 187 dbgs() << " Zero padding from " << (const void *)LastAtomEnd 188 << " to " << (const void *)AtomDataPtr << "\n"; 189 }); 190 while (LastAtomEnd != AtomDataPtr) 191 *LastAtomEnd++ = 0; 192 193 // Copy initial atom content. 194 LLVM_DEBUG({ 195 dbgs() << " Copying atom " << *DA << " content, " 196 << DA->getContent().size() << " bytes, from " 197 << (const void *)DA->getContent().data() << " to " 198 << (const void *)AtomDataPtr << "\n"; 199 }); 200 memcpy(AtomDataPtr, DA->getContent().data(), DA->getContent().size()); 201 202 // Copy atom data and apply fixups. 203 LLVM_DEBUG(dbgs() << " Applying fixups.\n"); 204 for (auto &E : DA->edges()) { 205 206 // Skip non-relocation edges. 207 if (!E.isRelocation()) 208 continue; 209 210 // Dispatch to LinkerImpl for fixup. 211 if (auto Err = impl().applyFixup(*DA, E, AtomDataPtr)) 212 return Err; 213 } 214 215 // Point the atom's content to the fixed up buffer. 216 DA->setContent(StringRef(AtomDataPtr, DA->getContent().size())); 217 218 // Update atom end pointer. 219 LastAtomEnd = AtomDataPtr + DA->getContent().size(); 220 AtomDataPtr = LastAtomEnd; 221 } 222 } 223 224 // Zero pad the rest of the segment. 225 LLVM_DEBUG({ 226 dbgs() << " Zero padding end of segment from " 227 << (const void *)LastAtomEnd << " to " 228 << (const void *)((char *)SegMem.data() + SegMem.size()) << "\n"; 229 }); 230 while (LastAtomEnd != SegMem.data() + SegMem.size()) 231 *LastAtomEnd++ = 0; 232 } 233 234 return Error::success(); 235 } 236 }; 237 238 /// Dead strips and replaces discarded definitions with external atoms. 239 /// 240 /// Finds the set of nodes reachable from any node initially marked live 241 /// (nodes marked should-discard are treated as not live, even if they are 242 /// reachable). All nodes not marked as live at the end of this process, 243 /// are deleted. Nodes that are live, but marked should-discard are replaced 244 /// with external atoms and all edges to them are re-written. 245 void prune(AtomGraph &G); 246 247 Error addEHFrame(AtomGraph &G, Section &EHFrameSection, 248 StringRef EHFrameContent, JITTargetAddress EHFrameAddress, 249 Edge::Kind FDEToCIERelocKind, Edge::Kind FDEToTargetRelocKind); 250 251 } // end namespace jitlink 252 } // end namespace llvm 253 254 #undef DEBUG_TYPE // "jitlink" 255 256 #endif // LLVM_EXECUTIONENGINE_JITLINK_JITLINKGENERIC_H 257