//===- RTDyldObjectLinkingLayer.h - RTDyld-based jit linking ---*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Contains the definition for an RTDyld-based, in-process object linking layer. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H #define LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/Legacy.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Error.h" #include <algorithm> #include <cassert> #include <functional> #include <list> #include <memory> #include <string> #include <utility> #include <vector> namespace llvm { namespace orc { class RTDyldObjectLinkingLayerBase { public: using ObjectPtr = std::unique_ptr<MemoryBuffer>; protected: /// @brief Holds an object to be allocated/linked as a unit in the JIT. /// /// An instance of this class will be created for each object added /// via JITObjectLayer::addObject. Deleting the instance (via /// removeObject) frees its memory, removing all symbol definitions that /// had been provided by this instance. Higher level layers are responsible /// for taking any action required to handle the missing symbols. class LinkedObject { public: LinkedObject() = default; LinkedObject(const LinkedObject&) = delete; void operator=(const LinkedObject&) = delete; virtual ~LinkedObject() = default; virtual Error finalize() = 0; virtual JITSymbol::GetAddressFtor getSymbolMaterializer(std::string Name) = 0; virtual void mapSectionAddress(const void *LocalAddress, JITTargetAddress TargetAddr) const = 0; JITSymbol getSymbol(StringRef Name, bool ExportedSymbolsOnly) { auto SymEntry = SymbolTable.find(Name); if (SymEntry == SymbolTable.end()) return nullptr; if (!SymEntry->second.getFlags().isExported() && ExportedSymbolsOnly) return nullptr; if (!Finalized) return JITSymbol(getSymbolMaterializer(Name), SymEntry->second.getFlags()); return JITSymbol(SymEntry->second); } protected: StringMap<JITEvaluatedSymbol> SymbolTable; bool Finalized = false; }; }; /// @brief Bare bones object linking layer. /// /// This class is intended to be used as the base layer for a JIT. It allows /// object files to be loaded into memory, linked, and the addresses of their /// symbols queried. All objects added to this layer can see each other's /// symbols. class RTDyldObjectLinkingLayer : public RTDyldObjectLinkingLayerBase { public: using RTDyldObjectLinkingLayerBase::ObjectPtr; /// @brief Functor for receiving object-loaded notifications. using NotifyLoadedFtor = std::function<void(VModuleKey, const object::ObjectFile &Obj, const RuntimeDyld::LoadedObjectInfo &)>; /// @brief Functor for receiving finalization notifications. using NotifyFinalizedFtor = std::function<void(VModuleKey)>; private: using OwnedObject = object::OwningBinary<object::ObjectFile>; template <typename MemoryManagerPtrT> class ConcreteLinkedObject : public LinkedObject { public: ConcreteLinkedObject(RTDyldObjectLinkingLayer &Parent, VModuleKey K, OwnedObject Obj, MemoryManagerPtrT MemMgr, std::shared_ptr<SymbolResolver> Resolver, bool ProcessAllSections) : MemMgr(std::move(MemMgr)), PFC(llvm::make_unique<PreFinalizeContents>( Parent, std::move(K), std::move(Obj), std::move(Resolver), ProcessAllSections)) { buildInitialSymbolTable(PFC->Obj); } ~ConcreteLinkedObject() override { MemMgr->deregisterEHFrames(); } Error finalize() override { assert(PFC && "mapSectionAddress called on finalized LinkedObject"); JITSymbolResolverAdapter ResolverAdapter(PFC->Parent.ES, *PFC->Resolver); PFC->RTDyld = llvm::make_unique<RuntimeDyld>(*MemMgr, ResolverAdapter); PFC->RTDyld->setProcessAllSections(PFC->ProcessAllSections); Finalized = true; std::unique_ptr<RuntimeDyld::LoadedObjectInfo> Info = PFC->RTDyld->loadObject(*PFC->Obj.getBinary()); // Copy the symbol table out of the RuntimeDyld instance. { auto SymTab = PFC->RTDyld->getSymbolTable(); for (auto &KV : SymTab) SymbolTable[KV.first] = KV.second; } if (PFC->Parent.NotifyLoaded) PFC->Parent.NotifyLoaded(PFC->K, *PFC->Obj.getBinary(), *Info); PFC->RTDyld->finalizeWithMemoryManagerLocking(); if (PFC->RTDyld->hasError()) return make_error<StringError>(PFC->RTDyld->getErrorString(), inconvertibleErrorCode()); if (PFC->Parent.NotifyFinalized) PFC->Parent.NotifyFinalized(PFC->K); // Release resources. PFC = nullptr; return Error::success(); } JITSymbol::GetAddressFtor getSymbolMaterializer(std::string Name) override { return [this, Name]() -> Expected<JITTargetAddress> { // The symbol may be materialized between the creation of this lambda // and its execution, so we need to double check. if (!this->Finalized) if (auto Err = this->finalize()) return std::move(Err); return this->getSymbol(Name, false).getAddress(); }; } void mapSectionAddress(const void *LocalAddress, JITTargetAddress TargetAddr) const override { assert(PFC && "mapSectionAddress called on finalized LinkedObject"); assert(PFC->RTDyld && "mapSectionAddress called on raw LinkedObject"); PFC->RTDyld->mapSectionAddress(LocalAddress, TargetAddr); } private: void buildInitialSymbolTable(const OwnedObject &Obj) { for (auto &Symbol : Obj.getBinary()->symbols()) { if (Symbol.getFlags() & object::SymbolRef::SF_Undefined) continue; Expected<StringRef> SymbolName = Symbol.getName(); // FIXME: Raise an error for bad symbols. if (!SymbolName) { consumeError(SymbolName.takeError()); continue; } auto Flags = JITSymbolFlags::fromObjectSymbol(Symbol); SymbolTable.insert( std::make_pair(*SymbolName, JITEvaluatedSymbol(0, Flags))); } } // Contains the information needed prior to finalization: the object files, // memory manager, resolver, and flags needed for RuntimeDyld. struct PreFinalizeContents { PreFinalizeContents(RTDyldObjectLinkingLayer &Parent, VModuleKey K, OwnedObject Obj, std::shared_ptr<SymbolResolver> Resolver, bool ProcessAllSections) : Parent(Parent), K(std::move(K)), Obj(std::move(Obj)), Resolver(std::move(Resolver)), ProcessAllSections(ProcessAllSections) {} RTDyldObjectLinkingLayer &Parent; VModuleKey K; OwnedObject Obj; std::shared_ptr<SymbolResolver> Resolver; bool ProcessAllSections; std::unique_ptr<RuntimeDyld> RTDyld; }; MemoryManagerPtrT MemMgr; std::unique_ptr<PreFinalizeContents> PFC; }; template <typename MemoryManagerPtrT> std::unique_ptr<ConcreteLinkedObject<MemoryManagerPtrT>> createLinkedObject(RTDyldObjectLinkingLayer &Parent, VModuleKey K, OwnedObject Obj, MemoryManagerPtrT MemMgr, std::shared_ptr<SymbolResolver> Resolver, bool ProcessAllSections) { using LOS = ConcreteLinkedObject<MemoryManagerPtrT>; return llvm::make_unique<LOS>(Parent, std::move(K), std::move(Obj), std::move(MemMgr), std::move(Resolver), ProcessAllSections); } public: struct Resources { std::shared_ptr<RuntimeDyld::MemoryManager> MemMgr; std::shared_ptr<SymbolResolver> Resolver; }; using ResourcesGetter = std::function<Resources(VModuleKey)>; /// @brief Construct an ObjectLinkingLayer with the given NotifyLoaded, /// and NotifyFinalized functors. RTDyldObjectLinkingLayer( ExecutionSession &ES, ResourcesGetter GetResources, NotifyLoadedFtor NotifyLoaded = NotifyLoadedFtor(), NotifyFinalizedFtor NotifyFinalized = NotifyFinalizedFtor()) : ES(ES), GetResources(std::move(GetResources)), NotifyLoaded(std::move(NotifyLoaded)), NotifyFinalized(std::move(NotifyFinalized)), ProcessAllSections(false) { } /// @brief Set the 'ProcessAllSections' flag. /// /// If set to true, all sections in each object file will be allocated using /// the memory manager, rather than just the sections required for execution. /// /// This is kludgy, and may be removed in the future. void setProcessAllSections(bool ProcessAllSections) { this->ProcessAllSections = ProcessAllSections; } /// @brief Add an object to the JIT. Error addObject(VModuleKey K, ObjectPtr ObjBuffer) { auto Obj = object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()); if (!Obj) return Obj.takeError(); assert(!LinkedObjects.count(K) && "VModuleKey already in use"); auto R = GetResources(K); LinkedObjects[K] = createLinkedObject( *this, K, OwnedObject(std::move(*Obj), std::move(ObjBuffer)), std::move(R.MemMgr), std::move(R.Resolver), ProcessAllSections); return Error::success(); } /// @brief Remove the object associated with VModuleKey K. /// /// All memory allocated for the object will be freed, and the sections and /// symbols it provided will no longer be available. No attempt is made to /// re-emit the missing symbols, and any use of these symbols (directly or /// indirectly) will result in undefined behavior. If dependence tracking is /// required to detect or resolve such issues it should be added at a higher /// layer. Error removeObject(VModuleKey K) { assert(LinkedObjects.count(K) && "VModuleKey not associated with object"); // How do we invalidate the symbols in H? LinkedObjects.erase(K); return Error::success(); } /// @brief Search for the given named symbol. /// @param Name The name of the symbol to search for. /// @param ExportedSymbolsOnly If true, search only for exported symbols. /// @return A handle for the given named symbol, if it exists. JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) { for (auto &KV : LinkedObjects) if (auto Sym = KV.second->getSymbol(Name, ExportedSymbolsOnly)) return Sym; else if (auto Err = Sym.takeError()) return std::move(Err); return nullptr; } /// @brief Search for the given named symbol in the context of the loaded /// object represented by the VModuleKey K. /// @param K The VModuleKey for the object to search in. /// @param Name The name of the symbol to search for. /// @param ExportedSymbolsOnly If true, search only for exported symbols. /// @return A handle for the given named symbol, if it is found in the /// given object. JITSymbol findSymbolIn(VModuleKey K, StringRef Name, bool ExportedSymbolsOnly) { assert(LinkedObjects.count(K) && "VModuleKey not associated with object"); return LinkedObjects[K]->getSymbol(Name, ExportedSymbolsOnly); } /// @brief Map section addresses for the object associated with the /// VModuleKey K. void mapSectionAddress(VModuleKey K, const void *LocalAddress, JITTargetAddress TargetAddr) { assert(LinkedObjects.count(K) && "VModuleKey not associated with object"); LinkedObjects[K]->mapSectionAddress(LocalAddress, TargetAddr); } /// @brief Immediately emit and finalize the object represented by the given /// VModuleKey. /// @param K VModuleKey for object to emit/finalize. Error emitAndFinalize(VModuleKey K) { assert(LinkedObjects.count(K) && "VModuleKey not associated with object"); return LinkedObjects[K]->finalize(); } private: ExecutionSession &ES; std::map<VModuleKey, std::unique_ptr<LinkedObject>> LinkedObjects; ResourcesGetter GetResources; NotifyLoadedFtor NotifyLoaded; NotifyFinalizedFtor NotifyFinalized; bool ProcessAllSections = false; }; } // end namespace orc } // end namespace llvm #endif // LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H