1 //===------- ObjectLinkingLayer.cpp - JITLink backed ORC ObjectLayer ------===// 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 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" 10 11 #include "llvm/ADT/Optional.h" 12 #include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h" 13 14 #include <vector> 15 16 #define DEBUG_TYPE "orc" 17 18 using namespace llvm; 19 using namespace llvm::jitlink; 20 using namespace llvm::orc; 21 22 namespace llvm { 23 namespace orc { 24 25 class ObjectLinkingLayerJITLinkContext final : public JITLinkContext { 26 public: 27 ObjectLinkingLayerJITLinkContext(ObjectLinkingLayer &Layer, 28 MaterializationResponsibility MR, 29 std::unique_ptr<MemoryBuffer> ObjBuffer) 30 : Layer(Layer), MR(std::move(MR)), ObjBuffer(std::move(ObjBuffer)) {} 31 32 JITLinkMemoryManager &getMemoryManager() override { return Layer.MemMgr; } 33 34 MemoryBufferRef getObjectBuffer() const override { 35 return ObjBuffer->getMemBufferRef(); 36 } 37 38 void notifyFailed(Error Err) override { 39 Layer.getExecutionSession().reportError(std::move(Err)); 40 MR.failMaterialization(); 41 } 42 43 void lookup(const DenseSet<StringRef> &Symbols, 44 JITLinkAsyncLookupContinuation LookupContinuation) override { 45 46 JITDylibSearchList SearchOrder; 47 MR.getTargetJITDylib().withSearchOrderDo( 48 [&](const JITDylibSearchList &JDs) { SearchOrder = JDs; }); 49 50 auto &ES = Layer.getExecutionSession(); 51 52 SymbolNameSet InternedSymbols; 53 for (auto &S : Symbols) 54 InternedSymbols.insert(ES.intern(S)); 55 56 // OnResolve -- De-intern the symbols and pass the result to the linker. 57 // FIXME: Capture LookupContinuation by move once we have c++14. 58 auto SharedLookupContinuation = 59 std::make_shared<JITLinkAsyncLookupContinuation>( 60 std::move(LookupContinuation)); 61 auto OnResolve = [SharedLookupContinuation](Expected<SymbolMap> Result) { 62 if (!Result) 63 (*SharedLookupContinuation)(Result.takeError()); 64 else { 65 AsyncLookupResult LR; 66 for (auto &KV : *Result) 67 LR[*KV.first] = KV.second; 68 (*SharedLookupContinuation)(std::move(LR)); 69 } 70 }; 71 72 ES.lookup( 73 SearchOrder, std::move(InternedSymbols), std::move(OnResolve), 74 // OnReady: 75 [&ES](Error Err) { ES.reportError(std::move(Err)); }, 76 // RegisterDependencies: 77 [this](const SymbolDependenceMap &Deps) { 78 registerDependencies(Deps); 79 }); 80 } 81 82 void notifyResolved(AtomGraph &G) override { 83 auto &ES = Layer.getExecutionSession(); 84 85 SymbolFlagsMap ExtraSymbolsToClaim; 86 bool AutoClaim = Layer.AutoClaimObjectSymbols; 87 88 SymbolMap InternedResult; 89 for (auto *DA : G.defined_atoms()) 90 if (DA->hasName() && DA->isGlobal()) { 91 auto InternedName = ES.intern(DA->getName()); 92 JITSymbolFlags Flags; 93 94 if (DA->isExported()) 95 Flags |= JITSymbolFlags::Exported; 96 if (DA->isWeak()) 97 Flags |= JITSymbolFlags::Weak; 98 if (DA->isCallable()) 99 Flags |= JITSymbolFlags::Callable; 100 if (DA->isCommon()) 101 Flags |= JITSymbolFlags::Common; 102 103 InternedResult[InternedName] = 104 JITEvaluatedSymbol(DA->getAddress(), Flags); 105 if (AutoClaim && !MR.getSymbols().count(InternedName)) { 106 assert(!ExtraSymbolsToClaim.count(InternedName) && 107 "Duplicate symbol to claim?"); 108 ExtraSymbolsToClaim[InternedName] = Flags; 109 } 110 } 111 112 for (auto *A : G.absolute_atoms()) 113 if (A->hasName()) { 114 auto InternedName = ES.intern(A->getName()); 115 JITSymbolFlags Flags; 116 Flags |= JITSymbolFlags::Absolute; 117 if (A->isWeak()) 118 Flags |= JITSymbolFlags::Weak; 119 if (A->isCallable()) 120 Flags |= JITSymbolFlags::Callable; 121 InternedResult[InternedName] = 122 JITEvaluatedSymbol(A->getAddress(), Flags); 123 if (AutoClaim && !MR.getSymbols().count(InternedName)) { 124 assert(!ExtraSymbolsToClaim.count(InternedName) && 125 "Duplicate symbol to claim?"); 126 ExtraSymbolsToClaim[InternedName] = Flags; 127 } 128 } 129 130 if (!ExtraSymbolsToClaim.empty()) 131 if (auto Err = MR.defineMaterializing(ExtraSymbolsToClaim)) 132 return notifyFailed(std::move(Err)); 133 134 MR.resolve(InternedResult); 135 136 if (Layer.NotifyLoaded) 137 Layer.NotifyLoaded(MR.getVModuleKey()); 138 } 139 140 void notifyFinalized( 141 std::unique_ptr<JITLinkMemoryManager::Allocation> A) override { 142 143 if (EHFrameAddr) { 144 // If there is an eh-frame then try to register it. 145 if (auto Err = registerEHFrameSection((void *)EHFrameAddr)) { 146 Layer.getExecutionSession().reportError(std::move(Err)); 147 MR.failMaterialization(); 148 return; 149 } 150 } 151 152 MR.emit(); 153 Layer.notifyFinalized( 154 ObjectLinkingLayer::ObjectResources(std::move(A), EHFrameAddr)); 155 } 156 157 AtomGraphPassFunction getMarkLivePass(const Triple &TT) const override { 158 return [this](AtomGraph &G) { return markResponsibilitySymbolsLive(G); }; 159 } 160 161 Error modifyPassConfig(const Triple &TT, PassConfiguration &Config) override { 162 // Add passes to mark duplicate defs as should-discard, and to walk the 163 // atom graph to build the symbol dependence graph. 164 Config.PrePrunePasses.push_back( 165 [this](AtomGraph &G) { return markSymbolsToDiscard(G); }); 166 Config.PostPrunePasses.push_back( 167 [this](AtomGraph &G) { return computeNamedSymbolDependencies(G); }); 168 169 Config.PostFixupPasses.push_back( 170 createEHFrameRecorderPass(TT, EHFrameAddr)); 171 172 if (Layer.ModifyPassConfig) 173 Layer.ModifyPassConfig(TT, Config); 174 175 return Error::success(); 176 } 177 178 private: 179 using AnonAtomNamedDependenciesMap = 180 DenseMap<const DefinedAtom *, SymbolNameSet>; 181 182 Error markSymbolsToDiscard(AtomGraph &G) { 183 auto &ES = Layer.getExecutionSession(); 184 for (auto *DA : G.defined_atoms()) 185 if (DA->isWeak() && DA->hasName()) { 186 auto S = ES.intern(DA->getName()); 187 auto I = MR.getSymbols().find(S); 188 if (I == MR.getSymbols().end()) 189 DA->setShouldDiscard(true); 190 } 191 192 for (auto *A : G.absolute_atoms()) 193 if (A->isWeak() && A->hasName()) { 194 auto S = ES.intern(A->getName()); 195 auto I = MR.getSymbols().find(S); 196 if (I == MR.getSymbols().end()) 197 A->setShouldDiscard(true); 198 } 199 200 return Error::success(); 201 } 202 203 Error markResponsibilitySymbolsLive(AtomGraph &G) const { 204 auto &ES = Layer.getExecutionSession(); 205 for (auto *DA : G.defined_atoms()) 206 if (DA->hasName() && 207 MR.getSymbols().count(ES.intern(DA->getName()))) 208 DA->setLive(true); 209 return Error::success(); 210 } 211 212 Error computeNamedSymbolDependencies(AtomGraph &G) { 213 auto &ES = MR.getTargetJITDylib().getExecutionSession(); 214 auto AnonDeps = computeAnonDeps(G); 215 216 for (auto *DA : G.defined_atoms()) { 217 218 // Skip anonymous and non-global atoms: we do not need dependencies for 219 // these. 220 if (!DA->hasName() || !DA->isGlobal()) 221 continue; 222 223 auto DAName = ES.intern(DA->getName()); 224 SymbolNameSet &DADeps = NamedSymbolDeps[DAName]; 225 226 for (auto &E : DA->edges()) { 227 auto &TA = E.getTarget(); 228 229 if (TA.hasName()) 230 DADeps.insert(ES.intern(TA.getName())); 231 else { 232 assert(TA.isDefined() && "Anonymous atoms must be defined"); 233 auto &DTA = static_cast<DefinedAtom &>(TA); 234 auto I = AnonDeps.find(&DTA); 235 if (I != AnonDeps.end()) 236 for (auto &S : I->second) 237 DADeps.insert(S); 238 } 239 } 240 } 241 242 return Error::success(); 243 } 244 245 AnonAtomNamedDependenciesMap computeAnonDeps(AtomGraph &G) { 246 247 auto &ES = MR.getTargetJITDylib().getExecutionSession(); 248 AnonAtomNamedDependenciesMap DepMap; 249 250 // For all anonymous atoms: 251 // (1) Add their named dependencies. 252 // (2) Add them to the worklist for further iteration if they have any 253 // depend on any other anonymous atoms. 254 struct WorklistEntry { 255 WorklistEntry(DefinedAtom *DA, DenseSet<DefinedAtom *> DAAnonDeps) 256 : DA(DA), DAAnonDeps(std::move(DAAnonDeps)) {} 257 258 DefinedAtom *DA = nullptr; 259 DenseSet<DefinedAtom *> DAAnonDeps; 260 }; 261 std::vector<WorklistEntry> Worklist; 262 for (auto *DA : G.defined_atoms()) 263 if (!DA->hasName()) { 264 auto &DANamedDeps = DepMap[DA]; 265 DenseSet<DefinedAtom *> DAAnonDeps; 266 267 for (auto &E : DA->edges()) { 268 auto &TA = E.getTarget(); 269 if (TA.hasName()) 270 DANamedDeps.insert(ES.intern(TA.getName())); 271 else { 272 assert(TA.isDefined() && "Anonymous atoms must be defined"); 273 DAAnonDeps.insert(static_cast<DefinedAtom *>(&TA)); 274 } 275 } 276 277 if (!DAAnonDeps.empty()) 278 Worklist.push_back(WorklistEntry(DA, std::move(DAAnonDeps))); 279 } 280 281 // Loop over all anonymous atoms with anonymous dependencies, propagating 282 // their respective *named* dependencies. Iterate until we hit a stable 283 // state. 284 bool Changed; 285 do { 286 Changed = false; 287 for (auto &WLEntry : Worklist) { 288 auto *DA = WLEntry.DA; 289 auto &DANamedDeps = DepMap[DA]; 290 auto &DAAnonDeps = WLEntry.DAAnonDeps; 291 292 for (auto *TA : DAAnonDeps) { 293 auto I = DepMap.find(TA); 294 if (I != DepMap.end()) 295 for (const auto &S : I->second) 296 Changed |= DANamedDeps.insert(S).second; 297 } 298 } 299 } while (Changed); 300 301 return DepMap; 302 } 303 304 void registerDependencies(const SymbolDependenceMap &QueryDeps) { 305 for (auto &NamedDepsEntry : NamedSymbolDeps) { 306 auto &Name = NamedDepsEntry.first; 307 auto &NameDeps = NamedDepsEntry.second; 308 SymbolDependenceMap SymbolDeps; 309 310 for (const auto &QueryDepsEntry : QueryDeps) { 311 JITDylib &SourceJD = *QueryDepsEntry.first; 312 const SymbolNameSet &Symbols = QueryDepsEntry.second; 313 auto &DepsForJD = SymbolDeps[&SourceJD]; 314 315 for (const auto &S : Symbols) 316 if (NameDeps.count(S)) 317 DepsForJD.insert(S); 318 319 if (DepsForJD.empty()) 320 SymbolDeps.erase(&SourceJD); 321 } 322 323 MR.addDependencies(Name, SymbolDeps); 324 } 325 } 326 327 ObjectLinkingLayer &Layer; 328 MaterializationResponsibility MR; 329 std::unique_ptr<MemoryBuffer> ObjBuffer; 330 DenseMap<SymbolStringPtr, SymbolNameSet> NamedSymbolDeps; 331 JITTargetAddress EHFrameAddr = 0; 332 }; 333 334 ObjectLinkingLayer::ObjectLinkingLayer( 335 ExecutionSession &ES, JITLinkMemoryManager &MemMgr, 336 NotifyLoadedFunction NotifyLoaded, NotifyEmittedFunction NotifyEmitted, 337 ModifyPassConfigFunction ModifyPassConfig) 338 : ObjectLayer(ES), MemMgr(MemMgr), NotifyLoaded(std::move(NotifyLoaded)), 339 NotifyEmitted(std::move(NotifyEmitted)), 340 ModifyPassConfig(std::move(ModifyPassConfig)) {} 341 342 void ObjectLinkingLayer::emit(MaterializationResponsibility R, 343 std::unique_ptr<MemoryBuffer> O) { 344 assert(O && "Object must not be null"); 345 jitLink(llvm::make_unique<ObjectLinkingLayerJITLinkContext>( 346 *this, std::move(R), std::move(O))); 347 } 348 349 ObjectLinkingLayer::ObjectResources::ObjectResources( 350 AllocPtr Alloc, JITTargetAddress EHFrameAddr) 351 : Alloc(std::move(Alloc)), EHFrameAddr(EHFrameAddr) {} 352 353 ObjectLinkingLayer::ObjectResources::ObjectResources(ObjectResources &&Other) 354 : Alloc(std::move(Other.Alloc)), EHFrameAddr(Other.EHFrameAddr) { 355 Other.EHFrameAddr = 0; 356 } 357 358 ObjectLinkingLayer::ObjectResources & 359 ObjectLinkingLayer::ObjectResources::operator=(ObjectResources &&Other) { 360 std::swap(Alloc, Other.Alloc); 361 std::swap(EHFrameAddr, Other.EHFrameAddr); 362 return *this; 363 } 364 365 ObjectLinkingLayer::ObjectResources::~ObjectResources() { 366 const char *ErrBanner = 367 "ObjectLinkingLayer received error deallocating object resources:"; 368 369 assert((EHFrameAddr == 0 || Alloc) && 370 "Non-null EHFrameAddr must have an associated allocation"); 371 372 if (EHFrameAddr) 373 if (auto Err = deregisterEHFrameSection((void *)EHFrameAddr)) 374 logAllUnhandledErrors(std::move(Err), llvm::errs(), ErrBanner); 375 376 if (Alloc) 377 if (auto Err = Alloc->deallocate()) 378 logAllUnhandledErrors(std::move(Err), llvm::errs(), ErrBanner); 379 } 380 381 } // End namespace orc. 382 } // End namespace llvm. 383