Home | History | Annotate | Download | only in Orc
      1 //===- ObjectLinkingLayer.h - Add object files to a JIT process -*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // Contains the definition for the object layer of the JIT.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_EXECUTIONENGINE_ORC_OBJECTLINKINGLAYER_H
     15 #define LLVM_EXECUTIONENGINE_ORC_OBJECTLINKINGLAYER_H
     16 
     17 #include "JITSymbol.h"
     18 #include "llvm/ADT/STLExtras.h"
     19 #include "llvm/ExecutionEngine/ExecutionEngine.h"
     20 #include "llvm/ExecutionEngine/SectionMemoryManager.h"
     21 #include <list>
     22 #include <memory>
     23 
     24 namespace llvm {
     25 namespace orc {
     26 
     27 class ObjectLinkingLayerBase {
     28 protected:
     29   /// @brief Holds a set of objects to be allocated/linked as a unit in the JIT.
     30   ///
     31   /// An instance of this class will be created for each set of objects added
     32   /// via JITObjectLayer::addObjectSet. Deleting the instance (via
     33   /// removeObjectSet) frees its memory, removing all symbol definitions that
     34   /// had been provided by this instance. Higher level layers are responsible
     35   /// for taking any action required to handle the missing symbols.
     36   class LinkedObjectSet {
     37     LinkedObjectSet(const LinkedObjectSet&) = delete;
     38     void operator=(const LinkedObjectSet&) = delete;
     39   public:
     40     LinkedObjectSet() = default;
     41     virtual ~LinkedObjectSet() {}
     42 
     43     virtual void finalize() = 0;
     44 
     45     virtual JITSymbol::GetAddressFtor
     46     getSymbolMaterializer(std::string Name) = 0;
     47 
     48     virtual void mapSectionAddress(const void *LocalAddress,
     49                                    TargetAddress TargetAddr) const = 0;
     50 
     51     JITSymbol getSymbol(StringRef Name, bool ExportedSymbolsOnly) {
     52       auto SymEntry = SymbolTable.find(Name);
     53       if (SymEntry == SymbolTable.end())
     54         return nullptr;
     55       if (!SymEntry->second.isExported() && ExportedSymbolsOnly)
     56         return nullptr;
     57       if (!Finalized)
     58         return JITSymbol(getSymbolMaterializer(Name),
     59                          SymEntry->second.getFlags());
     60       return JITSymbol(SymEntry->second);
     61     }
     62   protected:
     63     StringMap<RuntimeDyld::SymbolInfo> SymbolTable;
     64     bool Finalized = false;
     65   };
     66 
     67   typedef std::list<std::unique_ptr<LinkedObjectSet>> LinkedObjectSetListT;
     68 
     69 public:
     70   /// @brief Handle to a set of loaded objects.
     71   typedef LinkedObjectSetListT::iterator ObjSetHandleT;
     72 };
     73 
     74 
     75 /// @brief Default (no-op) action to perform when loading objects.
     76 class DoNothingOnNotifyLoaded {
     77 public:
     78   template <typename ObjSetT, typename LoadResult>
     79   void operator()(ObjectLinkingLayerBase::ObjSetHandleT, const ObjSetT &,
     80                   const LoadResult &) {}
     81 };
     82 
     83 /// @brief Bare bones object linking layer.
     84 ///
     85 ///   This class is intended to be used as the base layer for a JIT. It allows
     86 /// object files to be loaded into memory, linked, and the addresses of their
     87 /// symbols queried. All objects added to this layer can see each other's
     88 /// symbols.
     89 template <typename NotifyLoadedFtor = DoNothingOnNotifyLoaded>
     90 class ObjectLinkingLayer : public ObjectLinkingLayerBase {
     91 public:
     92 
     93   /// @brief Functor for receiving finalization notifications.
     94   typedef std::function<void(ObjSetHandleT)> NotifyFinalizedFtor;
     95 
     96 private:
     97 
     98   template <typename ObjSetT, typename MemoryManagerPtrT,
     99             typename SymbolResolverPtrT, typename FinalizerFtor>
    100   class ConcreteLinkedObjectSet : public LinkedObjectSet {
    101   public:
    102     ConcreteLinkedObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr,
    103                             SymbolResolverPtrT Resolver,
    104                             FinalizerFtor Finalizer,
    105                             bool ProcessAllSections)
    106       : MemMgr(std::move(MemMgr)),
    107         PFC(llvm::make_unique<PreFinalizeContents>(std::move(Objects),
    108                                                    std::move(Resolver),
    109                                                    std::move(Finalizer),
    110                                                    ProcessAllSections)) {
    111       buildInitialSymbolTable(PFC->Objects);
    112     }
    113 
    114     void setHandle(ObjSetHandleT H) {
    115       PFC->Handle = H;
    116     }
    117 
    118     void finalize() override {
    119       assert(PFC && "mapSectionAddress called on finalized LinkedObjectSet");
    120 
    121       RuntimeDyld RTDyld(*MemMgr, *PFC->Resolver);
    122       RTDyld.setProcessAllSections(PFC->ProcessAllSections);
    123       PFC->RTDyld = &RTDyld;
    124 
    125       PFC->Finalizer(PFC->Handle, RTDyld, std::move(PFC->Objects),
    126                      [&]() {
    127                        this->updateSymbolTable(RTDyld);
    128                        this->Finalized = true;
    129                      });
    130 
    131       // Release resources.
    132       PFC = nullptr;
    133     }
    134 
    135     JITSymbol::GetAddressFtor getSymbolMaterializer(std::string Name) override {
    136       return
    137         [this, Name]() {
    138           // The symbol may be materialized between the creation of this lambda
    139           // and its execution, so we need to double check.
    140           if (!this->Finalized)
    141             this->finalize();
    142           return this->getSymbol(Name, false).getAddress();
    143         };
    144     }
    145 
    146     void mapSectionAddress(const void *LocalAddress,
    147                            TargetAddress TargetAddr) const override {
    148       assert(PFC && "mapSectionAddress called on finalized LinkedObjectSet");
    149       assert(PFC->RTDyld && "mapSectionAddress called on raw LinkedObjectSet");
    150       PFC->RTDyld->mapSectionAddress(LocalAddress, TargetAddr);
    151     }
    152 
    153   private:
    154 
    155     void buildInitialSymbolTable(const ObjSetT &Objects) {
    156       for (const auto &Obj : Objects)
    157         for (auto &Symbol : getObject(*Obj).symbols()) {
    158           if (Symbol.getFlags() & object::SymbolRef::SF_Undefined)
    159             continue;
    160           Expected<StringRef> SymbolName = Symbol.getName();
    161           // FIXME: Raise an error for bad symbols.
    162           if (!SymbolName) {
    163             consumeError(SymbolName.takeError());
    164             continue;
    165           }
    166           auto Flags = JITSymbol::flagsFromObjectSymbol(Symbol);
    167           SymbolTable.insert(
    168             std::make_pair(*SymbolName, RuntimeDyld::SymbolInfo(0, Flags)));
    169         }
    170     }
    171 
    172     void updateSymbolTable(const RuntimeDyld &RTDyld) {
    173       for (auto &SymEntry : SymbolTable)
    174         SymEntry.second = RTDyld.getSymbol(SymEntry.first());
    175     }
    176 
    177     // Contains the information needed prior to finalization: the object files,
    178     // memory manager, resolver, and flags needed for RuntimeDyld.
    179     struct PreFinalizeContents {
    180       PreFinalizeContents(ObjSetT Objects, SymbolResolverPtrT Resolver,
    181                           FinalizerFtor Finalizer, bool ProcessAllSections)
    182         : Objects(std::move(Objects)), Resolver(std::move(Resolver)),
    183           Finalizer(std::move(Finalizer)),
    184           ProcessAllSections(ProcessAllSections) {}
    185 
    186       ObjSetT Objects;
    187       SymbolResolverPtrT Resolver;
    188       FinalizerFtor Finalizer;
    189       bool ProcessAllSections;
    190       ObjSetHandleT Handle;
    191       RuntimeDyld *RTDyld;
    192     };
    193 
    194     MemoryManagerPtrT MemMgr;
    195     std::unique_ptr<PreFinalizeContents> PFC;
    196   };
    197 
    198   template <typename ObjSetT, typename MemoryManagerPtrT,
    199             typename SymbolResolverPtrT, typename FinalizerFtor>
    200   std::unique_ptr<
    201     ConcreteLinkedObjectSet<ObjSetT, MemoryManagerPtrT,
    202                             SymbolResolverPtrT, FinalizerFtor>>
    203   createLinkedObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr,
    204                         SymbolResolverPtrT Resolver,
    205                         FinalizerFtor Finalizer,
    206                         bool ProcessAllSections) {
    207     typedef ConcreteLinkedObjectSet<ObjSetT, MemoryManagerPtrT,
    208                                     SymbolResolverPtrT, FinalizerFtor> LOS;
    209     return llvm::make_unique<LOS>(std::move(Objects), std::move(MemMgr),
    210                                   std::move(Resolver), std::move(Finalizer),
    211                                   ProcessAllSections);
    212   }
    213 
    214 public:
    215 
    216   /// @brief LoadedObjectInfo list. Contains a list of owning pointers to
    217   ///        RuntimeDyld::LoadedObjectInfo instances.
    218   typedef std::vector<std::unique_ptr<RuntimeDyld::LoadedObjectInfo>>
    219       LoadedObjInfoList;
    220 
    221   /// @brief Construct an ObjectLinkingLayer with the given NotifyLoaded,
    222   ///        and NotifyFinalized functors.
    223   ObjectLinkingLayer(
    224       NotifyLoadedFtor NotifyLoaded = NotifyLoadedFtor(),
    225       NotifyFinalizedFtor NotifyFinalized = NotifyFinalizedFtor())
    226       : NotifyLoaded(std::move(NotifyLoaded)),
    227         NotifyFinalized(std::move(NotifyFinalized)),
    228         ProcessAllSections(false) {}
    229 
    230   /// @brief Set the 'ProcessAllSections' flag.
    231   ///
    232   /// If set to true, all sections in each object file will be allocated using
    233   /// the memory manager, rather than just the sections required for execution.
    234   ///
    235   /// This is kludgy, and may be removed in the future.
    236   void setProcessAllSections(bool ProcessAllSections) {
    237     this->ProcessAllSections = ProcessAllSections;
    238   }
    239 
    240   /// @brief Add a set of objects (or archives) that will be treated as a unit
    241   ///        for the purposes of symbol lookup and memory management.
    242   ///
    243   /// @return A handle that can be used to refer to the loaded objects (for
    244   ///         symbol searching, finalization, freeing memory, etc.).
    245   template <typename ObjSetT,
    246             typename MemoryManagerPtrT,
    247             typename SymbolResolverPtrT>
    248   ObjSetHandleT addObjectSet(ObjSetT Objects,
    249                              MemoryManagerPtrT MemMgr,
    250                              SymbolResolverPtrT Resolver) {
    251 
    252     auto Finalizer = [&](ObjSetHandleT H, RuntimeDyld &RTDyld,
    253                          const ObjSetT &Objs,
    254                          std::function<void()> LOSHandleLoad) {
    255       LoadedObjInfoList LoadedObjInfos;
    256 
    257       for (auto &Obj : Objs)
    258         LoadedObjInfos.push_back(RTDyld.loadObject(this->getObject(*Obj)));
    259 
    260       LOSHandleLoad();
    261 
    262       this->NotifyLoaded(H, Objs, LoadedObjInfos);
    263 
    264       RTDyld.finalizeWithMemoryManagerLocking();
    265 
    266       if (this->NotifyFinalized)
    267         this->NotifyFinalized(H);
    268     };
    269 
    270     auto LOS =
    271       createLinkedObjectSet(std::move(Objects), std::move(MemMgr),
    272                             std::move(Resolver), std::move(Finalizer),
    273                             ProcessAllSections);
    274     // LOS is an owning-ptr. Keep a non-owning one so that we can set the handle
    275     // below.
    276     auto *LOSPtr = LOS.get();
    277 
    278     ObjSetHandleT Handle = LinkedObjSetList.insert(LinkedObjSetList.end(),
    279                                                    std::move(LOS));
    280     LOSPtr->setHandle(Handle);
    281 
    282     return Handle;
    283   }
    284 
    285   /// @brief Remove the set of objects associated with handle H.
    286   ///
    287   ///   All memory allocated for the objects will be freed, and the sections and
    288   /// symbols they provided will no longer be available. No attempt is made to
    289   /// re-emit the missing symbols, and any use of these symbols (directly or
    290   /// indirectly) will result in undefined behavior. If dependence tracking is
    291   /// required to detect or resolve such issues it should be added at a higher
    292   /// layer.
    293   void removeObjectSet(ObjSetHandleT H) {
    294     // How do we invalidate the symbols in H?
    295     LinkedObjSetList.erase(H);
    296   }
    297 
    298   /// @brief Search for the given named symbol.
    299   /// @param Name The name of the symbol to search for.
    300   /// @param ExportedSymbolsOnly If true, search only for exported symbols.
    301   /// @return A handle for the given named symbol, if it exists.
    302   JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) {
    303     for (auto I = LinkedObjSetList.begin(), E = LinkedObjSetList.end(); I != E;
    304          ++I)
    305       if (auto Symbol = findSymbolIn(I, Name, ExportedSymbolsOnly))
    306         return Symbol;
    307 
    308     return nullptr;
    309   }
    310 
    311   /// @brief Search for the given named symbol in the context of the set of
    312   ///        loaded objects represented by the handle H.
    313   /// @param H The handle for the object set to search in.
    314   /// @param Name The name of the symbol to search for.
    315   /// @param ExportedSymbolsOnly If true, search only for exported symbols.
    316   /// @return A handle for the given named symbol, if it is found in the
    317   ///         given object set.
    318   JITSymbol findSymbolIn(ObjSetHandleT H, StringRef Name,
    319                          bool ExportedSymbolsOnly) {
    320     return (*H)->getSymbol(Name, ExportedSymbolsOnly);
    321   }
    322 
    323   /// @brief Map section addresses for the objects associated with the handle H.
    324   void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress,
    325                          TargetAddress TargetAddr) {
    326     (*H)->mapSectionAddress(LocalAddress, TargetAddr);
    327   }
    328 
    329   /// @brief Immediately emit and finalize the object set represented by the
    330   ///        given handle.
    331   /// @param H Handle for object set to emit/finalize.
    332   void emitAndFinalize(ObjSetHandleT H) {
    333     (*H)->finalize();
    334   }
    335 
    336 private:
    337 
    338   static const object::ObjectFile& getObject(const object::ObjectFile &Obj) {
    339     return Obj;
    340   }
    341 
    342   template <typename ObjT>
    343   static const object::ObjectFile&
    344   getObject(const object::OwningBinary<ObjT> &Obj) {
    345     return *Obj.getBinary();
    346   }
    347 
    348   LinkedObjectSetListT LinkedObjSetList;
    349   NotifyLoadedFtor NotifyLoaded;
    350   NotifyFinalizedFtor NotifyFinalized;
    351   bool ProcessAllSections;
    352 };
    353 
    354 } // End namespace orc.
    355 } // End namespace llvm
    356 
    357 #endif // LLVM_EXECUTIONENGINE_ORC_OBJECTLINKINGLAYER_H
    358