| 1 | //===-- RTDyldObjectLinkingLayer.cpp - RuntimeDyld 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 <memory> |
| 10 | |
| 11 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" |
| 12 | #include "llvm/Object/COFF.h" |
| 13 | |
| 14 | namespace { |
| 15 | |
| 16 | using namespace llvm; |
| 17 | using namespace llvm::orc; |
| 18 | |
| 19 | class JITDylibSearchOrderResolver : public JITSymbolResolver { |
| 20 | public: |
| 21 | JITDylibSearchOrderResolver(MaterializationResponsibility &MR, |
| 22 | SymbolDependenceMap &Deps) |
| 23 | : MR(MR), Deps(Deps) {} |
| 24 | |
| 25 | void lookup(const LookupSet &Symbols, |
| 26 | OnResolvedFunction OnResolved) override { |
| 27 | auto &ES = MR.getTargetJITDylib().getExecutionSession(); |
| 28 | SymbolLookupSet InternedSymbols; |
| 29 | |
| 30 | // Intern the requested symbols: lookup takes interned strings. |
| 31 | for (auto &S : Symbols) |
| 32 | InternedSymbols.add(Name: ES.intern(SymName: S)); |
| 33 | |
| 34 | // Build an OnResolve callback to unwrap the interned strings and pass them |
| 35 | // to the OnResolved callback. |
| 36 | auto OnResolvedWithUnwrap = |
| 37 | [OnResolved = std::move(OnResolved)]( |
| 38 | Expected<SymbolMap> InternedResult) mutable { |
| 39 | if (!InternedResult) { |
| 40 | OnResolved(InternedResult.takeError()); |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | LookupResult Result; |
| 45 | for (auto &KV : *InternedResult) |
| 46 | Result[*KV.first] = {KV.second.getAddress().getValue(), |
| 47 | KV.second.getFlags()}; |
| 48 | OnResolved(Result); |
| 49 | }; |
| 50 | |
| 51 | JITDylibSearchOrder LinkOrder; |
| 52 | MR.getTargetJITDylib().withLinkOrderDo( |
| 53 | F: [&](const JITDylibSearchOrder &LO) { LinkOrder = LO; }); |
| 54 | ES.lookup( |
| 55 | K: LookupKind::Static, SearchOrder: LinkOrder, Symbols: InternedSymbols, RequiredState: SymbolState::Resolved, |
| 56 | NotifyComplete: std::move(OnResolvedWithUnwrap), |
| 57 | RegisterDependencies: [this](const SymbolDependenceMap &LookupDeps) { Deps = LookupDeps; }); |
| 58 | } |
| 59 | |
| 60 | Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) override { |
| 61 | LookupSet Result; |
| 62 | |
| 63 | for (auto &KV : MR.getSymbols()) { |
| 64 | if (Symbols.count(x: *KV.first)) |
| 65 | Result.insert(x: *KV.first); |
| 66 | } |
| 67 | |
| 68 | return Result; |
| 69 | } |
| 70 | |
| 71 | private: |
| 72 | MaterializationResponsibility &MR; |
| 73 | SymbolDependenceMap &Deps; |
| 74 | }; |
| 75 | |
| 76 | } // end anonymous namespace |
| 77 | |
| 78 | namespace llvm { |
| 79 | namespace orc { |
| 80 | |
| 81 | char RTDyldObjectLinkingLayer::ID; |
| 82 | |
| 83 | using BaseT = RTTIExtends<RTDyldObjectLinkingLayer, ObjectLayer>; |
| 84 | |
| 85 | RTDyldObjectLinkingLayer::RTDyldObjectLinkingLayer( |
| 86 | ExecutionSession &ES, GetMemoryManagerFunction GetMemoryManager) |
| 87 | : BaseT(ES), GetMemoryManager(std::move(GetMemoryManager)) { |
| 88 | ES.registerResourceManager(RM&: *this); |
| 89 | } |
| 90 | |
| 91 | RTDyldObjectLinkingLayer::~RTDyldObjectLinkingLayer() { |
| 92 | assert(MemMgrs.empty() && |
| 93 | "Layer destroyed with resources still attached" |
| 94 | "(ExecutionSession::endSession() must be called prior to " |
| 95 | "destruction)" ); |
| 96 | } |
| 97 | |
| 98 | void RTDyldObjectLinkingLayer::emit( |
| 99 | std::unique_ptr<MaterializationResponsibility> R, |
| 100 | std::unique_ptr<MemoryBuffer> O) { |
| 101 | assert(O && "Object must not be null" ); |
| 102 | |
| 103 | auto &ES = getExecutionSession(); |
| 104 | |
| 105 | auto Obj = object::ObjectFile::createObjectFile(Object: *O); |
| 106 | |
| 107 | if (!Obj) { |
| 108 | getExecutionSession().reportError(Err: Obj.takeError()); |
| 109 | R->failMaterialization(); |
| 110 | return; |
| 111 | } |
| 112 | |
| 113 | // Collect the internal symbols from the object file: We will need to |
| 114 | // filter these later. |
| 115 | auto InternalSymbols = std::make_shared<std::set<StringRef>>(); |
| 116 | { |
| 117 | SymbolFlagsMap ; |
| 118 | for (auto &Sym : (*Obj)->symbols()) { |
| 119 | |
| 120 | // Skip file symbols. |
| 121 | if (auto SymType = Sym.getType()) { |
| 122 | if (*SymType == object::SymbolRef::ST_File) |
| 123 | continue; |
| 124 | } else { |
| 125 | ES.reportError(Err: SymType.takeError()); |
| 126 | R->failMaterialization(); |
| 127 | return; |
| 128 | } |
| 129 | |
| 130 | Expected<uint32_t> SymFlagsOrErr = Sym.getFlags(); |
| 131 | if (!SymFlagsOrErr) { |
| 132 | // TODO: Test this error. |
| 133 | ES.reportError(Err: SymFlagsOrErr.takeError()); |
| 134 | R->failMaterialization(); |
| 135 | return; |
| 136 | } |
| 137 | |
| 138 | // Try to claim responsibility of weak symbols |
| 139 | // if AutoClaimObjectSymbols flag is set. |
| 140 | if (AutoClaimObjectSymbols && |
| 141 | (*SymFlagsOrErr & object::BasicSymbolRef::SF_Weak)) { |
| 142 | auto SymName = Sym.getName(); |
| 143 | if (!SymName) { |
| 144 | ES.reportError(Err: SymName.takeError()); |
| 145 | R->failMaterialization(); |
| 146 | return; |
| 147 | } |
| 148 | |
| 149 | // Already included in responsibility set, skip it |
| 150 | SymbolStringPtr SymbolName = ES.intern(SymName: *SymName); |
| 151 | if (R->getSymbols().count(Val: SymbolName)) |
| 152 | continue; |
| 153 | |
| 154 | auto SymFlags = JITSymbolFlags::fromObjectSymbol(Symbol: Sym); |
| 155 | if (!SymFlags) { |
| 156 | ES.reportError(Err: SymFlags.takeError()); |
| 157 | R->failMaterialization(); |
| 158 | return; |
| 159 | } |
| 160 | |
| 161 | ExtraSymbolsToClaim[SymbolName] = *SymFlags; |
| 162 | continue; |
| 163 | } |
| 164 | |
| 165 | // Don't include symbols that aren't global. |
| 166 | if (!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global)) { |
| 167 | if (auto SymName = Sym.getName()) |
| 168 | InternalSymbols->insert(x: *SymName); |
| 169 | else { |
| 170 | ES.reportError(Err: SymName.takeError()); |
| 171 | R->failMaterialization(); |
| 172 | return; |
| 173 | } |
| 174 | } |
| 175 | } |
| 176 | |
| 177 | if (!ExtraSymbolsToClaim.empty()) { |
| 178 | if (auto Err = R->defineMaterializing(SymbolFlags: ExtraSymbolsToClaim)) { |
| 179 | ES.reportError(Err: std::move(Err)); |
| 180 | R->failMaterialization(); |
| 181 | } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | auto MemMgr = GetMemoryManager(*O); |
| 186 | auto &MemMgrRef = *MemMgr; |
| 187 | |
| 188 | // Switch to shared ownership of MR so that it can be captured by both |
| 189 | // lambdas below. |
| 190 | std::shared_ptr<MaterializationResponsibility> SharedR(std::move(R)); |
| 191 | auto Deps = std::make_unique<SymbolDependenceMap>(); |
| 192 | |
| 193 | auto Resolver = |
| 194 | std::make_unique<JITDylibSearchOrderResolver>(args&: *SharedR, args&: *Deps); |
| 195 | auto *ResolverPtr = Resolver.get(); |
| 196 | |
| 197 | jitLinkForORC( |
| 198 | O: object::OwningBinary<object::ObjectFile>(std::move(*Obj), std::move(O)), |
| 199 | MemMgr&: MemMgrRef, Resolver&: *ResolverPtr, ProcessAllSections, |
| 200 | OnLoaded: [this, SharedR, &MemMgrRef, InternalSymbols]( |
| 201 | const object::ObjectFile &Obj, |
| 202 | RuntimeDyld::LoadedObjectInfo &LoadedObjInfo, |
| 203 | std::map<StringRef, JITEvaluatedSymbol> ResolvedSymbols) { |
| 204 | return onObjLoad(R&: *SharedR, Obj, MemMgr&: MemMgrRef, LoadedObjInfo, |
| 205 | Resolved: ResolvedSymbols, InternalSymbols&: *InternalSymbols); |
| 206 | }, |
| 207 | OnEmitted: [this, SharedR, MemMgr = std::move(MemMgr), Deps = std::move(Deps), |
| 208 | Resolver = std::move(Resolver)]( |
| 209 | object::OwningBinary<object::ObjectFile> Obj, |
| 210 | std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo, |
| 211 | Error Err) mutable { |
| 212 | onObjEmit(R&: *SharedR, O: std::move(Obj), MemMgr: std::move(MemMgr), |
| 213 | LoadedObjInfo: std::move(LoadedObjInfo), Deps: std::move(Deps), Err: std::move(Err)); |
| 214 | }); |
| 215 | } |
| 216 | |
| 217 | void RTDyldObjectLinkingLayer::registerJITEventListener(JITEventListener &L) { |
| 218 | std::lock_guard<std::mutex> Lock(RTDyldLayerMutex); |
| 219 | assert(!llvm::is_contained(EventListeners, &L) && |
| 220 | "Listener has already been registered" ); |
| 221 | EventListeners.push_back(x: &L); |
| 222 | } |
| 223 | |
| 224 | void RTDyldObjectLinkingLayer::unregisterJITEventListener(JITEventListener &L) { |
| 225 | std::lock_guard<std::mutex> Lock(RTDyldLayerMutex); |
| 226 | auto I = llvm::find(Range&: EventListeners, Val: &L); |
| 227 | assert(I != EventListeners.end() && "Listener not registered" ); |
| 228 | EventListeners.erase(position: I); |
| 229 | } |
| 230 | |
| 231 | Error RTDyldObjectLinkingLayer::onObjLoad( |
| 232 | MaterializationResponsibility &R, const object::ObjectFile &Obj, |
| 233 | RuntimeDyld::MemoryManager &MemMgr, |
| 234 | RuntimeDyld::LoadedObjectInfo &LoadedObjInfo, |
| 235 | std::map<StringRef, JITEvaluatedSymbol> Resolved, |
| 236 | std::set<StringRef> &InternalSymbols) { |
| 237 | SymbolFlagsMap ; |
| 238 | SymbolMap Symbols; |
| 239 | |
| 240 | // Hack to support COFF constant pool comdats introduced during compilation: |
| 241 | // (See http://llvm.org/PR40074) |
| 242 | if (auto *COFFObj = dyn_cast<object::COFFObjectFile>(Val: &Obj)) { |
| 243 | auto &ES = getExecutionSession(); |
| 244 | |
| 245 | // For all resolved symbols that are not already in the responsibility set: |
| 246 | // check whether the symbol is in a comdat section and if so mark it as |
| 247 | // weak. |
| 248 | for (auto &Sym : COFFObj->symbols()) { |
| 249 | // getFlags() on COFF symbols can't fail. |
| 250 | uint32_t SymFlags = cantFail(ValOrErr: Sym.getFlags()); |
| 251 | if (SymFlags & object::BasicSymbolRef::SF_Undefined) |
| 252 | continue; |
| 253 | auto Name = Sym.getName(); |
| 254 | if (!Name) |
| 255 | return Name.takeError(); |
| 256 | auto I = Resolved.find(x: *Name); |
| 257 | |
| 258 | // Skip unresolved symbols, internal symbols, and symbols that are |
| 259 | // already in the responsibility set. |
| 260 | if (I == Resolved.end() || InternalSymbols.count(x: *Name) || |
| 261 | R.getSymbols().count(Val: ES.intern(SymName: *Name))) |
| 262 | continue; |
| 263 | auto Sec = Sym.getSection(); |
| 264 | if (!Sec) |
| 265 | return Sec.takeError(); |
| 266 | if (*Sec == COFFObj->section_end()) |
| 267 | continue; |
| 268 | auto &COFFSec = *COFFObj->getCOFFSection(Section: **Sec); |
| 269 | if (COFFSec.Characteristics & COFF::IMAGE_SCN_LNK_COMDAT) |
| 270 | I->second.setFlags(I->second.getFlags() | JITSymbolFlags::Weak); |
| 271 | } |
| 272 | |
| 273 | // Handle any aliases. |
| 274 | for (auto &Sym : COFFObj->symbols()) { |
| 275 | uint32_t SymFlags = cantFail(ValOrErr: Sym.getFlags()); |
| 276 | if (SymFlags & object::BasicSymbolRef::SF_Undefined) |
| 277 | continue; |
| 278 | auto Name = Sym.getName(); |
| 279 | if (!Name) |
| 280 | return Name.takeError(); |
| 281 | auto I = Resolved.find(x: *Name); |
| 282 | |
| 283 | // Skip already-resolved symbols, and symbols that we're not responsible |
| 284 | // for. |
| 285 | if (I != Resolved.end() || !R.getSymbols().count(Val: ES.intern(SymName: *Name))) |
| 286 | continue; |
| 287 | |
| 288 | // Skip anything other than weak externals. |
| 289 | auto COFFSym = COFFObj->getCOFFSymbol(Symbol: Sym); |
| 290 | if (!COFFSym.isWeakExternal()) |
| 291 | continue; |
| 292 | auto *WeakExternal = COFFSym.getAux<object::coff_aux_weak_external>(); |
| 293 | if (WeakExternal->Characteristics != COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS) |
| 294 | continue; |
| 295 | |
| 296 | // We found an alias. Reuse the resolution of the alias target for the |
| 297 | // alias itself. |
| 298 | Expected<object::COFFSymbolRef> TargetSymbol = |
| 299 | COFFObj->getSymbol(index: WeakExternal->TagIndex); |
| 300 | if (!TargetSymbol) |
| 301 | return TargetSymbol.takeError(); |
| 302 | Expected<StringRef> TargetName = COFFObj->getSymbolName(Symbol: *TargetSymbol); |
| 303 | if (!TargetName) |
| 304 | return TargetName.takeError(); |
| 305 | auto J = Resolved.find(x: *TargetName); |
| 306 | if (J == Resolved.end()) |
| 307 | return make_error<StringError>(Args: "Could alias target " + *TargetName + |
| 308 | " not resolved" , |
| 309 | Args: inconvertibleErrorCode()); |
| 310 | Resolved[*Name] = J->second; |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | for (auto &KV : Resolved) { |
| 315 | // Scan the symbols and add them to the Symbols map for resolution. |
| 316 | |
| 317 | // We never claim internal symbols. |
| 318 | if (InternalSymbols.count(x: KV.first)) |
| 319 | continue; |
| 320 | |
| 321 | auto InternedName = getExecutionSession().intern(SymName: KV.first); |
| 322 | auto Flags = KV.second.getFlags(); |
| 323 | auto I = R.getSymbols().find(Val: InternedName); |
| 324 | if (I != R.getSymbols().end()) { |
| 325 | // Override object flags and claim responsibility for symbols if |
| 326 | // requested. |
| 327 | if (OverrideObjectFlags) |
| 328 | Flags = I->second; |
| 329 | else { |
| 330 | // RuntimeDyld/MCJIT's weak tracking isn't compatible with ORC's. Even |
| 331 | // if we're not overriding flags in general we should set the weak flag |
| 332 | // according to the MaterializationResponsibility object symbol table. |
| 333 | if (I->second.isWeak()) |
| 334 | Flags |= JITSymbolFlags::Weak; |
| 335 | } |
| 336 | } else if (AutoClaimObjectSymbols) |
| 337 | ExtraSymbolsToClaim[InternedName] = Flags; |
| 338 | |
| 339 | Symbols[InternedName] = {ExecutorAddr(KV.second.getAddress()), Flags}; |
| 340 | } |
| 341 | |
| 342 | if (!ExtraSymbolsToClaim.empty()) { |
| 343 | if (auto Err = R.defineMaterializing(SymbolFlags: ExtraSymbolsToClaim)) |
| 344 | return Err; |
| 345 | |
| 346 | // If we claimed responsibility for any weak symbols but were rejected then |
| 347 | // we need to remove them from the resolved set. |
| 348 | for (auto &KV : ExtraSymbolsToClaim) |
| 349 | if (KV.second.isWeak() && !R.getSymbols().count(Val: KV.first)) |
| 350 | Symbols.erase(Val: KV.first); |
| 351 | } |
| 352 | |
| 353 | if (auto Err = R.notifyResolved(Symbols)) { |
| 354 | R.failMaterialization(); |
| 355 | return Err; |
| 356 | } |
| 357 | |
| 358 | if (NotifyLoaded) |
| 359 | NotifyLoaded(R, Obj, LoadedObjInfo); |
| 360 | |
| 361 | return Error::success(); |
| 362 | } |
| 363 | |
| 364 | void RTDyldObjectLinkingLayer::onObjEmit( |
| 365 | MaterializationResponsibility &R, |
| 366 | object::OwningBinary<object::ObjectFile> O, |
| 367 | std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr, |
| 368 | std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo, |
| 369 | std::unique_ptr<SymbolDependenceMap> Deps, Error Err) { |
| 370 | if (Err) { |
| 371 | getExecutionSession().reportError(Err: std::move(Err)); |
| 372 | R.failMaterialization(); |
| 373 | return; |
| 374 | } |
| 375 | |
| 376 | SymbolDependenceGroup SDG; |
| 377 | for (auto &[Sym, Flags] : R.getSymbols()) |
| 378 | SDG.Symbols.insert(V: Sym); |
| 379 | SDG.Dependencies = std::move(*Deps); |
| 380 | |
| 381 | if (auto Err = R.notifyEmitted(EmittedDeps: SDG)) { |
| 382 | getExecutionSession().reportError(Err: std::move(Err)); |
| 383 | R.failMaterialization(); |
| 384 | return; |
| 385 | } |
| 386 | |
| 387 | std::unique_ptr<object::ObjectFile> Obj; |
| 388 | std::unique_ptr<MemoryBuffer> ObjBuffer; |
| 389 | std::tie(args&: Obj, args&: ObjBuffer) = O.takeBinary(); |
| 390 | |
| 391 | // Run EventListener notifyLoaded callbacks. |
| 392 | { |
| 393 | std::lock_guard<std::mutex> Lock(RTDyldLayerMutex); |
| 394 | for (auto *L : EventListeners) |
| 395 | L->notifyObjectLoaded(K: pointerToJITTargetAddress(Ptr: MemMgr.get()), Obj: *Obj, |
| 396 | L: *LoadedObjInfo); |
| 397 | } |
| 398 | |
| 399 | if (NotifyEmitted) |
| 400 | NotifyEmitted(R, std::move(ObjBuffer)); |
| 401 | |
| 402 | if (auto Err = R.withResourceKeyDo( |
| 403 | F: [&](ResourceKey K) { MemMgrs[K].push_back(x: std::move(MemMgr)); })) { |
| 404 | getExecutionSession().reportError(Err: std::move(Err)); |
| 405 | R.failMaterialization(); |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | Error RTDyldObjectLinkingLayer::handleRemoveResources(JITDylib &JD, |
| 410 | ResourceKey K) { |
| 411 | |
| 412 | std::vector<MemoryManagerUP> MemMgrsToRemove; |
| 413 | |
| 414 | getExecutionSession().runSessionLocked(F: [&] { |
| 415 | auto I = MemMgrs.find(Val: K); |
| 416 | if (I != MemMgrs.end()) { |
| 417 | std::swap(x&: MemMgrsToRemove, y&: I->second); |
| 418 | MemMgrs.erase(I); |
| 419 | } |
| 420 | }); |
| 421 | |
| 422 | { |
| 423 | std::lock_guard<std::mutex> Lock(RTDyldLayerMutex); |
| 424 | for (auto &MemMgr : MemMgrsToRemove) { |
| 425 | for (auto *L : EventListeners) |
| 426 | L->notifyFreeingObject(K: pointerToJITTargetAddress(Ptr: MemMgr.get())); |
| 427 | MemMgr->deregisterEHFrames(); |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | return Error::success(); |
| 432 | } |
| 433 | |
| 434 | void RTDyldObjectLinkingLayer::handleTransferResources(JITDylib &JD, |
| 435 | ResourceKey DstKey, |
| 436 | ResourceKey SrcKey) { |
| 437 | if (MemMgrs.contains(Val: SrcKey)) { |
| 438 | // DstKey may not be in the DenseMap yet, so the following line may resize |
| 439 | // the container and invalidate iterators and value references. |
| 440 | auto &DstMemMgrs = MemMgrs[DstKey]; |
| 441 | auto &SrcMemMgrs = MemMgrs[SrcKey]; |
| 442 | DstMemMgrs.reserve(n: DstMemMgrs.size() + SrcMemMgrs.size()); |
| 443 | for (auto &MemMgr : SrcMemMgrs) |
| 444 | DstMemMgrs.push_back(x: std::move(MemMgr)); |
| 445 | |
| 446 | MemMgrs.erase(Val: SrcKey); |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | } // End namespace orc. |
| 451 | } // End namespace llvm. |
| 452 | |