//===------- ObjectLinkingLayer.cpp - JITLink backed ORC ObjectLayer ------===// // // 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 // //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" #include "llvm/ADT/Optional.h" #include "llvm/ExecutionEngine/JITLink/JITLink_EHFrameSupport.h" #include #define DEBUG_TYPE "orc" using namespace llvm; using namespace llvm::jitlink; using namespace llvm::orc; namespace llvm { namespace orc { class ObjectLinkingLayerJITLinkContext final : public JITLinkContext { public: ObjectLinkingLayerJITLinkContext(ObjectLinkingLayer &Layer, MaterializationResponsibility MR, std::unique_ptr ObjBuffer) : Layer(Layer), MR(std::move(MR)), ObjBuffer(std::move(ObjBuffer)) {} JITLinkMemoryManager &getMemoryManager() override { return Layer.MemMgr; } MemoryBufferRef getObjectBuffer() const override { return ObjBuffer->getMemBufferRef(); } void notifyFailed(Error Err) override { Layer.getExecutionSession().reportError(std::move(Err)); MR.failMaterialization(); } void lookup(const DenseSet &Symbols, JITLinkAsyncLookupContinuation LookupContinuation) override { JITDylibSearchList SearchOrder; MR.getTargetJITDylib().withSearchOrderDo( [&](const JITDylibSearchList &JDs) { SearchOrder = JDs; }); auto &ES = Layer.getExecutionSession(); SymbolNameSet InternedSymbols; for (auto &S : Symbols) InternedSymbols.insert(ES.intern(S)); // OnResolve -- De-intern the symbols and pass the result to the linker. // FIXME: Capture LookupContinuation by move once we have c++14. auto SharedLookupContinuation = std::make_shared( std::move(LookupContinuation)); auto OnResolve = [SharedLookupContinuation](Expected Result) { if (!Result) (*SharedLookupContinuation)(Result.takeError()); else { AsyncLookupResult LR; for (auto &KV : *Result) LR[*KV.first] = KV.second; (*SharedLookupContinuation)(std::move(LR)); } }; ES.lookup( SearchOrder, std::move(InternedSymbols), std::move(OnResolve), // OnReady: [&ES](Error Err) { ES.reportError(std::move(Err)); }, // RegisterDependencies: [this](const SymbolDependenceMap &Deps) { registerDependencies(Deps); }); } void notifyResolved(AtomGraph &G) override { auto &ES = Layer.getExecutionSession(); SymbolFlagsMap ExtraSymbolsToClaim; bool AutoClaim = Layer.AutoClaimObjectSymbols; SymbolMap InternedResult; for (auto *DA : G.defined_atoms()) if (DA->hasName() && DA->isGlobal()) { auto InternedName = ES.intern(DA->getName()); JITSymbolFlags Flags; if (DA->isExported()) Flags |= JITSymbolFlags::Exported; if (DA->isWeak()) Flags |= JITSymbolFlags::Weak; if (DA->isCallable()) Flags |= JITSymbolFlags::Callable; if (DA->isCommon()) Flags |= JITSymbolFlags::Common; InternedResult[InternedName] = JITEvaluatedSymbol(DA->getAddress(), Flags); if (AutoClaim && !MR.getSymbols().count(InternedName)) { assert(!ExtraSymbolsToClaim.count(InternedName) && "Duplicate symbol to claim?"); ExtraSymbolsToClaim[InternedName] = Flags; } } for (auto *A : G.absolute_atoms()) if (A->hasName()) { auto InternedName = ES.intern(A->getName()); JITSymbolFlags Flags; Flags |= JITSymbolFlags::Absolute; if (A->isWeak()) Flags |= JITSymbolFlags::Weak; if (A->isCallable()) Flags |= JITSymbolFlags::Callable; InternedResult[InternedName] = JITEvaluatedSymbol(A->getAddress(), Flags); if (AutoClaim && !MR.getSymbols().count(InternedName)) { assert(!ExtraSymbolsToClaim.count(InternedName) && "Duplicate symbol to claim?"); ExtraSymbolsToClaim[InternedName] = Flags; } } if (!ExtraSymbolsToClaim.empty()) if (auto Err = MR.defineMaterializing(ExtraSymbolsToClaim)) return notifyFailed(std::move(Err)); MR.resolve(InternedResult); if (Layer.NotifyLoaded) Layer.NotifyLoaded(MR.getVModuleKey()); } void notifyFinalized( std::unique_ptr A) override { if (EHFrameAddr) { // If there is an eh-frame then try to register it. if (auto Err = registerEHFrameSection((void *)EHFrameAddr)) { Layer.getExecutionSession().reportError(std::move(Err)); MR.failMaterialization(); return; } } MR.emit(); Layer.notifyFinalized( ObjectLinkingLayer::ObjectResources(std::move(A), EHFrameAddr)); } AtomGraphPassFunction getMarkLivePass(const Triple &TT) const override { return [this](AtomGraph &G) { return markResponsibilitySymbolsLive(G); }; } Error modifyPassConfig(const Triple &TT, PassConfiguration &Config) override { // Add passes to mark duplicate defs as should-discard, and to walk the // atom graph to build the symbol dependence graph. Config.PrePrunePasses.push_back( [this](AtomGraph &G) { return markSymbolsToDiscard(G); }); Config.PostPrunePasses.push_back( [this](AtomGraph &G) { return computeNamedSymbolDependencies(G); }); Config.PostFixupPasses.push_back( createEHFrameRecorderPass(TT, EHFrameAddr)); if (Layer.ModifyPassConfig) Layer.ModifyPassConfig(TT, Config); return Error::success(); } private: using AnonAtomNamedDependenciesMap = DenseMap; Error markSymbolsToDiscard(AtomGraph &G) { auto &ES = Layer.getExecutionSession(); for (auto *DA : G.defined_atoms()) if (DA->isWeak() && DA->hasName()) { auto S = ES.intern(DA->getName()); auto I = MR.getSymbols().find(S); if (I == MR.getSymbols().end()) DA->setShouldDiscard(true); } for (auto *A : G.absolute_atoms()) if (A->isWeak() && A->hasName()) { auto S = ES.intern(A->getName()); auto I = MR.getSymbols().find(S); if (I == MR.getSymbols().end()) A->setShouldDiscard(true); } return Error::success(); } Error markResponsibilitySymbolsLive(AtomGraph &G) const { auto &ES = Layer.getExecutionSession(); for (auto *DA : G.defined_atoms()) if (DA->hasName() && MR.getSymbols().count(ES.intern(DA->getName()))) DA->setLive(true); return Error::success(); } Error computeNamedSymbolDependencies(AtomGraph &G) { auto &ES = MR.getTargetJITDylib().getExecutionSession(); auto AnonDeps = computeAnonDeps(G); for (auto *DA : G.defined_atoms()) { // Skip anonymous and non-global atoms: we do not need dependencies for // these. if (!DA->hasName() || !DA->isGlobal()) continue; auto DAName = ES.intern(DA->getName()); SymbolNameSet &DADeps = NamedSymbolDeps[DAName]; for (auto &E : DA->edges()) { auto &TA = E.getTarget(); if (TA.hasName()) DADeps.insert(ES.intern(TA.getName())); else { assert(TA.isDefined() && "Anonymous atoms must be defined"); auto &DTA = static_cast(TA); auto I = AnonDeps.find(&DTA); if (I != AnonDeps.end()) for (auto &S : I->second) DADeps.insert(S); } } } return Error::success(); } AnonAtomNamedDependenciesMap computeAnonDeps(AtomGraph &G) { auto &ES = MR.getTargetJITDylib().getExecutionSession(); AnonAtomNamedDependenciesMap DepMap; // For all anonymous atoms: // (1) Add their named dependencies. // (2) Add them to the worklist for further iteration if they have any // depend on any other anonymous atoms. struct WorklistEntry { WorklistEntry(DefinedAtom *DA, DenseSet DAAnonDeps) : DA(DA), DAAnonDeps(std::move(DAAnonDeps)) {} DefinedAtom *DA = nullptr; DenseSet DAAnonDeps; }; std::vector Worklist; for (auto *DA : G.defined_atoms()) if (!DA->hasName()) { auto &DANamedDeps = DepMap[DA]; DenseSet DAAnonDeps; for (auto &E : DA->edges()) { auto &TA = E.getTarget(); if (TA.hasName()) DANamedDeps.insert(ES.intern(TA.getName())); else { assert(TA.isDefined() && "Anonymous atoms must be defined"); DAAnonDeps.insert(static_cast(&TA)); } } if (!DAAnonDeps.empty()) Worklist.push_back(WorklistEntry(DA, std::move(DAAnonDeps))); } // Loop over all anonymous atoms with anonymous dependencies, propagating // their respective *named* dependencies. Iterate until we hit a stable // state. bool Changed; do { Changed = false; for (auto &WLEntry : Worklist) { auto *DA = WLEntry.DA; auto &DANamedDeps = DepMap[DA]; auto &DAAnonDeps = WLEntry.DAAnonDeps; for (auto *TA : DAAnonDeps) { auto I = DepMap.find(TA); if (I != DepMap.end()) for (const auto &S : I->second) Changed |= DANamedDeps.insert(S).second; } } } while (Changed); return DepMap; } void registerDependencies(const SymbolDependenceMap &QueryDeps) { for (auto &NamedDepsEntry : NamedSymbolDeps) { auto &Name = NamedDepsEntry.first; auto &NameDeps = NamedDepsEntry.second; SymbolDependenceMap SymbolDeps; for (const auto &QueryDepsEntry : QueryDeps) { JITDylib &SourceJD = *QueryDepsEntry.first; const SymbolNameSet &Symbols = QueryDepsEntry.second; auto &DepsForJD = SymbolDeps[&SourceJD]; for (const auto &S : Symbols) if (NameDeps.count(S)) DepsForJD.insert(S); if (DepsForJD.empty()) SymbolDeps.erase(&SourceJD); } MR.addDependencies(Name, SymbolDeps); } } ObjectLinkingLayer &Layer; MaterializationResponsibility MR; std::unique_ptr ObjBuffer; DenseMap NamedSymbolDeps; JITTargetAddress EHFrameAddr = 0; }; ObjectLinkingLayer::ObjectLinkingLayer( ExecutionSession &ES, JITLinkMemoryManager &MemMgr, NotifyLoadedFunction NotifyLoaded, NotifyEmittedFunction NotifyEmitted, ModifyPassConfigFunction ModifyPassConfig) : ObjectLayer(ES), MemMgr(MemMgr), NotifyLoaded(std::move(NotifyLoaded)), NotifyEmitted(std::move(NotifyEmitted)), ModifyPassConfig(std::move(ModifyPassConfig)) {} void ObjectLinkingLayer::emit(MaterializationResponsibility R, std::unique_ptr O) { assert(O && "Object must not be null"); jitLink(llvm::make_unique( *this, std::move(R), std::move(O))); } ObjectLinkingLayer::ObjectResources::ObjectResources( AllocPtr Alloc, JITTargetAddress EHFrameAddr) : Alloc(std::move(Alloc)), EHFrameAddr(EHFrameAddr) {} ObjectLinkingLayer::ObjectResources::ObjectResources(ObjectResources &&Other) : Alloc(std::move(Other.Alloc)), EHFrameAddr(Other.EHFrameAddr) { Other.EHFrameAddr = 0; } ObjectLinkingLayer::ObjectResources & ObjectLinkingLayer::ObjectResources::operator=(ObjectResources &&Other) { std::swap(Alloc, Other.Alloc); std::swap(EHFrameAddr, Other.EHFrameAddr); return *this; } ObjectLinkingLayer::ObjectResources::~ObjectResources() { const char *ErrBanner = "ObjectLinkingLayer received error deallocating object resources:"; assert((EHFrameAddr == 0 || Alloc) && "Non-null EHFrameAddr must have an associated allocation"); if (EHFrameAddr) if (auto Err = deregisterEHFrameSection((void *)EHFrameAddr)) logAllUnhandledErrors(std::move(Err), llvm::errs(), ErrBanner); if (Alloc) if (auto Err = Alloc->deallocate()) logAllUnhandledErrors(std::move(Err), llvm::errs(), ErrBanner); } } // End namespace orc. } // End namespace llvm.