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