| //===------ ELFNixPlatform.cpp - Utilities for executing MachO in Orc -----===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h" |
| |
| #include "llvm/BinaryFormat/ELF.h" |
| #include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h" |
| #include "llvm/ExecutionEngine/JITLink/aarch64.h" |
| #include "llvm/ExecutionEngine/JITLink/x86_64.h" |
| #include "llvm/ExecutionEngine/Orc/DebugUtils.h" |
| #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" |
| #include "llvm/Support/BinaryByteStream.h" |
| #include "llvm/Support/Debug.h" |
| #include <optional> |
| |
| #define DEBUG_TYPE "orc" |
| |
| using namespace llvm; |
| using namespace llvm::orc; |
| using namespace llvm::orc::shared; |
| |
| namespace { |
| |
| class DSOHandleMaterializationUnit : public MaterializationUnit { |
| public: |
| DSOHandleMaterializationUnit(ELFNixPlatform &ENP, |
| const SymbolStringPtr &DSOHandleSymbol) |
| : MaterializationUnit( |
| createDSOHandleSectionInterface(ENP, DSOHandleSymbol)), |
| ENP(ENP) {} |
| |
| StringRef getName() const override { return "DSOHandleMU"; } |
| |
| void materialize(std::unique_ptr<MaterializationResponsibility> R) override { |
| unsigned PointerSize; |
| support::endianness Endianness; |
| jitlink::Edge::Kind EdgeKind; |
| const auto &TT = |
| ENP.getExecutionSession().getExecutorProcessControl().getTargetTriple(); |
| |
| switch (TT.getArch()) { |
| case Triple::x86_64: |
| PointerSize = 8; |
| Endianness = support::endianness::little; |
| EdgeKind = jitlink::x86_64::Pointer64; |
| break; |
| case Triple::aarch64: |
| PointerSize = 8; |
| Endianness = support::endianness::little; |
| EdgeKind = jitlink::aarch64::Pointer64; |
| break; |
| default: |
| llvm_unreachable("Unrecognized architecture"); |
| } |
| |
| // void *__dso_handle = &__dso_handle; |
| auto G = std::make_unique<jitlink::LinkGraph>( |
| "<DSOHandleMU>", TT, PointerSize, Endianness, |
| jitlink::getGenericEdgeKindName); |
| auto &DSOHandleSection = |
| G->createSection(".data.__dso_handle", MemProt::Read); |
| auto &DSOHandleBlock = G->createContentBlock( |
| DSOHandleSection, getDSOHandleContent(PointerSize), orc::ExecutorAddr(), |
| 8, 0); |
| auto &DSOHandleSymbol = G->addDefinedSymbol( |
| DSOHandleBlock, 0, *R->getInitializerSymbol(), DSOHandleBlock.getSize(), |
| jitlink::Linkage::Strong, jitlink::Scope::Default, false, true); |
| DSOHandleBlock.addEdge(EdgeKind, 0, DSOHandleSymbol, 0); |
| |
| ENP.getObjectLinkingLayer().emit(std::move(R), std::move(G)); |
| } |
| |
| void discard(const JITDylib &JD, const SymbolStringPtr &Sym) override {} |
| |
| private: |
| static MaterializationUnit::Interface |
| createDSOHandleSectionInterface(ELFNixPlatform &ENP, |
| const SymbolStringPtr &DSOHandleSymbol) { |
| SymbolFlagsMap SymbolFlags; |
| SymbolFlags[DSOHandleSymbol] = JITSymbolFlags::Exported; |
| return MaterializationUnit::Interface(std::move(SymbolFlags), |
| DSOHandleSymbol); |
| } |
| |
| ArrayRef<char> getDSOHandleContent(size_t PointerSize) { |
| static const char Content[8] = {0}; |
| assert(PointerSize <= sizeof Content); |
| return {Content, PointerSize}; |
| } |
| |
| ELFNixPlatform &ENP; |
| }; |
| |
| StringRef EHFrameSectionName = ".eh_frame"; |
| StringRef InitArrayFuncSectionName = ".init_array"; |
| |
| StringRef ThreadBSSSectionName = ".tbss"; |
| StringRef ThreadDataSectionName = ".tdata"; |
| |
| } // end anonymous namespace |
| |
| namespace llvm { |
| namespace orc { |
| |
| Expected<std::unique_ptr<ELFNixPlatform>> |
| ELFNixPlatform::Create(ExecutionSession &ES, |
| ObjectLinkingLayer &ObjLinkingLayer, |
| JITDylib &PlatformJD, const char *OrcRuntimePath, |
| std::optional<SymbolAliasMap> RuntimeAliases) { |
| |
| auto &EPC = ES.getExecutorProcessControl(); |
| |
| // If the target is not supported then bail out immediately. |
| if (!supportedTarget(EPC.getTargetTriple())) |
| return make_error<StringError>("Unsupported ELFNixPlatform triple: " + |
| EPC.getTargetTriple().str(), |
| inconvertibleErrorCode()); |
| |
| // Create default aliases if the caller didn't supply any. |
| if (!RuntimeAliases) { |
| auto StandardRuntimeAliases = standardPlatformAliases(ES, PlatformJD); |
| if (!StandardRuntimeAliases) |
| return StandardRuntimeAliases.takeError(); |
| RuntimeAliases = std::move(*StandardRuntimeAliases); |
| } |
| |
| // Define the aliases. |
| if (auto Err = PlatformJD.define(symbolAliases(std::move(*RuntimeAliases)))) |
| return std::move(Err); |
| |
| // Add JIT-dispatch function support symbols. |
| if (auto Err = PlatformJD.define(absoluteSymbols( |
| {{ES.intern("__orc_rt_jit_dispatch"), |
| {EPC.getJITDispatchInfo().JITDispatchFunction.getValue(), |
| JITSymbolFlags::Exported}}, |
| {ES.intern("__orc_rt_jit_dispatch_ctx"), |
| {EPC.getJITDispatchInfo().JITDispatchContext.getValue(), |
| JITSymbolFlags::Exported}}}))) |
| return std::move(Err); |
| |
| // Create a generator for the ORC runtime archive. |
| auto OrcRuntimeArchiveGenerator = StaticLibraryDefinitionGenerator::Load( |
| ObjLinkingLayer, OrcRuntimePath, EPC.getTargetTriple()); |
| if (!OrcRuntimeArchiveGenerator) |
| return OrcRuntimeArchiveGenerator.takeError(); |
| |
| // Create the instance. |
| Error Err = Error::success(); |
| auto P = std::unique_ptr<ELFNixPlatform>( |
| new ELFNixPlatform(ES, ObjLinkingLayer, PlatformJD, |
| std::move(*OrcRuntimeArchiveGenerator), Err)); |
| if (Err) |
| return std::move(Err); |
| return std::move(P); |
| } |
| |
| Error ELFNixPlatform::setupJITDylib(JITDylib &JD) { |
| return JD.define( |
| std::make_unique<DSOHandleMaterializationUnit>(*this, DSOHandleSymbol)); |
| } |
| |
| Error ELFNixPlatform::teardownJITDylib(JITDylib &JD) { |
| return Error::success(); |
| } |
| |
| Error ELFNixPlatform::notifyAdding(ResourceTracker &RT, |
| const MaterializationUnit &MU) { |
| auto &JD = RT.getJITDylib(); |
| const auto &InitSym = MU.getInitializerSymbol(); |
| if (!InitSym) |
| return Error::success(); |
| |
| RegisteredInitSymbols[&JD].add(InitSym, |
| SymbolLookupFlags::WeaklyReferencedSymbol); |
| LLVM_DEBUG({ |
| dbgs() << "ELFNixPlatform: Registered init symbol " << *InitSym |
| << " for MU " << MU.getName() << "\n"; |
| }); |
| return Error::success(); |
| } |
| |
| Error ELFNixPlatform::notifyRemoving(ResourceTracker &RT) { |
| llvm_unreachable("Not supported yet"); |
| } |
| |
| static void addAliases(ExecutionSession &ES, SymbolAliasMap &Aliases, |
| ArrayRef<std::pair<const char *, const char *>> AL) { |
| for (auto &KV : AL) { |
| auto AliasName = ES.intern(KV.first); |
| assert(!Aliases.count(AliasName) && "Duplicate symbol name in alias map"); |
| Aliases[std::move(AliasName)] = {ES.intern(KV.second), |
| JITSymbolFlags::Exported}; |
| } |
| } |
| |
| Expected<SymbolAliasMap> |
| ELFNixPlatform::standardPlatformAliases(ExecutionSession &ES, |
| JITDylib &PlatformJD) { |
| SymbolAliasMap Aliases; |
| addAliases(ES, Aliases, requiredCXXAliases()); |
| addAliases(ES, Aliases, standardRuntimeUtilityAliases()); |
| |
| // Determine whether or not the libunwind extended-API function for |
| // dynamically registering an entire .eh_frame section is available. |
| // If it is not, we assume that libgcc_s is being used, and alias to |
| // its __register_frame with the same functionality. |
| auto RTRegisterFrame = ES.intern("__orc_rt_register_eh_frame_section"); |
| auto LibUnwindRegisterFrame = ES.intern("__unw_add_dynamic_eh_frame_section"); |
| auto RTDeregisterFrame = ES.intern("__orc_rt_deregister_eh_frame_section"); |
| auto LibUnwindDeregisterFrame = |
| ES.intern("__unw_remove_dynamic_eh_frame_section"); |
| auto SM = ES.lookup(makeJITDylibSearchOrder(&PlatformJD), |
| SymbolLookupSet() |
| .add(LibUnwindRegisterFrame, |
| SymbolLookupFlags::WeaklyReferencedSymbol) |
| .add(LibUnwindDeregisterFrame, |
| SymbolLookupFlags::WeaklyReferencedSymbol)); |
| if (!SM) { // Weak-ref means no "missing symbol" errors, so this must be |
| // something more serious that we should report. |
| return SM.takeError(); |
| } else if (SM->size() == 2) { |
| LLVM_DEBUG({ |
| dbgs() << "Using libunwind " << LibUnwindRegisterFrame |
| << " for unwind info registration\n"; |
| }); |
| Aliases[std::move(RTRegisterFrame)] = {LibUnwindRegisterFrame, |
| JITSymbolFlags::Exported}; |
| Aliases[std::move(RTDeregisterFrame)] = {LibUnwindDeregisterFrame, |
| JITSymbolFlags::Exported}; |
| } else { |
| // Since LLVM libunwind is not present, we assume that unwinding |
| // is provided by libgcc |
| LLVM_DEBUG({ |
| dbgs() << "Using libgcc __register_frame" |
| << " for unwind info registration\n"; |
| }); |
| Aliases[std::move(RTRegisterFrame)] = {ES.intern("__register_frame"), |
| JITSymbolFlags::Exported}; |
| Aliases[std::move(RTDeregisterFrame)] = {ES.intern("__deregister_frame"), |
| JITSymbolFlags::Exported}; |
| } |
| |
| return Aliases; |
| } |
| |
| ArrayRef<std::pair<const char *, const char *>> |
| ELFNixPlatform::requiredCXXAliases() { |
| static const std::pair<const char *, const char *> RequiredCXXAliases[] = { |
| {"__cxa_atexit", "__orc_rt_elfnix_cxa_atexit"}, |
| {"atexit", "__orc_rt_elfnix_atexit"}}; |
| |
| return ArrayRef<std::pair<const char *, const char *>>(RequiredCXXAliases); |
| } |
| |
| ArrayRef<std::pair<const char *, const char *>> |
| ELFNixPlatform::standardRuntimeUtilityAliases() { |
| static const std::pair<const char *, const char *> |
| StandardRuntimeUtilityAliases[] = { |
| {"__orc_rt_run_program", "__orc_rt_elfnix_run_program"}, |
| {"__orc_rt_jit_dlerror", "__orc_rt_elfnix_jit_dlerror"}, |
| {"__orc_rt_jit_dlopen", "__orc_rt_elfnix_jit_dlopen"}, |
| {"__orc_rt_jit_dlclose", "__orc_rt_elfnix_jit_dlclose"}, |
| {"__orc_rt_jit_dlsym", "__orc_rt_elfnix_jit_dlsym"}, |
| {"__orc_rt_log_error", "__orc_rt_log_error_to_stderr"}}; |
| |
| return ArrayRef<std::pair<const char *, const char *>>( |
| StandardRuntimeUtilityAliases); |
| } |
| |
| bool ELFNixPlatform::isInitializerSection(StringRef SecName) { |
| if (SecName.consume_front(InitArrayFuncSectionName) && |
| (SecName.empty() || SecName[0] == '.')) |
| return true; |
| return false; |
| } |
| |
| bool ELFNixPlatform::supportedTarget(const Triple &TT) { |
| switch (TT.getArch()) { |
| case Triple::x86_64: |
| case Triple::aarch64: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| ELFNixPlatform::ELFNixPlatform( |
| ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, |
| JITDylib &PlatformJD, |
| std::unique_ptr<DefinitionGenerator> OrcRuntimeGenerator, Error &Err) |
| : ES(ES), ObjLinkingLayer(ObjLinkingLayer), |
| DSOHandleSymbol(ES.intern("__dso_handle")) { |
| ErrorAsOutParameter _(&Err); |
| |
| ObjLinkingLayer.addPlugin(std::make_unique<ELFNixPlatformPlugin>(*this)); |
| |
| PlatformJD.addGenerator(std::move(OrcRuntimeGenerator)); |
| |
| // PlatformJD hasn't been 'set-up' by the platform yet (since we're creating |
| // the platform now), so set it up. |
| if (auto E2 = setupJITDylib(PlatformJD)) { |
| Err = std::move(E2); |
| return; |
| } |
| |
| RegisteredInitSymbols[&PlatformJD].add( |
| DSOHandleSymbol, SymbolLookupFlags::WeaklyReferencedSymbol); |
| |
| // Associate wrapper function tags with JIT-side function implementations. |
| if (auto E2 = associateRuntimeSupportFunctions(PlatformJD)) { |
| Err = std::move(E2); |
| return; |
| } |
| |
| // Lookup addresses of runtime functions callable by the platform, |
| // call the platform bootstrap function to initialize the platform-state |
| // object in the executor. |
| if (auto E2 = bootstrapELFNixRuntime(PlatformJD)) { |
| Err = std::move(E2); |
| return; |
| } |
| } |
| |
| Error ELFNixPlatform::associateRuntimeSupportFunctions(JITDylib &PlatformJD) { |
| ExecutionSession::JITDispatchHandlerAssociationMap WFs; |
| |
| using GetInitializersSPSSig = |
| SPSExpected<SPSELFNixJITDylibInitializerSequence>(SPSString); |
| WFs[ES.intern("__orc_rt_elfnix_get_initializers_tag")] = |
| ES.wrapAsyncWithSPS<GetInitializersSPSSig>( |
| this, &ELFNixPlatform::rt_getInitializers); |
| |
| using GetDeinitializersSPSSig = |
| SPSExpected<SPSELFJITDylibDeinitializerSequence>(SPSExecutorAddr); |
| WFs[ES.intern("__orc_rt_elfnix_get_deinitializers_tag")] = |
| ES.wrapAsyncWithSPS<GetDeinitializersSPSSig>( |
| this, &ELFNixPlatform::rt_getDeinitializers); |
| |
| using LookupSymbolSPSSig = |
| SPSExpected<SPSExecutorAddr>(SPSExecutorAddr, SPSString); |
| WFs[ES.intern("__orc_rt_elfnix_symbol_lookup_tag")] = |
| ES.wrapAsyncWithSPS<LookupSymbolSPSSig>(this, |
| &ELFNixPlatform::rt_lookupSymbol); |
| |
| return ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs)); |
| } |
| |
| void ELFNixPlatform::getInitializersBuildSequencePhase( |
| SendInitializerSequenceFn SendResult, JITDylib &JD, |
| std::vector<JITDylibSP> DFSLinkOrder) { |
| ELFNixJITDylibInitializerSequence FullInitSeq; |
| { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| for (auto &InitJD : reverse(DFSLinkOrder)) { |
| LLVM_DEBUG({ |
| dbgs() << "ELFNixPlatform: Appending inits for \"" << InitJD->getName() |
| << "\" to sequence\n"; |
| }); |
| auto ISItr = InitSeqs.find(InitJD.get()); |
| if (ISItr != InitSeqs.end()) { |
| FullInitSeq.emplace_back(std::move(ISItr->second)); |
| InitSeqs.erase(ISItr); |
| } |
| } |
| } |
| |
| SendResult(std::move(FullInitSeq)); |
| } |
| |
| void ELFNixPlatform::getInitializersLookupPhase( |
| SendInitializerSequenceFn SendResult, JITDylib &JD) { |
| |
| auto DFSLinkOrder = JD.getDFSLinkOrder(); |
| if (!DFSLinkOrder) { |
| SendResult(DFSLinkOrder.takeError()); |
| return; |
| } |
| |
| DenseMap<JITDylib *, SymbolLookupSet> NewInitSymbols; |
| ES.runSessionLocked([&]() { |
| for (auto &InitJD : *DFSLinkOrder) { |
| auto RISItr = RegisteredInitSymbols.find(InitJD.get()); |
| if (RISItr != RegisteredInitSymbols.end()) { |
| NewInitSymbols[InitJD.get()] = std::move(RISItr->second); |
| RegisteredInitSymbols.erase(RISItr); |
| } |
| } |
| }); |
| |
| // If there are no further init symbols to look up then move on to the next |
| // phase. |
| if (NewInitSymbols.empty()) { |
| getInitializersBuildSequencePhase(std::move(SendResult), JD, |
| std::move(*DFSLinkOrder)); |
| return; |
| } |
| |
| // Otherwise issue a lookup and re-run this phase when it completes. |
| lookupInitSymbolsAsync( |
| [this, SendResult = std::move(SendResult), &JD](Error Err) mutable { |
| if (Err) |
| SendResult(std::move(Err)); |
| else |
| getInitializersLookupPhase(std::move(SendResult), JD); |
| }, |
| ES, std::move(NewInitSymbols)); |
| } |
| |
| void ELFNixPlatform::rt_getInitializers(SendInitializerSequenceFn SendResult, |
| StringRef JDName) { |
| LLVM_DEBUG({ |
| dbgs() << "ELFNixPlatform::rt_getInitializers(\"" << JDName << "\")\n"; |
| }); |
| |
| JITDylib *JD = ES.getJITDylibByName(JDName); |
| if (!JD) { |
| LLVM_DEBUG({ |
| dbgs() << " No such JITDylib \"" << JDName << "\". Sending error.\n"; |
| }); |
| SendResult(make_error<StringError>("No JITDylib named " + JDName, |
| inconvertibleErrorCode())); |
| return; |
| } |
| |
| getInitializersLookupPhase(std::move(SendResult), *JD); |
| } |
| |
| void ELFNixPlatform::rt_getDeinitializers( |
| SendDeinitializerSequenceFn SendResult, ExecutorAddr Handle) { |
| LLVM_DEBUG({ |
| dbgs() << "ELFNixPlatform::rt_getDeinitializers(\"" |
| << formatv("{0:x}", Handle.getValue()) << "\")\n"; |
| }); |
| |
| JITDylib *JD = nullptr; |
| |
| { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| auto I = HandleAddrToJITDylib.find(Handle); |
| if (I != HandleAddrToJITDylib.end()) |
| JD = I->second; |
| } |
| |
| if (!JD) { |
| LLVM_DEBUG({ |
| dbgs() << " No JITDylib for handle " |
| << formatv("{0:x}", Handle.getValue()) << "\n"; |
| }); |
| SendResult(make_error<StringError>("No JITDylib associated with handle " + |
| formatv("{0:x}", Handle.getValue()), |
| inconvertibleErrorCode())); |
| return; |
| } |
| |
| SendResult(ELFNixJITDylibDeinitializerSequence()); |
| } |
| |
| void ELFNixPlatform::rt_lookupSymbol(SendSymbolAddressFn SendResult, |
| ExecutorAddr Handle, |
| StringRef SymbolName) { |
| LLVM_DEBUG({ |
| dbgs() << "ELFNixPlatform::rt_lookupSymbol(\"" |
| << formatv("{0:x}", Handle.getValue()) << "\")\n"; |
| }); |
| |
| JITDylib *JD = nullptr; |
| |
| { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| auto I = HandleAddrToJITDylib.find(Handle); |
| if (I != HandleAddrToJITDylib.end()) |
| JD = I->second; |
| } |
| |
| if (!JD) { |
| LLVM_DEBUG({ |
| dbgs() << " No JITDylib for handle " |
| << formatv("{0:x}", Handle.getValue()) << "\n"; |
| }); |
| SendResult(make_error<StringError>("No JITDylib associated with handle " + |
| formatv("{0:x}", Handle.getValue()), |
| inconvertibleErrorCode())); |
| return; |
| } |
| |
| // Use functor class to work around XL build compiler issue on AIX. |
| class RtLookupNotifyComplete { |
| public: |
| RtLookupNotifyComplete(SendSymbolAddressFn &&SendResult) |
| : SendResult(std::move(SendResult)) {} |
| void operator()(Expected<SymbolMap> Result) { |
| if (Result) { |
| assert(Result->size() == 1 && "Unexpected result map count"); |
| SendResult(ExecutorAddr(Result->begin()->second.getAddress())); |
| } else { |
| SendResult(Result.takeError()); |
| } |
| } |
| |
| private: |
| SendSymbolAddressFn SendResult; |
| }; |
| |
| ES.lookup( |
| LookupKind::DLSym, {{JD, JITDylibLookupFlags::MatchExportedSymbolsOnly}}, |
| SymbolLookupSet(ES.intern(SymbolName)), SymbolState::Ready, |
| RtLookupNotifyComplete(std::move(SendResult)), NoDependenciesToRegister); |
| } |
| |
| Error ELFNixPlatform::bootstrapELFNixRuntime(JITDylib &PlatformJD) { |
| |
| std::pair<const char *, ExecutorAddr *> Symbols[] = { |
| {"__orc_rt_elfnix_platform_bootstrap", &orc_rt_elfnix_platform_bootstrap}, |
| {"__orc_rt_elfnix_platform_shutdown", &orc_rt_elfnix_platform_shutdown}, |
| {"__orc_rt_elfnix_register_object_sections", |
| &orc_rt_elfnix_register_object_sections}, |
| {"__orc_rt_elfnix_create_pthread_key", |
| &orc_rt_elfnix_create_pthread_key}}; |
| |
| SymbolLookupSet RuntimeSymbols; |
| std::vector<std::pair<SymbolStringPtr, ExecutorAddr *>> AddrsToRecord; |
| for (const auto &KV : Symbols) { |
| auto Name = ES.intern(KV.first); |
| RuntimeSymbols.add(Name); |
| AddrsToRecord.push_back({std::move(Name), KV.second}); |
| } |
| |
| auto RuntimeSymbolAddrs = ES.lookup( |
| {{&PlatformJD, JITDylibLookupFlags::MatchAllSymbols}}, RuntimeSymbols); |
| if (!RuntimeSymbolAddrs) |
| return RuntimeSymbolAddrs.takeError(); |
| |
| for (const auto &KV : AddrsToRecord) { |
| auto &Name = KV.first; |
| assert(RuntimeSymbolAddrs->count(Name) && "Missing runtime symbol?"); |
| KV.second->setValue((*RuntimeSymbolAddrs)[Name].getAddress()); |
| } |
| |
| auto PJDDSOHandle = ES.lookup( |
| {{&PlatformJD, JITDylibLookupFlags::MatchAllSymbols}}, DSOHandleSymbol); |
| if (!PJDDSOHandle) |
| return PJDDSOHandle.takeError(); |
| |
| if (auto Err = ES.callSPSWrapper<void(uint64_t)>( |
| orc_rt_elfnix_platform_bootstrap, PJDDSOHandle->getAddress())) |
| return Err; |
| |
| // FIXME: Ordering is fuzzy here. We're probably best off saying |
| // "behavior is undefined if code that uses the runtime is added before |
| // the platform constructor returns", then move all this to the constructor. |
| RuntimeBootstrapped = true; |
| std::vector<ELFPerObjectSectionsToRegister> DeferredPOSRs; |
| { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| DeferredPOSRs = std::move(BootstrapPOSRs); |
| } |
| |
| for (auto &D : DeferredPOSRs) |
| if (auto Err = registerPerObjectSections(D)) |
| return Err; |
| |
| return Error::success(); |
| } |
| |
| Error ELFNixPlatform::registerInitInfo( |
| JITDylib &JD, ArrayRef<jitlink::Section *> InitSections) { |
| |
| std::unique_lock<std::mutex> Lock(PlatformMutex); |
| |
| ELFNixJITDylibInitializers *InitSeq = nullptr; |
| { |
| auto I = InitSeqs.find(&JD); |
| if (I == InitSeqs.end()) { |
| // If there's no init sequence entry yet then we need to look up the |
| // header symbol to force creation of one. |
| Lock.unlock(); |
| |
| auto SearchOrder = |
| JD.withLinkOrderDo([](const JITDylibSearchOrder &SO) { return SO; }); |
| if (auto Err = ES.lookup(SearchOrder, DSOHandleSymbol).takeError()) |
| return Err; |
| |
| Lock.lock(); |
| I = InitSeqs.find(&JD); |
| assert(I != InitSeqs.end() && |
| "Entry missing after header symbol lookup?"); |
| } |
| InitSeq = &I->second; |
| } |
| |
| for (auto *Sec : InitSections) { |
| // FIXME: Avoid copy here. |
| jitlink::SectionRange R(*Sec); |
| InitSeq->InitSections[Sec->getName()].push_back( |
| {ExecutorAddr(R.getStart()), ExecutorAddr(R.getEnd())}); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error ELFNixPlatform::registerPerObjectSections( |
| const ELFPerObjectSectionsToRegister &POSR) { |
| |
| if (!orc_rt_elfnix_register_object_sections) |
| return make_error<StringError>("Attempting to register per-object " |
| "sections, but runtime support has not " |
| "been loaded yet", |
| inconvertibleErrorCode()); |
| |
| Error ErrResult = Error::success(); |
| if (auto Err = ES.callSPSWrapper<shared::SPSError( |
| SPSELFPerObjectSectionsToRegister)>( |
| orc_rt_elfnix_register_object_sections, ErrResult, POSR)) |
| return Err; |
| return ErrResult; |
| } |
| |
| Expected<uint64_t> ELFNixPlatform::createPThreadKey() { |
| if (!orc_rt_elfnix_create_pthread_key) |
| return make_error<StringError>( |
| "Attempting to create pthread key in target, but runtime support has " |
| "not been loaded yet", |
| inconvertibleErrorCode()); |
| |
| Expected<uint64_t> Result(0); |
| if (auto Err = ES.callSPSWrapper<SPSExpected<uint64_t>(void)>( |
| orc_rt_elfnix_create_pthread_key, Result)) |
| return std::move(Err); |
| return Result; |
| } |
| |
| void ELFNixPlatform::ELFNixPlatformPlugin::modifyPassConfig( |
| MaterializationResponsibility &MR, jitlink::LinkGraph &LG, |
| jitlink::PassConfiguration &Config) { |
| |
| // If the initializer symbol is the __dso_handle symbol then just add |
| // the DSO handle support passes. |
| if (MR.getInitializerSymbol() == MP.DSOHandleSymbol) { |
| addDSOHandleSupportPasses(MR, Config); |
| // The DSOHandle materialization unit doesn't require any other |
| // support, so we can bail out early. |
| return; |
| } |
| |
| // If the object contains initializers then add passes to record them. |
| if (MR.getInitializerSymbol()) |
| addInitializerSupportPasses(MR, Config); |
| |
| // Add passes for eh-frame and TLV support. |
| addEHAndTLVSupportPasses(MR, Config); |
| } |
| |
| ObjectLinkingLayer::Plugin::SyntheticSymbolDependenciesMap |
| ELFNixPlatform::ELFNixPlatformPlugin::getSyntheticSymbolDependencies( |
| MaterializationResponsibility &MR) { |
| std::lock_guard<std::mutex> Lock(PluginMutex); |
| auto I = InitSymbolDeps.find(&MR); |
| if (I != InitSymbolDeps.end()) { |
| SyntheticSymbolDependenciesMap Result; |
| Result[MR.getInitializerSymbol()] = std::move(I->second); |
| InitSymbolDeps.erase(&MR); |
| return Result; |
| } |
| return SyntheticSymbolDependenciesMap(); |
| } |
| |
| void ELFNixPlatform::ELFNixPlatformPlugin::addInitializerSupportPasses( |
| MaterializationResponsibility &MR, jitlink::PassConfiguration &Config) { |
| |
| /// Preserve init sections. |
| Config.PrePrunePasses.push_back([this, &MR](jitlink::LinkGraph &G) -> Error { |
| if (auto Err = preserveInitSections(G, MR)) |
| return Err; |
| return Error::success(); |
| }); |
| |
| Config.PostFixupPasses.push_back( |
| [this, &JD = MR.getTargetJITDylib()](jitlink::LinkGraph &G) { |
| return registerInitSections(G, JD); |
| }); |
| } |
| |
| void ELFNixPlatform::ELFNixPlatformPlugin::addDSOHandleSupportPasses( |
| MaterializationResponsibility &MR, jitlink::PassConfiguration &Config) { |
| |
| Config.PostAllocationPasses.push_back([this, &JD = MR.getTargetJITDylib()]( |
| jitlink::LinkGraph &G) -> Error { |
| auto I = llvm::find_if(G.defined_symbols(), [this](jitlink::Symbol *Sym) { |
| return Sym->getName() == *MP.DSOHandleSymbol; |
| }); |
| assert(I != G.defined_symbols().end() && "Missing DSO handle symbol"); |
| { |
| std::lock_guard<std::mutex> Lock(MP.PlatformMutex); |
| auto HandleAddr = (*I)->getAddress(); |
| MP.HandleAddrToJITDylib[HandleAddr] = &JD; |
| assert(!MP.InitSeqs.count(&JD) && "InitSeq entry for JD already exists"); |
| MP.InitSeqs.insert(std::make_pair( |
| &JD, ELFNixJITDylibInitializers(JD.getName(), HandleAddr))); |
| } |
| return Error::success(); |
| }); |
| } |
| |
| void ELFNixPlatform::ELFNixPlatformPlugin::addEHAndTLVSupportPasses( |
| MaterializationResponsibility &MR, jitlink::PassConfiguration &Config) { |
| |
| // Insert TLV lowering at the start of the PostPrunePasses, since we want |
| // it to run before GOT/PLT lowering. |
| |
| // TODO: Check that before the fixTLVSectionsAndEdges pass, the GOT/PLT build |
| // pass has done. Because the TLS descriptor need to be allocate in GOT. |
| Config.PostPrunePasses.push_back( |
| [this, &JD = MR.getTargetJITDylib()](jitlink::LinkGraph &G) { |
| return fixTLVSectionsAndEdges(G, JD); |
| }); |
| |
| // Add a pass to register the final addresses of the eh-frame and TLV sections |
| // with the runtime. |
| Config.PostFixupPasses.push_back([this](jitlink::LinkGraph &G) -> Error { |
| ELFPerObjectSectionsToRegister POSR; |
| |
| if (auto *EHFrameSection = G.findSectionByName(EHFrameSectionName)) { |
| jitlink::SectionRange R(*EHFrameSection); |
| if (!R.empty()) |
| POSR.EHFrameSection = {ExecutorAddr(R.getStart()), |
| ExecutorAddr(R.getEnd())}; |
| } |
| |
| // Get a pointer to the thread data section if there is one. It will be used |
| // below. |
| jitlink::Section *ThreadDataSection = |
| G.findSectionByName(ThreadDataSectionName); |
| |
| // Handle thread BSS section if there is one. |
| if (auto *ThreadBSSSection = G.findSectionByName(ThreadBSSSectionName)) { |
| // If there's already a thread data section in this graph then merge the |
| // thread BSS section content into it, otherwise just treat the thread |
| // BSS section as the thread data section. |
| if (ThreadDataSection) |
| G.mergeSections(*ThreadDataSection, *ThreadBSSSection); |
| else |
| ThreadDataSection = ThreadBSSSection; |
| } |
| |
| // Having merged thread BSS (if present) and thread data (if present), |
| // record the resulting section range. |
| if (ThreadDataSection) { |
| jitlink::SectionRange R(*ThreadDataSection); |
| if (!R.empty()) |
| POSR.ThreadDataSection = {ExecutorAddr(R.getStart()), |
| ExecutorAddr(R.getEnd())}; |
| } |
| |
| if (POSR.EHFrameSection.Start || POSR.ThreadDataSection.Start) { |
| |
| // If we're still bootstrapping the runtime then just record this |
| // frame for now. |
| if (!MP.RuntimeBootstrapped) { |
| std::lock_guard<std::mutex> Lock(MP.PlatformMutex); |
| MP.BootstrapPOSRs.push_back(POSR); |
| return Error::success(); |
| } |
| |
| // Otherwise register it immediately. |
| if (auto Err = MP.registerPerObjectSections(POSR)) |
| return Err; |
| } |
| |
| return Error::success(); |
| }); |
| } |
| |
| Error ELFNixPlatform::ELFNixPlatformPlugin::preserveInitSections( |
| jitlink::LinkGraph &G, MaterializationResponsibility &MR) { |
| |
| JITLinkSymbolSet InitSectionSymbols; |
| for (auto &InitSection : G.sections()) { |
| // Skip non-init sections. |
| if (!isInitializerSection(InitSection.getName())) |
| continue; |
| |
| // Make a pass over live symbols in the section: those blocks are already |
| // preserved. |
| DenseSet<jitlink::Block *> AlreadyLiveBlocks; |
| for (auto &Sym : InitSection.symbols()) { |
| auto &B = Sym->getBlock(); |
| if (Sym->isLive() && Sym->getOffset() == 0 && |
| Sym->getSize() == B.getSize() && !AlreadyLiveBlocks.count(&B)) { |
| InitSectionSymbols.insert(Sym); |
| AlreadyLiveBlocks.insert(&B); |
| } |
| } |
| |
| // Add anonymous symbols to preserve any not-already-preserved blocks. |
| for (auto *B : InitSection.blocks()) |
| if (!AlreadyLiveBlocks.count(B)) |
| InitSectionSymbols.insert( |
| &G.addAnonymousSymbol(*B, 0, B->getSize(), false, true)); |
| } |
| |
| if (!InitSectionSymbols.empty()) { |
| std::lock_guard<std::mutex> Lock(PluginMutex); |
| InitSymbolDeps[&MR] = std::move(InitSectionSymbols); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error ELFNixPlatform::ELFNixPlatformPlugin::registerInitSections( |
| jitlink::LinkGraph &G, JITDylib &JD) { |
| |
| SmallVector<jitlink::Section *> InitSections; |
| |
| LLVM_DEBUG({ dbgs() << "ELFNixPlatform::registerInitSections\n"; }); |
| |
| for (auto &Sec : G.sections()) { |
| if (isInitializerSection(Sec.getName())) { |
| InitSections.push_back(&Sec); |
| } |
| } |
| |
| // Dump the scraped inits. |
| LLVM_DEBUG({ |
| dbgs() << "ELFNixPlatform: Scraped " << G.getName() << " init sections:\n"; |
| for (auto *Sec : InitSections) { |
| jitlink::SectionRange R(*Sec); |
| dbgs() << " " << Sec->getName() << ": " |
| << formatv("[ {0:x} -- {1:x} ]", R.getStart(), R.getEnd()) << "\n"; |
| } |
| }); |
| |
| return MP.registerInitInfo(JD, InitSections); |
| } |
| |
| Error ELFNixPlatform::ELFNixPlatformPlugin::fixTLVSectionsAndEdges( |
| jitlink::LinkGraph &G, JITDylib &JD) { |
| |
| for (auto *Sym : G.external_symbols()) { |
| if (Sym->getName() == "__tls_get_addr") { |
| Sym->setName("___orc_rt_elfnix_tls_get_addr"); |
| } else if (Sym->getName() == "__tlsdesc_resolver") { |
| Sym->setName("___orc_rt_elfnix_tlsdesc_resolver"); |
| } |
| } |
| |
| auto *TLSInfoEntrySection = G.findSectionByName("$__TLSINFO"); |
| |
| if (TLSInfoEntrySection) { |
| std::optional<uint64_t> Key; |
| { |
| std::lock_guard<std::mutex> Lock(MP.PlatformMutex); |
| auto I = MP.JITDylibToPThreadKey.find(&JD); |
| if (I != MP.JITDylibToPThreadKey.end()) |
| Key = I->second; |
| } |
| if (!Key) { |
| if (auto KeyOrErr = MP.createPThreadKey()) |
| Key = *KeyOrErr; |
| else |
| return KeyOrErr.takeError(); |
| } |
| |
| uint64_t PlatformKeyBits = |
| support::endian::byte_swap(*Key, G.getEndianness()); |
| |
| for (auto *B : TLSInfoEntrySection->blocks()) { |
| // FIXME: The TLS descriptor byte length may different with different |
| // ISA |
| assert(B->getSize() == (G.getPointerSize() * 2) && |
| "TLS descriptor must be 2 words length"); |
| auto TLSInfoEntryContent = B->getMutableContent(G); |
| memcpy(TLSInfoEntryContent.data(), &PlatformKeyBits, G.getPointerSize()); |
| } |
| } |
| |
| return Error::success(); |
| } |
| |
| } // End namespace orc. |
| } // End namespace llvm. |