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