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