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 <vector> |
25 | |
26 | namespace llvm { |
27 | namespace orc { |
28 | |
29 | struct ELFPerObjectSectionsToRegister { |
30 | ExecutorAddrRange EHFrameSection; |
31 | ExecutorAddrRange ThreadDataSection; |
32 | }; |
33 | |
34 | struct ELFNixJITDylibInitializers { |
35 | using SectionList = std::vector<ExecutorAddrRange>; |
36 | |
37 | ELFNixJITDylibInitializers(std::string Name, ExecutorAddr DSOHandleAddress) |
38 | : Name(std::move(Name)), DSOHandleAddress(std::move(DSOHandleAddress)) {} |
39 | |
40 | std::string Name; |
41 | ExecutorAddr DSOHandleAddress; |
42 | |
43 | StringMap<SectionList> InitSections; |
44 | }; |
45 | |
46 | class ELFNixJITDylibDeinitializers {}; |
47 | |
48 | using ELFNixJITDylibInitializerSequence = |
49 | std::vector<ELFNixJITDylibInitializers>; |
50 | |
51 | using ELFNixJITDylibDeinitializerSequence = |
52 | std::vector<ELFNixJITDylibDeinitializers>; |
53 | |
54 | /// Mediates between ELFNix initialization and ExecutionSession state. |
55 | class ELFNixPlatform : public Platform { |
56 | public: |
57 | /// Try to create a ELFNixPlatform instance, adding the ORC runtime to the |
58 | /// given JITDylib. |
59 | /// |
60 | /// The ORC runtime requires access to a number of symbols in |
61 | /// libc++. It is up to the caller to ensure that the required |
62 | /// symbols can be referenced by code added to PlatformJD. The |
63 | /// standard way to achieve this is to first attach dynamic library |
64 | /// search generators for either the given process, or for the |
65 | /// specific required libraries, to PlatformJD, then to create the |
66 | /// platform instance: |
67 | /// |
68 | /// \code{.cpp} |
69 | /// auto &PlatformJD = ES.createBareJITDylib("stdlib"); |
70 | /// PlatformJD.addGenerator( |
71 | /// ExitOnErr(EPCDynamicLibrarySearchGenerator |
72 | /// ::GetForTargetProcess(EPC))); |
73 | /// ES.setPlatform( |
74 | /// ExitOnErr(ELFNixPlatform::Create(ES, ObjLayer, EPC, PlatformJD, |
75 | /// "/path/to/orc/runtime"))); |
76 | /// \endcode |
77 | /// |
78 | /// Alternatively, these symbols could be added to another JITDylib that |
79 | /// PlatformJD links against. |
80 | /// |
81 | /// Clients are also responsible for ensuring that any JIT'd code that |
82 | /// depends on runtime functions (including any code using TLV or static |
83 | /// destructors) can reference the runtime symbols. This is usually achieved |
84 | /// by linking any JITDylibs containing regular code against |
85 | /// PlatformJD. |
86 | /// |
87 | /// By default, ELFNixPlatform will add the set of aliases returned by the |
88 | /// standardPlatformAliases function. This includes both required aliases |
89 | /// (e.g. __cxa_atexit -> __orc_rt_elf_cxa_atexit for static destructor |
90 | /// support), and optional aliases that provide JIT versions of common |
91 | /// functions (e.g. dlopen -> __orc_rt_elf_jit_dlopen). Clients can |
92 | /// override these defaults by passing a non-None value for the |
93 | /// RuntimeAliases function, in which case the client is responsible for |
94 | /// setting up all aliases (including the required ones). |
95 | static Expected<std::unique_ptr<ELFNixPlatform>> |
96 | Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, |
97 | JITDylib &PlatformJD, std::unique_ptr<DefinitionGenerator> OrcRuntime, |
98 | std::optional<SymbolAliasMap> RuntimeAliases = std::nullopt); |
99 | |
100 | /// Construct using a path to the ORC runtime. |
101 | static Expected<std::unique_ptr<ELFNixPlatform>> |
102 | Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, |
103 | JITDylib &PlatformJD, const char *OrcRuntimePath, |
104 | std::optional<SymbolAliasMap> RuntimeAliases = std::nullopt); |
105 | |
106 | ExecutionSession &getExecutionSession() const { return ES; } |
107 | ObjectLinkingLayer &getObjectLinkingLayer() const { return ObjLinkingLayer; } |
108 | |
109 | Error setupJITDylib(JITDylib &JD) override; |
110 | Error teardownJITDylib(JITDylib &JD) override; |
111 | Error notifyAdding(ResourceTracker &RT, |
112 | const MaterializationUnit &MU) override; |
113 | Error notifyRemoving(ResourceTracker &RT) override; |
114 | |
115 | /// Returns an AliasMap containing the default aliases for the ELFNixPlatform. |
116 | /// This can be modified by clients when constructing the platform to add |
117 | /// or remove aliases. |
118 | static Expected<SymbolAliasMap> standardPlatformAliases(ExecutionSession &ES, |
119 | JITDylib &PlatformJD); |
120 | |
121 | /// Returns the array of required CXX aliases. |
122 | static ArrayRef<std::pair<const char *, const char *>> requiredCXXAliases(); |
123 | |
124 | /// Returns the array of standard runtime utility aliases for ELF. |
125 | static ArrayRef<std::pair<const char *, const char *>> |
126 | standardRuntimeUtilityAliases(); |
127 | |
128 | private: |
129 | // The ELFNixPlatformPlugin scans/modifies LinkGraphs to support ELF |
130 | // platform features including initializers, exceptions, TLV, and language |
131 | // runtime registration. |
132 | class ELFNixPlatformPlugin : public ObjectLinkingLayer::Plugin { |
133 | public: |
134 | ELFNixPlatformPlugin(ELFNixPlatform &MP) : MP(MP) {} |
135 | |
136 | void modifyPassConfig(MaterializationResponsibility &MR, |
137 | jitlink::LinkGraph &G, |
138 | jitlink::PassConfiguration &Config) override; |
139 | |
140 | SyntheticSymbolDependenciesMap |
141 | getSyntheticSymbolDependencies(MaterializationResponsibility &MR) override; |
142 | |
143 | // FIXME: We should be tentatively tracking scraped sections and discarding |
144 | // if the MR fails. |
145 | Error notifyFailed(MaterializationResponsibility &MR) override { |
146 | return Error::success(); |
147 | } |
148 | |
149 | Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { |
150 | return Error::success(); |
151 | } |
152 | |
153 | void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, |
154 | ResourceKey SrcKey) override {} |
155 | |
156 | private: |
157 | using InitSymbolDepMap = |
158 | DenseMap<MaterializationResponsibility *, JITLinkSymbolSet>; |
159 | |
160 | void addInitializerSupportPasses(MaterializationResponsibility &MR, |
161 | jitlink::PassConfiguration &Config); |
162 | |
163 | void addDSOHandleSupportPasses(MaterializationResponsibility &MR, |
164 | jitlink::PassConfiguration &Config); |
165 | |
166 | void addEHAndTLVSupportPasses(MaterializationResponsibility &MR, |
167 | jitlink::PassConfiguration &Config); |
168 | |
169 | Error preserveInitSections(jitlink::LinkGraph &G, |
170 | MaterializationResponsibility &MR); |
171 | |
172 | Error registerInitSections(jitlink::LinkGraph &G, JITDylib &JD); |
173 | |
174 | Error fixTLVSectionsAndEdges(jitlink::LinkGraph &G, JITDylib &JD); |
175 | |
176 | std::mutex PluginMutex; |
177 | ELFNixPlatform &MP; |
178 | InitSymbolDepMap InitSymbolDeps; |
179 | }; |
180 | |
181 | using SendInitializerSequenceFn = |
182 | unique_function<void(Expected<ELFNixJITDylibInitializerSequence>)>; |
183 | |
184 | using SendDeinitializerSequenceFn = |
185 | unique_function<void(Expected<ELFNixJITDylibDeinitializerSequence>)>; |
186 | |
187 | using SendSymbolAddressFn = unique_function<void(Expected<ExecutorAddr>)>; |
188 | |
189 | static bool supportedTarget(const Triple &TT); |
190 | |
191 | ELFNixPlatform(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, |
192 | JITDylib &PlatformJD, |
193 | std::unique_ptr<DefinitionGenerator> OrcRuntimeGenerator, |
194 | Error &Err); |
195 | |
196 | // Associate ELFNixPlatform JIT-side runtime support functions with handlers. |
197 | Error associateRuntimeSupportFunctions(JITDylib &PlatformJD); |
198 | |
199 | void getInitializersBuildSequencePhase(SendInitializerSequenceFn SendResult, |
200 | JITDylib &JD, |
201 | std::vector<JITDylibSP> DFSLinkOrder); |
202 | |
203 | void getInitializersLookupPhase(SendInitializerSequenceFn SendResult, |
204 | JITDylib &JD); |
205 | |
206 | void rt_getInitializers(SendInitializerSequenceFn SendResult, |
207 | StringRef JDName); |
208 | |
209 | void rt_getDeinitializers(SendDeinitializerSequenceFn SendResult, |
210 | ExecutorAddr Handle); |
211 | |
212 | void rt_lookupSymbol(SendSymbolAddressFn SendResult, ExecutorAddr Handle, |
213 | StringRef SymbolName); |
214 | |
215 | // Records the addresses of runtime symbols used by the platform. |
216 | Error bootstrapELFNixRuntime(JITDylib &PlatformJD); |
217 | |
218 | Error registerInitInfo(JITDylib &JD, |
219 | ArrayRef<jitlink::Section *> InitSections); |
220 | |
221 | Error registerPerObjectSections(const ELFPerObjectSectionsToRegister &POSR); |
222 | |
223 | Expected<uint64_t> createPThreadKey(); |
224 | |
225 | ExecutionSession &ES; |
226 | ObjectLinkingLayer &ObjLinkingLayer; |
227 | |
228 | SymbolStringPtr DSOHandleSymbol; |
229 | std::atomic<bool> RuntimeBootstrapped{false}; |
230 | |
231 | ExecutorAddr orc_rt_elfnix_platform_bootstrap; |
232 | ExecutorAddr orc_rt_elfnix_platform_shutdown; |
233 | ExecutorAddr orc_rt_elfnix_register_object_sections; |
234 | ExecutorAddr orc_rt_elfnix_create_pthread_key; |
235 | |
236 | DenseMap<JITDylib *, SymbolLookupSet> RegisteredInitSymbols; |
237 | |
238 | // InitSeqs gets its own mutex to avoid locking the whole session when |
239 | // aggregating data from the jitlink. |
240 | std::mutex PlatformMutex; |
241 | DenseMap<JITDylib *, ELFNixJITDylibInitializers> InitSeqs; |
242 | std::vector<ELFPerObjectSectionsToRegister> BootstrapPOSRs; |
243 | |
244 | DenseMap<ExecutorAddr, JITDylib *> HandleAddrToJITDylib; |
245 | DenseMap<JITDylib *, uint64_t> JITDylibToPThreadKey; |
246 | }; |
247 | |
248 | namespace shared { |
249 | |
250 | using SPSELFPerObjectSectionsToRegister = |
251 | SPSTuple<SPSExecutorAddrRange, SPSExecutorAddrRange>; |
252 | |
253 | template <> |
254 | class SPSSerializationTraits<SPSELFPerObjectSectionsToRegister, |
255 | ELFPerObjectSectionsToRegister> { |
256 | |
257 | public: |
258 | static size_t size(const ELFPerObjectSectionsToRegister &MOPOSR) { |
259 | return SPSELFPerObjectSectionsToRegister::AsArgList::size( |
260 | Arg: MOPOSR.EHFrameSection, Args: MOPOSR.ThreadDataSection); |
261 | } |
262 | |
263 | static bool serialize(SPSOutputBuffer &OB, |
264 | const ELFPerObjectSectionsToRegister &MOPOSR) { |
265 | return SPSELFPerObjectSectionsToRegister::AsArgList::serialize( |
266 | OB, Arg: MOPOSR.EHFrameSection, Args: MOPOSR.ThreadDataSection); |
267 | } |
268 | |
269 | static bool deserialize(SPSInputBuffer &IB, |
270 | ELFPerObjectSectionsToRegister &MOPOSR) { |
271 | return SPSELFPerObjectSectionsToRegister::AsArgList::deserialize( |
272 | IB, Arg&: MOPOSR.EHFrameSection, Args&: MOPOSR.ThreadDataSection); |
273 | } |
274 | }; |
275 | |
276 | using SPSNamedExecutorAddrRangeSequenceMap = |
277 | SPSSequence<SPSTuple<SPSString, SPSExecutorAddrRangeSequence>>; |
278 | |
279 | using SPSELFNixJITDylibInitializers = |
280 | SPSTuple<SPSString, SPSExecutorAddr, SPSNamedExecutorAddrRangeSequenceMap>; |
281 | |
282 | using SPSELFNixJITDylibInitializerSequence = |
283 | SPSSequence<SPSELFNixJITDylibInitializers>; |
284 | |
285 | /// Serialization traits for ELFNixJITDylibInitializers. |
286 | template <> |
287 | class SPSSerializationTraits<SPSELFNixJITDylibInitializers, |
288 | ELFNixJITDylibInitializers> { |
289 | public: |
290 | static size_t size(const ELFNixJITDylibInitializers &MOJDIs) { |
291 | return SPSELFNixJITDylibInitializers::AsArgList::size( |
292 | Arg: MOJDIs.Name, Args: MOJDIs.DSOHandleAddress, Args: MOJDIs.InitSections); |
293 | } |
294 | |
295 | static bool serialize(SPSOutputBuffer &OB, |
296 | const ELFNixJITDylibInitializers &MOJDIs) { |
297 | return SPSELFNixJITDylibInitializers::AsArgList::serialize( |
298 | OB, Arg: MOJDIs.Name, Args: MOJDIs.DSOHandleAddress, Args: MOJDIs.InitSections); |
299 | } |
300 | |
301 | static bool deserialize(SPSInputBuffer &IB, |
302 | ELFNixJITDylibInitializers &MOJDIs) { |
303 | return SPSELFNixJITDylibInitializers::AsArgList::deserialize( |
304 | IB, Arg&: MOJDIs.Name, Args&: MOJDIs.DSOHandleAddress, Args&: MOJDIs.InitSections); |
305 | } |
306 | }; |
307 | |
308 | using SPSELFJITDylibDeinitializers = SPSEmpty; |
309 | |
310 | using SPSELFJITDylibDeinitializerSequence = |
311 | SPSSequence<SPSELFJITDylibDeinitializers>; |
312 | |
313 | template <> |
314 | class SPSSerializationTraits<SPSELFJITDylibDeinitializers, |
315 | ELFNixJITDylibDeinitializers> { |
316 | public: |
317 | static size_t size(const ELFNixJITDylibDeinitializers &MOJDDs) { return 0; } |
318 | |
319 | static bool serialize(SPSOutputBuffer &OB, |
320 | const ELFNixJITDylibDeinitializers &MOJDDs) { |
321 | return true; |
322 | } |
323 | |
324 | static bool deserialize(SPSInputBuffer &IB, |
325 | ELFNixJITDylibDeinitializers &MOJDDs) { |
326 | MOJDDs = ELFNixJITDylibDeinitializers(); |
327 | return true; |
328 | } |
329 | }; |
330 | |
331 | } // end namespace shared |
332 | } // end namespace orc |
333 | } // end namespace llvm |
334 | |
335 | #endif // LLVM_EXECUTIONENGINE_ORC_ELFNIXPLATFORM_H |
336 | |