| //===-- 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/ExecutionEngine.h" |
| #include "llvm/ExecutionEngine/JITSymbol.h" |
| #include "llvm/ExecutionEngine/RuntimeDyld.h" |
| #include "llvm/ExecutionEngine/SectionMemoryManager.h" |
| #include "llvm/Object/ObjectFile.h" |
| #include "llvm/Support/Error.h" |
| #include <cassert> |
| #include <algorithm> |
| #include <functional> |
| #include <list> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| namespace llvm { |
| namespace orc { |
| |
| class RTDyldObjectLinkingLayerBase { |
| protected: |
| /// @brief Holds a set of objects to be allocated/linked as a unit in the JIT. |
| /// |
| /// An instance of this class will be created for each set of objects added |
| /// via JITObjectLayer::addObjectSet. Deleting the instance (via |
| /// removeObjectSet) 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 LinkedObjectSet { |
| public: |
| LinkedObjectSet() = default; |
| LinkedObjectSet(const LinkedObjectSet&) = delete; |
| void operator=(const LinkedObjectSet&) = delete; |
| virtual ~LinkedObjectSet() = default; |
| |
| virtual void 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; |
| }; |
| |
| typedef std::list<std::unique_ptr<LinkedObjectSet>> LinkedObjectSetListT; |
| |
| public: |
| /// @brief Handle to a set of loaded objects. |
| typedef LinkedObjectSetListT::iterator ObjSetHandleT; |
| }; |
| |
| /// @brief Default (no-op) action to perform when loading objects. |
| class DoNothingOnNotifyLoaded { |
| public: |
| template <typename ObjSetT, typename LoadResult> |
| void operator()(RTDyldObjectLinkingLayerBase::ObjSetHandleT, const ObjSetT &, |
| const LoadResult &) {} |
| }; |
| |
| /// @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. |
| template <typename NotifyLoadedFtor = DoNothingOnNotifyLoaded> |
| class RTDyldObjectLinkingLayer : public RTDyldObjectLinkingLayerBase { |
| public: |
| /// @brief Functor for receiving finalization notifications. |
| typedef std::function<void(ObjSetHandleT)> NotifyFinalizedFtor; |
| |
| private: |
| template <typename ObjSetT, typename MemoryManagerPtrT, |
| typename SymbolResolverPtrT, typename FinalizerFtor> |
| class ConcreteLinkedObjectSet : public LinkedObjectSet { |
| public: |
| ConcreteLinkedObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr, |
| SymbolResolverPtrT Resolver, |
| FinalizerFtor Finalizer, |
| bool ProcessAllSections) |
| : MemMgr(std::move(MemMgr)), |
| PFC(llvm::make_unique<PreFinalizeContents>(std::move(Objects), |
| std::move(Resolver), |
| std::move(Finalizer), |
| ProcessAllSections)) { |
| buildInitialSymbolTable(PFC->Objects); |
| } |
| |
| void setHandle(ObjSetHandleT H) { |
| PFC->Handle = H; |
| } |
| |
| void finalize() override { |
| assert(PFC && "mapSectionAddress called on finalized LinkedObjectSet"); |
| |
| RuntimeDyld RTDyld(*MemMgr, *PFC->Resolver); |
| RTDyld.setProcessAllSections(PFC->ProcessAllSections); |
| PFC->RTDyld = &RTDyld; |
| |
| this->Finalized = true; |
| PFC->Finalizer(PFC->Handle, RTDyld, std::move(PFC->Objects), |
| [&]() { |
| this->updateSymbolTable(RTDyld); |
| }); |
| |
| // Release resources. |
| PFC = nullptr; |
| } |
| |
| JITSymbol::GetAddressFtor getSymbolMaterializer(std::string Name) override { |
| return |
| [this, Name]() { |
| // The symbol may be materialized between the creation of this lambda |
| // and its execution, so we need to double check. |
| if (!this->Finalized) |
| this->finalize(); |
| return this->getSymbol(Name, false).getAddress(); |
| }; |
| } |
| |
| void mapSectionAddress(const void *LocalAddress, |
| JITTargetAddress TargetAddr) const override { |
| assert(PFC && "mapSectionAddress called on finalized LinkedObjectSet"); |
| assert(PFC->RTDyld && "mapSectionAddress called on raw LinkedObjectSet"); |
| PFC->RTDyld->mapSectionAddress(LocalAddress, TargetAddr); |
| } |
| |
| private: |
| void buildInitialSymbolTable(const ObjSetT &Objects) { |
| for (const auto &Obj : Objects) |
| for (auto &Symbol : getObject(*Obj).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))); |
| } |
| } |
| |
| void updateSymbolTable(const RuntimeDyld &RTDyld) { |
| for (auto &SymEntry : SymbolTable) |
| SymEntry.second = RTDyld.getSymbol(SymEntry.first()); |
| } |
| |
| // Contains the information needed prior to finalization: the object files, |
| // memory manager, resolver, and flags needed for RuntimeDyld. |
| struct PreFinalizeContents { |
| PreFinalizeContents(ObjSetT Objects, SymbolResolverPtrT Resolver, |
| FinalizerFtor Finalizer, bool ProcessAllSections) |
| : Objects(std::move(Objects)), Resolver(std::move(Resolver)), |
| Finalizer(std::move(Finalizer)), |
| ProcessAllSections(ProcessAllSections) {} |
| |
| ObjSetT Objects; |
| SymbolResolverPtrT Resolver; |
| FinalizerFtor Finalizer; |
| bool ProcessAllSections; |
| ObjSetHandleT Handle; |
| RuntimeDyld *RTDyld; |
| }; |
| |
| MemoryManagerPtrT MemMgr; |
| std::unique_ptr<PreFinalizeContents> PFC; |
| }; |
| |
| template <typename ObjSetT, typename MemoryManagerPtrT, |
| typename SymbolResolverPtrT, typename FinalizerFtor> |
| std::unique_ptr< |
| ConcreteLinkedObjectSet<ObjSetT, MemoryManagerPtrT, |
| SymbolResolverPtrT, FinalizerFtor>> |
| createLinkedObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr, |
| SymbolResolverPtrT Resolver, |
| FinalizerFtor Finalizer, |
| bool ProcessAllSections) { |
| typedef ConcreteLinkedObjectSet<ObjSetT, MemoryManagerPtrT, |
| SymbolResolverPtrT, FinalizerFtor> LOS; |
| return llvm::make_unique<LOS>(std::move(Objects), std::move(MemMgr), |
| std::move(Resolver), std::move(Finalizer), |
| ProcessAllSections); |
| } |
| |
| public: |
| /// @brief LoadedObjectInfo list. Contains a list of owning pointers to |
| /// RuntimeDyld::LoadedObjectInfo instances. |
| typedef std::vector<std::unique_ptr<RuntimeDyld::LoadedObjectInfo>> |
| LoadedObjInfoList; |
| |
| /// @brief Construct an ObjectLinkingLayer with the given NotifyLoaded, |
| /// and NotifyFinalized functors. |
| RTDyldObjectLinkingLayer( |
| NotifyLoadedFtor NotifyLoaded = NotifyLoadedFtor(), |
| NotifyFinalizedFtor NotifyFinalized = NotifyFinalizedFtor()) |
| : 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 a set of objects (or archives) that will be treated as a unit |
| /// for the purposes of symbol lookup and memory management. |
| /// |
| /// @return A handle that can be used to refer to the loaded objects (for |
| /// symbol searching, finalization, freeing memory, etc.). |
| template <typename ObjSetT, |
| typename MemoryManagerPtrT, |
| typename SymbolResolverPtrT> |
| ObjSetHandleT addObjectSet(ObjSetT Objects, |
| MemoryManagerPtrT MemMgr, |
| SymbolResolverPtrT Resolver) { |
| auto Finalizer = [&](ObjSetHandleT H, RuntimeDyld &RTDyld, |
| const ObjSetT &Objs, |
| std::function<void()> LOSHandleLoad) { |
| LoadedObjInfoList LoadedObjInfos; |
| |
| for (auto &Obj : Objs) |
| LoadedObjInfos.push_back(RTDyld.loadObject(this->getObject(*Obj))); |
| |
| LOSHandleLoad(); |
| |
| this->NotifyLoaded(H, Objs, LoadedObjInfos); |
| |
| RTDyld.finalizeWithMemoryManagerLocking(); |
| |
| if (this->NotifyFinalized) |
| this->NotifyFinalized(H); |
| }; |
| |
| auto LOS = |
| createLinkedObjectSet(std::move(Objects), std::move(MemMgr), |
| std::move(Resolver), std::move(Finalizer), |
| ProcessAllSections); |
| // LOS is an owning-ptr. Keep a non-owning one so that we can set the handle |
| // below. |
| auto *LOSPtr = LOS.get(); |
| |
| ObjSetHandleT Handle = LinkedObjSetList.insert(LinkedObjSetList.end(), |
| std::move(LOS)); |
| LOSPtr->setHandle(Handle); |
| |
| return Handle; |
| } |
| |
| /// @brief Remove the set of objects associated with handle H. |
| /// |
| /// All memory allocated for the objects will be freed, and the sections and |
| /// symbols they 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. |
| void removeObjectSet(ObjSetHandleT H) { |
| // How do we invalidate the symbols in H? |
| LinkedObjSetList.erase(H); |
| } |
| |
| /// @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 I = LinkedObjSetList.begin(), E = LinkedObjSetList.end(); I != E; |
| ++I) |
| if (auto Symbol = findSymbolIn(I, Name, ExportedSymbolsOnly)) |
| return Symbol; |
| |
| return nullptr; |
| } |
| |
| /// @brief Search for the given named symbol in the context of the set of |
| /// loaded objects represented by the handle H. |
| /// @param H The handle for the object set 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 set. |
| JITSymbol findSymbolIn(ObjSetHandleT H, StringRef Name, |
| bool ExportedSymbolsOnly) { |
| return (*H)->getSymbol(Name, ExportedSymbolsOnly); |
| } |
| |
| /// @brief Map section addresses for the objects associated with the handle H. |
| void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress, |
| JITTargetAddress TargetAddr) { |
| (*H)->mapSectionAddress(LocalAddress, TargetAddr); |
| } |
| |
| /// @brief Immediately emit and finalize the object set represented by the |
| /// given handle. |
| /// @param H Handle for object set to emit/finalize. |
| void emitAndFinalize(ObjSetHandleT H) { |
| (*H)->finalize(); |
| } |
| |
| private: |
| static const object::ObjectFile& getObject(const object::ObjectFile &Obj) { |
| return Obj; |
| } |
| |
| template <typename ObjT> |
| static const object::ObjectFile& |
| getObject(const object::OwningBinary<ObjT> &Obj) { |
| return *Obj.getBinary(); |
| } |
| |
| LinkedObjectSetListT LinkedObjSetList; |
| NotifyLoadedFtor NotifyLoaded; |
| NotifyFinalizedFtor NotifyFinalized; |
| bool ProcessAllSections; |
| }; |
| |
| } // end namespace orc |
| } // end namespace llvm |
| |
| #endif // LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H |