1 //===- RTDyldObjectLinkingLayer.h - RTDyld-based jit linking  ---*- C++ -*-===//
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 // Contains the definition for an RTDyld-based, in-process object linking layer.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H
14 #define LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H
15 
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/StringMap.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/ExecutionEngine/JITSymbol.h"
20 #include "llvm/ExecutionEngine/Orc/Core.h"
21 #include "llvm/ExecutionEngine/Orc/Layer.h"
22 #include "llvm/ExecutionEngine/Orc/Legacy.h"
23 #include "llvm/ExecutionEngine/RuntimeDyld.h"
24 #include "llvm/Object/ObjectFile.h"
25 #include "llvm/Support/Error.h"
26 #include <algorithm>
27 #include <cassert>
28 #include <functional>
29 #include <list>
30 #include <memory>
31 #include <string>
32 #include <utility>
33 #include <vector>
34 
35 namespace llvm {
36 namespace orc {
37 
38 class RTDyldObjectLinkingLayer : public ObjectLayer {
39 public:
40   /// Functor for receiving object-loaded notifications.
41   using NotifyLoadedFunction =
42       std::function<void(VModuleKey, const object::ObjectFile &Obj,
43                          const RuntimeDyld::LoadedObjectInfo &)>;
44 
45   /// Functor for receiving finalization notifications.
46   using NotifyEmittedFunction =
47       std::function<void(VModuleKey, std::unique_ptr<MemoryBuffer>)>;
48 
49   using GetMemoryManagerFunction =
50       std::function<std::unique_ptr<RuntimeDyld::MemoryManager>()>;
51 
52   /// Construct an ObjectLinkingLayer with the given NotifyLoaded,
53   ///        and NotifyEmitted functors.
54   RTDyldObjectLinkingLayer(ExecutionSession &ES,
55                            GetMemoryManagerFunction GetMemoryManager);
56 
57   ~RTDyldObjectLinkingLayer();
58 
59   /// Emit the object.
60   void emit(MaterializationResponsibility R,
61             std::unique_ptr<MemoryBuffer> O) override;
62 
63   /// Set the NotifyLoaded callback.
64   RTDyldObjectLinkingLayer &setNotifyLoaded(NotifyLoadedFunction NotifyLoaded) {
65     this->NotifyLoaded = std::move(NotifyLoaded);
66     return *this;
67   }
68 
69   /// Set the NotifyEmitted callback.
70   RTDyldObjectLinkingLayer &
71   setNotifyEmitted(NotifyEmittedFunction NotifyEmitted) {
72     this->NotifyEmitted = std::move(NotifyEmitted);
73     return *this;
74   }
75 
76   /// Set the 'ProcessAllSections' flag.
77   ///
78   /// If set to true, all sections in each object file will be allocated using
79   /// the memory manager, rather than just the sections required for execution.
80   ///
81   /// This is kludgy, and may be removed in the future.
82   RTDyldObjectLinkingLayer &setProcessAllSections(bool ProcessAllSections) {
83     this->ProcessAllSections = ProcessAllSections;
84     return *this;
85   }
86 
87   /// Instructs this RTDyldLinkingLayer2 instance to override the symbol flags
88   /// returned by RuntimeDyld for any given object file with the flags supplied
89   /// by the MaterializationResponsibility instance. This is a workaround to
90   /// support symbol visibility in COFF, which does not use the libObject's
91   /// SF_Exported flag. Use only when generating / adding COFF object files.
92   ///
93   /// FIXME: We should be able to remove this if/when COFF properly tracks
94   /// exported symbols.
95   RTDyldObjectLinkingLayer &
96   setOverrideObjectFlagsWithResponsibilityFlags(bool OverrideObjectFlags) {
97     this->OverrideObjectFlags = OverrideObjectFlags;
98     return *this;
99   }
100 
101   /// If set, this RTDyldObjectLinkingLayer instance will claim responsibility
102   /// for any symbols provided by a given object file that were not already in
103   /// the MaterializationResponsibility instance. Setting this flag allows
104   /// higher-level program representations (e.g. LLVM IR) to be added based on
105   /// only a subset of the symbols they provide, without having to write
106   /// intervening layers to scan and add the additional symbols. This trades
107   /// diagnostic quality for convenience however: If all symbols are enumerated
108   /// up-front then clashes can be detected and reported early (and usually
109   /// deterministically). If this option is set, clashes for the additional
110   /// symbols may not be detected until late, and detection may depend on
111   /// the flow of control through JIT'd code. Use with care.
112   RTDyldObjectLinkingLayer &
113   setAutoClaimResponsibilityForObjectSymbols(bool AutoClaimObjectSymbols) {
114     this->AutoClaimObjectSymbols = AutoClaimObjectSymbols;
115     return *this;
116   }
117 
118 private:
119   Error onObjLoad(VModuleKey K, MaterializationResponsibility &R,
120                   object::ObjectFile &Obj,
121                   std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
122                   std::map<StringRef, JITEvaluatedSymbol> Resolved,
123                   std::set<StringRef> &InternalSymbols);
124 
125   void onObjEmit(VModuleKey K, std::unique_ptr<MemoryBuffer> ObjBuffer,
126                  MaterializationResponsibility &R, Error Err);
127 
128   mutable std::mutex RTDyldLayerMutex;
129   GetMemoryManagerFunction GetMemoryManager;
130   NotifyLoadedFunction NotifyLoaded;
131   NotifyEmittedFunction NotifyEmitted;
132   bool ProcessAllSections = false;
133   bool OverrideObjectFlags = false;
134   bool AutoClaimObjectSymbols = false;
135   std::vector<std::unique_ptr<RuntimeDyld::MemoryManager>> MemMgrs;
136 };
137 
138 class LegacyRTDyldObjectLinkingLayerBase {
139 public:
140   using ObjectPtr = std::unique_ptr<MemoryBuffer>;
141 
142 protected:
143 
144   /// Holds an object to be allocated/linked as a unit in the JIT.
145   ///
146   /// An instance of this class will be created for each object added
147   /// via JITObjectLayer::addObject. Deleting the instance (via
148   /// removeObject) frees its memory, removing all symbol definitions that
149   /// had been provided by this instance. Higher level layers are responsible
150   /// for taking any action required to handle the missing symbols.
151   class LinkedObject {
152   public:
153     LinkedObject() = default;
154     LinkedObject(const LinkedObject&) = delete;
155     void operator=(const LinkedObject&) = delete;
156     virtual ~LinkedObject() = default;
157 
158     virtual Error finalize() = 0;
159 
160     virtual JITSymbol::GetAddressFtor
161     getSymbolMaterializer(std::string Name) = 0;
162 
163     virtual void mapSectionAddress(const void *LocalAddress,
164                                    JITTargetAddress TargetAddr) const = 0;
165 
166     JITSymbol getSymbol(StringRef Name, bool ExportedSymbolsOnly) {
167       auto SymEntry = SymbolTable.find(Name);
168       if (SymEntry == SymbolTable.end())
169         return nullptr;
170       if (!SymEntry->second.getFlags().isExported() && ExportedSymbolsOnly)
171         return nullptr;
172       if (!Finalized)
173         return JITSymbol(getSymbolMaterializer(Name),
174                          SymEntry->second.getFlags());
175       return JITSymbol(SymEntry->second);
176     }
177 
178   protected:
179     StringMap<JITEvaluatedSymbol> SymbolTable;
180     bool Finalized = false;
181   };
182 };
183 
184 /// Bare bones object linking layer.
185 ///
186 ///   This class is intended to be used as the base layer for a JIT. It allows
187 /// object files to be loaded into memory, linked, and the addresses of their
188 /// symbols queried. All objects added to this layer can see each other's
189 /// symbols.
190 class LegacyRTDyldObjectLinkingLayer : public LegacyRTDyldObjectLinkingLayerBase {
191 public:
192 
193   using LegacyRTDyldObjectLinkingLayerBase::ObjectPtr;
194 
195   /// Functor for receiving object-loaded notifications.
196   using NotifyLoadedFtor =
197       std::function<void(VModuleKey, const object::ObjectFile &Obj,
198                          const RuntimeDyld::LoadedObjectInfo &)>;
199 
200   /// Functor for receiving finalization notifications.
201   using NotifyFinalizedFtor =
202       std::function<void(VModuleKey, const object::ObjectFile &Obj,
203                          const RuntimeDyld::LoadedObjectInfo &)>;
204 
205   /// Functor for receiving deallocation notifications.
206   using NotifyFreedFtor = std::function<void(VModuleKey, const object::ObjectFile &Obj)>;
207 
208 private:
209   using OwnedObject = object::OwningBinary<object::ObjectFile>;
210 
211   template <typename MemoryManagerPtrT>
212   class ConcreteLinkedObject : public LinkedObject {
213   public:
214     ConcreteLinkedObject(LegacyRTDyldObjectLinkingLayer &Parent, VModuleKey K,
215                          OwnedObject Obj, MemoryManagerPtrT MemMgr,
216                          std::shared_ptr<SymbolResolver> Resolver,
217                          bool ProcessAllSections)
218         : K(std::move(K)),
219           Parent(Parent),
220           MemMgr(std::move(MemMgr)),
221           PFC(std::make_unique<PreFinalizeContents>(
222               std::move(Obj), std::move(Resolver),
223               ProcessAllSections)) {
224       buildInitialSymbolTable(PFC->Obj);
225     }
226 
227     ~ConcreteLinkedObject() override {
228       if (this->Parent.NotifyFreed && ObjForNotify.getBinary())
229         this->Parent.NotifyFreed(K, *ObjForNotify.getBinary());
230 
231       MemMgr->deregisterEHFrames();
232     }
233 
234     Error finalize() override {
235       assert(PFC && "mapSectionAddress called on finalized LinkedObject");
236 
237       JITSymbolResolverAdapter ResolverAdapter(Parent.ES, *PFC->Resolver,
238 					       nullptr);
239       PFC->RTDyld = std::make_unique<RuntimeDyld>(*MemMgr, ResolverAdapter);
240       PFC->RTDyld->setProcessAllSections(PFC->ProcessAllSections);
241 
242       Finalized = true;
243 
244       std::unique_ptr<RuntimeDyld::LoadedObjectInfo> Info =
245           PFC->RTDyld->loadObject(*PFC->Obj.getBinary());
246 
247       // Copy the symbol table out of the RuntimeDyld instance.
248       {
249         auto SymTab = PFC->RTDyld->getSymbolTable();
250         for (auto &KV : SymTab)
251           SymbolTable[KV.first] = KV.second;
252       }
253 
254       if (Parent.NotifyLoaded)
255         Parent.NotifyLoaded(K, *PFC->Obj.getBinary(), *Info);
256 
257       PFC->RTDyld->finalizeWithMemoryManagerLocking();
258 
259       if (PFC->RTDyld->hasError())
260         return make_error<StringError>(PFC->RTDyld->getErrorString(),
261                                        inconvertibleErrorCode());
262 
263       if (Parent.NotifyFinalized)
264         Parent.NotifyFinalized(K, *PFC->Obj.getBinary(), *Info);
265 
266       // Release resources.
267       if (this->Parent.NotifyFreed)
268         ObjForNotify = std::move(PFC->Obj); // needed for callback
269       PFC = nullptr;
270       return Error::success();
271     }
272 
273     JITSymbol::GetAddressFtor getSymbolMaterializer(std::string Name) override {
274       return [this, Name]() -> Expected<JITTargetAddress> {
275         // The symbol may be materialized between the creation of this lambda
276         // and its execution, so we need to double check.
277         if (!this->Finalized)
278           if (auto Err = this->finalize())
279             return std::move(Err);
280         return this->getSymbol(Name, false).getAddress();
281       };
282     }
283 
284     void mapSectionAddress(const void *LocalAddress,
285                            JITTargetAddress TargetAddr) const override {
286       assert(PFC && "mapSectionAddress called on finalized LinkedObject");
287       assert(PFC->RTDyld && "mapSectionAddress called on raw LinkedObject");
288       PFC->RTDyld->mapSectionAddress(LocalAddress, TargetAddr);
289     }
290 
291   private:
292     void buildInitialSymbolTable(const OwnedObject &Obj) {
293       for (auto &Symbol : Obj.getBinary()->symbols()) {
294         if (Symbol.getFlags() & object::SymbolRef::SF_Undefined)
295           continue;
296         Expected<StringRef> SymbolName = Symbol.getName();
297         // FIXME: Raise an error for bad symbols.
298         if (!SymbolName) {
299           consumeError(SymbolName.takeError());
300           continue;
301         }
302         // FIXME: Raise an error for bad symbols.
303         auto Flags = JITSymbolFlags::fromObjectSymbol(Symbol);
304         if (!Flags) {
305           consumeError(Flags.takeError());
306           continue;
307         }
308         SymbolTable.insert(
309             std::make_pair(*SymbolName, JITEvaluatedSymbol(0, *Flags)));
310       }
311     }
312 
313     // Contains the information needed prior to finalization: the object files,
314     // memory manager, resolver, and flags needed for RuntimeDyld.
315     struct PreFinalizeContents {
316       PreFinalizeContents(OwnedObject Obj,
317                           std::shared_ptr<SymbolResolver> Resolver,
318                           bool ProcessAllSections)
319           : Obj(std::move(Obj)),
320             Resolver(std::move(Resolver)),
321             ProcessAllSections(ProcessAllSections) {}
322 
323       OwnedObject Obj;
324       std::shared_ptr<SymbolResolver> Resolver;
325       bool ProcessAllSections;
326       std::unique_ptr<RuntimeDyld> RTDyld;
327     };
328 
329     VModuleKey K;
330     LegacyRTDyldObjectLinkingLayer &Parent;
331     MemoryManagerPtrT MemMgr;
332     OwnedObject ObjForNotify;
333     std::unique_ptr<PreFinalizeContents> PFC;
334   };
335 
336   template <typename MemoryManagerPtrT>
337   std::unique_ptr<ConcreteLinkedObject<MemoryManagerPtrT>>
338   createLinkedObject(LegacyRTDyldObjectLinkingLayer &Parent, VModuleKey K,
339                      OwnedObject Obj, MemoryManagerPtrT MemMgr,
340                      std::shared_ptr<SymbolResolver> Resolver,
341                      bool ProcessAllSections) {
342     using LOS = ConcreteLinkedObject<MemoryManagerPtrT>;
343     return std::make_unique<LOS>(Parent, std::move(K), std::move(Obj),
344                                   std::move(MemMgr), std::move(Resolver),
345                                   ProcessAllSections);
346   }
347 
348 public:
349   struct Resources {
350     std::shared_ptr<RuntimeDyld::MemoryManager> MemMgr;
351     std::shared_ptr<SymbolResolver> Resolver;
352   };
353 
354   using ResourcesGetter = std::function<Resources(VModuleKey)>;
355 
356   /// Construct an ObjectLinkingLayer with the given NotifyLoaded,
357   ///        and NotifyFinalized functors.
358   LLVM_ATTRIBUTE_DEPRECATED(
359       LegacyRTDyldObjectLinkingLayer(
360           ExecutionSession &ES, ResourcesGetter GetResources,
361           NotifyLoadedFtor NotifyLoaded = NotifyLoadedFtor(),
362           NotifyFinalizedFtor NotifyFinalized = NotifyFinalizedFtor(),
363           NotifyFreedFtor NotifyFreed = NotifyFreedFtor()),
364       "ORCv1 layers (layers with the 'Legacy' prefix) are deprecated. Please "
365       "use "
366       "ORCv2 (see docs/ORCv2.rst)");
367 
368   // Legacy layer constructor with deprecation acknowledgement.
369   LegacyRTDyldObjectLinkingLayer(
370       ORCv1DeprecationAcknowledgement, ExecutionSession &ES,
371       ResourcesGetter GetResources,
372       NotifyLoadedFtor NotifyLoaded = NotifyLoadedFtor(),
373       NotifyFinalizedFtor NotifyFinalized = NotifyFinalizedFtor(),
374       NotifyFreedFtor NotifyFreed = NotifyFreedFtor())
375       : ES(ES), GetResources(std::move(GetResources)),
376         NotifyLoaded(std::move(NotifyLoaded)),
377         NotifyFinalized(std::move(NotifyFinalized)),
378         NotifyFreed(std::move(NotifyFreed)), ProcessAllSections(false) {}
379 
380   /// Set the 'ProcessAllSections' flag.
381   ///
382   /// If set to true, all sections in each object file will be allocated using
383   /// the memory manager, rather than just the sections required for execution.
384   ///
385   /// This is kludgy, and may be removed in the future.
386   void setProcessAllSections(bool ProcessAllSections) {
387     this->ProcessAllSections = ProcessAllSections;
388   }
389 
390   /// Add an object to the JIT.
391   Error addObject(VModuleKey K, ObjectPtr ObjBuffer) {
392 
393     auto Obj =
394         object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef());
395     if (!Obj)
396       return Obj.takeError();
397 
398     assert(!LinkedObjects.count(K) && "VModuleKey already in use");
399 
400     auto R = GetResources(K);
401 
402     LinkedObjects[K] = createLinkedObject(
403         *this, K, OwnedObject(std::move(*Obj), std::move(ObjBuffer)),
404         std::move(R.MemMgr), std::move(R.Resolver), ProcessAllSections);
405 
406     return Error::success();
407   }
408 
409   /// Remove the object associated with VModuleKey K.
410   ///
411   ///   All memory allocated for the object will be freed, and the sections and
412   /// symbols it provided will no longer be available. No attempt is made to
413   /// re-emit the missing symbols, and any use of these symbols (directly or
414   /// indirectly) will result in undefined behavior. If dependence tracking is
415   /// required to detect or resolve such issues it should be added at a higher
416   /// layer.
417   Error removeObject(VModuleKey K) {
418     assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
419     // How do we invalidate the symbols in H?
420     LinkedObjects.erase(K);
421     return Error::success();
422   }
423 
424   /// Search for the given named symbol.
425   /// @param Name The name of the symbol to search for.
426   /// @param ExportedSymbolsOnly If true, search only for exported symbols.
427   /// @return A handle for the given named symbol, if it exists.
428   JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) {
429     for (auto &KV : LinkedObjects)
430       if (auto Sym = KV.second->getSymbol(Name, ExportedSymbolsOnly))
431         return Sym;
432       else if (auto Err = Sym.takeError())
433         return std::move(Err);
434 
435     return nullptr;
436   }
437 
438   /// Search for the given named symbol in the context of the loaded
439   ///        object represented by the VModuleKey K.
440   /// @param K The VModuleKey for the object to search in.
441   /// @param Name The name of the symbol to search for.
442   /// @param ExportedSymbolsOnly If true, search only for exported symbols.
443   /// @return A handle for the given named symbol, if it is found in the
444   ///         given object.
445   JITSymbol findSymbolIn(VModuleKey K, StringRef Name,
446                          bool ExportedSymbolsOnly) {
447     assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
448     return LinkedObjects[K]->getSymbol(Name, ExportedSymbolsOnly);
449   }
450 
451   /// Map section addresses for the object associated with the
452   ///        VModuleKey K.
453   void mapSectionAddress(VModuleKey K, const void *LocalAddress,
454                          JITTargetAddress TargetAddr) {
455     assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
456     LinkedObjects[K]->mapSectionAddress(LocalAddress, TargetAddr);
457   }
458 
459   /// Immediately emit and finalize the object represented by the given
460   ///        VModuleKey.
461   /// @param K VModuleKey for object to emit/finalize.
462   Error emitAndFinalize(VModuleKey K) {
463     assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
464     return LinkedObjects[K]->finalize();
465   }
466 
467 private:
468   ExecutionSession &ES;
469 
470   ResourcesGetter GetResources;
471   NotifyLoadedFtor NotifyLoaded;
472   NotifyFinalizedFtor NotifyFinalized;
473   NotifyFreedFtor NotifyFreed;
474 
475   // NB!  `LinkedObjects` needs to be destroyed before `NotifyFreed` because
476   // `~ConcreteLinkedObject` calls `NotifyFreed`
477   std::map<VModuleKey, std::unique_ptr<LinkedObject>> LinkedObjects;
478   bool ProcessAllSections = false;
479 };
480 
481 } // end namespace orc
482 } // end namespace llvm
483 
484 #endif // LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H
485