| //===------ 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. |