xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ExecutionEngine/Orc/ELFNixPlatform.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- ELFNixPlatform.h -- Utilities for executing ELF in Orc --*- 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 // Linux/BSD support for executing JIT'd ELF in Orc.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_EXECUTIONENGINE_ORC_ELFNIXPLATFORM_H
14 #define LLVM_EXECUTIONENGINE_ORC_ELFNIXPLATFORM_H
15 
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/ExecutionEngine/Orc/Core.h"
18 #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
19 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
20 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
21 #include "llvm/Support/Compiler.h"
22 
23 #include <future>
24 #include <thread>
25 #include <unordered_map>
26 #include <vector>
27 
28 namespace llvm {
29 namespace orc {
30 
31 struct ELFPerObjectSectionsToRegister {
32   ExecutorAddrRange EHFrameSection;
33   ExecutorAddrRange ThreadDataSection;
34 };
35 
36 using ELFNixJITDylibDepInfo = std::vector<ExecutorAddr>;
37 using ELFNixJITDylibDepInfoMap =
38     std::vector<std::pair<ExecutorAddr, ELFNixJITDylibDepInfo>>;
39 
40 struct RuntimeFunction {
RuntimeFunctionRuntimeFunction41   RuntimeFunction(SymbolStringPtr Name) : Name(std::move(Name)) {}
42   SymbolStringPtr Name;
43   ExecutorAddr Addr;
44 };
45 
46 struct FunctionPairKeyHash {
47   std::size_t
operatorFunctionPairKeyHash48   operator()(const std::pair<RuntimeFunction *, RuntimeFunction *> &key) const {
49     return std::hash<void *>()(key.first->Addr.toPtr<void *>()) ^
50            std::hash<void *>()(key.second->Addr.toPtr<void *>());
51   }
52 };
53 
54 struct FunctionPairKeyEqual {
55   std::size_t
operatorFunctionPairKeyEqual56   operator()(const std::pair<RuntimeFunction *, RuntimeFunction *> &lhs,
57              const std::pair<RuntimeFunction *, RuntimeFunction *> &rhs) const {
58     return lhs.first == rhs.first && lhs.second == rhs.second;
59   }
60 };
61 
62 using DeferredRuntimeFnMap = std::unordered_map<
63     std::pair<RuntimeFunction *, RuntimeFunction *>,
64     SmallVector<std::pair<shared::WrapperFunctionCall::ArgDataBufferType,
65                           shared::WrapperFunctionCall::ArgDataBufferType>>,
66     FunctionPairKeyHash, FunctionPairKeyEqual>;
67 
68 /// Mediates between ELFNix initialization and ExecutionSession state.
69 class LLVM_ABI ELFNixPlatform : public Platform {
70 public:
71   /// Try to create a ELFNixPlatform instance, adding the ORC runtime to the
72   /// given JITDylib.
73   ///
74   /// The ORC runtime requires access to a number of symbols in
75   /// libc++. It is up to the caller to ensure that the required
76   /// symbols can be referenced by code added to PlatformJD. The
77   /// standard way to achieve this is to first attach dynamic library
78   /// search generators for either the given process, or for the
79   /// specific required libraries, to PlatformJD, then to create the
80   /// platform instance:
81   ///
82   /// \code{.cpp}
83   ///   auto &PlatformJD = ES.createBareJITDylib("stdlib");
84   ///   PlatformJD.addGenerator(
85   ///     ExitOnErr(EPCDynamicLibrarySearchGenerator
86   ///                 ::GetForTargetProcess(EPC)));
87   ///   ES.setPlatform(
88   ///     ExitOnErr(ELFNixPlatform::Create(ES, ObjLayer, EPC, PlatformJD,
89   ///                                     "/path/to/orc/runtime")));
90   /// \endcode
91   ///
92   /// Alternatively, these symbols could be added to another JITDylib that
93   /// PlatformJD links against.
94   ///
95   /// Clients are also responsible for ensuring that any JIT'd code that
96   /// depends on runtime functions (including any code using TLV or static
97   /// destructors) can reference the runtime symbols. This is usually achieved
98   /// by linking any JITDylibs containing regular code against
99   /// PlatformJD.
100   ///
101   /// By default, ELFNixPlatform will add the set of aliases returned by the
102   /// standardPlatformAliases function. This includes both required aliases
103   /// (e.g. __cxa_atexit -> __orc_rt_elf_cxa_atexit for static destructor
104   /// support), and optional aliases that provide JIT versions of common
105   /// functions (e.g. dlopen -> __orc_rt_elf_jit_dlopen). Clients can
106   /// override these defaults by passing a non-None value for the
107   /// RuntimeAliases function, in which case the client is responsible for
108   /// setting up all aliases (including the required ones).
109   static Expected<std::unique_ptr<ELFNixPlatform>>
110   Create(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
111          std::unique_ptr<DefinitionGenerator> OrcRuntime,
112          std::optional<SymbolAliasMap> RuntimeAliases = std::nullopt);
113 
114   /// Construct using a path to the ORC runtime.
115   static Expected<std::unique_ptr<ELFNixPlatform>>
116   Create(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
117          const char *OrcRuntimePath,
118          std::optional<SymbolAliasMap> RuntimeAliases = std::nullopt);
119 
getExecutionSession()120   ExecutionSession &getExecutionSession() const { return ES; }
getObjectLinkingLayer()121   ObjectLinkingLayer &getObjectLinkingLayer() const { return ObjLinkingLayer; }
122 
123   Error setupJITDylib(JITDylib &JD) override;
124   Error teardownJITDylib(JITDylib &JD) override;
125   Error notifyAdding(ResourceTracker &RT,
126                      const MaterializationUnit &MU) override;
127   Error notifyRemoving(ResourceTracker &RT) override;
128 
129   /// Returns an AliasMap containing the default aliases for the ELFNixPlatform.
130   /// This can be modified by clients when constructing the platform to add
131   /// or remove aliases.
132   static Expected<SymbolAliasMap> standardPlatformAliases(ExecutionSession &ES,
133                                                           JITDylib &PlatformJD);
134 
135   /// Returns the array of required CXX aliases.
136   static ArrayRef<std::pair<const char *, const char *>> requiredCXXAliases();
137 
138   /// Returns the array of standard runtime utility aliases for ELF.
139   static ArrayRef<std::pair<const char *, const char *>>
140   standardRuntimeUtilityAliases();
141 
142   /// Returns a list of aliases required to enable lazy compilation via the
143   /// ORC runtime.
144   static ArrayRef<std::pair<const char *, const char *>>
145   standardLazyCompilationAliases();
146 
147 private:
148   // Data needed for bootstrap only.
149   struct BootstrapInfo {
150     std::mutex Mutex;
151     std::condition_variable CV;
152     size_t ActiveGraphs = 0;
153     ExecutorAddr ELFNixHeaderAddr;
154     DeferredRuntimeFnMap DeferredRTFnMap;
155 
addArgumentsToRTFnMapBootstrapInfo156     void addArgumentsToRTFnMap(
157         RuntimeFunction *func1, RuntimeFunction *func2,
158         const shared::WrapperFunctionCall::ArgDataBufferType &arg1,
159         const shared::WrapperFunctionCall::ArgDataBufferType &arg2) {
160       std::lock_guard<std::mutex> Lock(Mutex);
161       auto &argList = DeferredRTFnMap[std::make_pair(func1, func2)];
162       argList.emplace_back(arg1, arg2);
163     }
164   };
165 
166   // The ELFNixPlatformPlugin scans/modifies LinkGraphs to support ELF
167   // platform features including initializers, exceptions, TLV, and language
168   // runtime registration.
169   class LLVM_ABI ELFNixPlatformPlugin : public ObjectLinkingLayer::Plugin {
170   public:
ELFNixPlatformPlugin(ELFNixPlatform & MP)171     ELFNixPlatformPlugin(ELFNixPlatform &MP) : MP(MP) {}
172 
173     void modifyPassConfig(MaterializationResponsibility &MR,
174                           jitlink::LinkGraph &G,
175                           jitlink::PassConfiguration &Config) override;
176 
177     // FIXME: We should be tentatively tracking scraped sections and discarding
178     // if the MR fails.
notifyFailed(MaterializationResponsibility & MR)179     Error notifyFailed(MaterializationResponsibility &MR) override {
180       return Error::success();
181     }
182 
notifyRemovingResources(JITDylib & JD,ResourceKey K)183     Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
184       return Error::success();
185     }
186 
notifyTransferringResources(JITDylib & JD,ResourceKey DstKey,ResourceKey SrcKey)187     void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
188                                      ResourceKey SrcKey) override {}
189 
190   private:
191     Error bootstrapPipelineStart(jitlink::LinkGraph &G);
192     Error bootstrapPipelineRecordRuntimeFunctions(jitlink::LinkGraph &G);
193     Error bootstrapPipelineEnd(jitlink::LinkGraph &G);
194 
195     void addDSOHandleSupportPasses(MaterializationResponsibility &MR,
196                                    jitlink::PassConfiguration &Config);
197 
198     void addEHAndTLVSupportPasses(MaterializationResponsibility &MR,
199                                   jitlink::PassConfiguration &Config,
200                                   bool IsBootstrapping);
201 
202     Error preserveInitSections(jitlink::LinkGraph &G,
203                                MaterializationResponsibility &MR);
204 
205     Error registerInitSections(jitlink::LinkGraph &G, JITDylib &JD,
206                                bool IsBootstrapping);
207 
208     Error fixTLVSectionsAndEdges(jitlink::LinkGraph &G, JITDylib &JD);
209 
210     std::mutex PluginMutex;
211     ELFNixPlatform &MP;
212   };
213 
214   using PushInitializersSendResultFn =
215       unique_function<void(Expected<ELFNixJITDylibDepInfoMap>)>;
216 
217   using SendSymbolAddressFn = unique_function<void(Expected<ExecutorAddr>)>;
218 
219   static bool supportedTarget(const Triple &TT);
220 
221   ELFNixPlatform(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
222                  std::unique_ptr<DefinitionGenerator> OrcRuntimeGenerator,
223                  Error &Err);
224 
225   // Associate ELFNixPlatform JIT-side runtime support functions with handlers.
226   Error associateRuntimeSupportFunctions(JITDylib &PlatformJD);
227 
228   void pushInitializersLoop(PushInitializersSendResultFn SendResult,
229                             JITDylibSP JD);
230 
231   void rt_recordInitializers(PushInitializersSendResultFn SendResult,
232                              ExecutorAddr JDHeader);
233 
234   void rt_lookupSymbol(SendSymbolAddressFn SendResult, ExecutorAddr Handle,
235                        StringRef SymbolName);
236 
237   Error registerPerObjectSections(jitlink::LinkGraph &G,
238                                   const ELFPerObjectSectionsToRegister &POSR,
239                                   bool IsBootstrapping);
240 
241   Expected<uint64_t> createPThreadKey();
242 
243   ExecutionSession &ES;
244   JITDylib &PlatformJD;
245   ObjectLinkingLayer &ObjLinkingLayer;
246 
247   SymbolStringPtr DSOHandleSymbol;
248 
249   RuntimeFunction PlatformBootstrap{
250       ES.intern("__orc_rt_elfnix_platform_bootstrap")};
251   RuntimeFunction PlatformShutdown{
252       ES.intern("__orc_rt_elfnix_platform_shutdown")};
253   RuntimeFunction RegisterJITDylib{
254       ES.intern("__orc_rt_elfnix_register_jitdylib")};
255   RuntimeFunction DeregisterJITDylib{
256       ES.intern("__orc_rt_elfnix_deregister_jitdylib")};
257   RuntimeFunction RegisterObjectSections{
258       ES.intern("__orc_rt_elfnix_register_object_sections")};
259   RuntimeFunction DeregisterObjectSections{
260       ES.intern("__orc_rt_elfnix_deregister_object_sections")};
261   RuntimeFunction RegisterInitSections{
262       ES.intern("__orc_rt_elfnix_register_init_sections")};
263   RuntimeFunction DeregisterInitSections{
264       ES.intern("__orc_rt_elfnix_deregister_init_sections")};
265   RuntimeFunction CreatePThreadKey{
266       ES.intern("__orc_rt_elfnix_create_pthread_key")};
267 
268   DenseMap<JITDylib *, SymbolLookupSet> RegisteredInitSymbols;
269 
270   // InitSeqs gets its own mutex to avoid locking the whole session when
271   // aggregating data from the jitlink.
272   std::mutex PlatformMutex;
273   std::vector<ELFPerObjectSectionsToRegister> BootstrapPOSRs;
274 
275   DenseMap<ExecutorAddr, JITDylib *> HandleAddrToJITDylib;
276   DenseMap<JITDylib *, ExecutorAddr> JITDylibToHandleAddr;
277   DenseMap<JITDylib *, uint64_t> JITDylibToPThreadKey;
278 
279   std::atomic<BootstrapInfo *> Bootstrap;
280 };
281 
282 namespace shared {
283 
284 using SPSELFPerObjectSectionsToRegister =
285     SPSTuple<SPSExecutorAddrRange, SPSExecutorAddrRange>;
286 
287 template <>
288 class SPSSerializationTraits<SPSELFPerObjectSectionsToRegister,
289                              ELFPerObjectSectionsToRegister> {
290 
291 public:
size(const ELFPerObjectSectionsToRegister & MOPOSR)292   static size_t size(const ELFPerObjectSectionsToRegister &MOPOSR) {
293     return SPSELFPerObjectSectionsToRegister::AsArgList::size(
294         MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection);
295   }
296 
serialize(SPSOutputBuffer & OB,const ELFPerObjectSectionsToRegister & MOPOSR)297   static bool serialize(SPSOutputBuffer &OB,
298                         const ELFPerObjectSectionsToRegister &MOPOSR) {
299     return SPSELFPerObjectSectionsToRegister::AsArgList::serialize(
300         OB, MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection);
301   }
302 
deserialize(SPSInputBuffer & IB,ELFPerObjectSectionsToRegister & MOPOSR)303   static bool deserialize(SPSInputBuffer &IB,
304                           ELFPerObjectSectionsToRegister &MOPOSR) {
305     return SPSELFPerObjectSectionsToRegister::AsArgList::deserialize(
306         IB, MOPOSR.EHFrameSection, MOPOSR.ThreadDataSection);
307   }
308 };
309 
310 using SPSELFNixJITDylibDepInfoMap =
311     SPSSequence<SPSTuple<SPSExecutorAddr, SPSSequence<SPSExecutorAddr>>>;
312 
313 } // end namespace shared
314 } // end namespace orc
315 } // end namespace llvm
316 
317 #endif // LLVM_EXECUTIONENGINE_ORC_ELFNIXPLATFORM_H
318