| //===------- COFFPlatform.cpp - Utilities for executing COFF 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/COFFPlatform.h" |
| #include "llvm/ExecutionEngine/Orc/DebugUtils.h" |
| #include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h" |
| #include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h" |
| |
| #include "llvm/Object/COFF.h" |
| |
| #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" |
| |
| #include "llvm/ExecutionEngine/JITLink/x86_64.h" |
| |
| #define DEBUG_TYPE "orc" |
| |
| using namespace llvm; |
| using namespace llvm::orc; |
| using namespace llvm::orc::shared; |
| |
| namespace llvm { |
| namespace orc { |
| namespace shared { |
| |
| using SPSCOFFJITDylibDepInfo = SPSSequence<SPSExecutorAddr>; |
| using SPSCOFFJITDylibDepInfoMap = |
| SPSSequence<SPSTuple<SPSExecutorAddr, SPSCOFFJITDylibDepInfo>>; |
| using SPSCOFFObjectSectionsMap = |
| SPSSequence<SPSTuple<SPSString, SPSExecutorAddrRange>>; |
| using SPSCOFFRegisterObjectSectionsArgs = |
| SPSArgList<SPSExecutorAddr, SPSCOFFObjectSectionsMap, bool>; |
| using SPSCOFFDeregisterObjectSectionsArgs = |
| SPSArgList<SPSExecutorAddr, SPSCOFFObjectSectionsMap>; |
| |
| } // namespace shared |
| } // namespace orc |
| } // namespace llvm |
| namespace { |
| |
| class COFFHeaderMaterializationUnit : public MaterializationUnit { |
| public: |
| COFFHeaderMaterializationUnit(COFFPlatform &CP, |
| const SymbolStringPtr &HeaderStartSymbol) |
| : MaterializationUnit(createHeaderInterface(CP, HeaderStartSymbol)), |
| CP(CP) {} |
| |
| StringRef getName() const override { return "COFFHeaderMU"; } |
| |
| void materialize(std::unique_ptr<MaterializationResponsibility> R) override { |
| unsigned PointerSize; |
| support::endianness Endianness; |
| const auto &TT = |
| CP.getExecutionSession().getExecutorProcessControl().getTargetTriple(); |
| |
| switch (TT.getArch()) { |
| case Triple::x86_64: |
| PointerSize = 8; |
| Endianness = support::endianness::little; |
| break; |
| default: |
| llvm_unreachable("Unrecognized architecture"); |
| } |
| |
| auto G = std::make_unique<jitlink::LinkGraph>( |
| "<COFFHeaderMU>", TT, PointerSize, Endianness, |
| jitlink::getGenericEdgeKindName); |
| auto &HeaderSection = G->createSection("__header", MemProt::Read); |
| auto &HeaderBlock = createHeaderBlock(*G, HeaderSection); |
| |
| // Init symbol is __ImageBase symbol. |
| auto &ImageBaseSymbol = G->addDefinedSymbol( |
| HeaderBlock, 0, *R->getInitializerSymbol(), HeaderBlock.getSize(), |
| jitlink::Linkage::Strong, jitlink::Scope::Default, false, true); |
| |
| addImageBaseRelocationEdge(HeaderBlock, ImageBaseSymbol); |
| |
| CP.getObjectLinkingLayer().emit(std::move(R), std::move(G)); |
| } |
| |
| void discard(const JITDylib &JD, const SymbolStringPtr &Sym) override {} |
| |
| private: |
| struct HeaderSymbol { |
| const char *Name; |
| uint64_t Offset; |
| }; |
| |
| struct NTHeader { |
| support::ulittle32_t PEMagic; |
| object::coff_file_header FileHeader; |
| struct PEHeader { |
| object::pe32plus_header Header; |
| object::data_directory DataDirectory[COFF::NUM_DATA_DIRECTORIES + 1]; |
| } OptionalHeader; |
| }; |
| |
| struct HeaderBlockContent { |
| object::dos_header DOSHeader; |
| COFFHeaderMaterializationUnit::NTHeader NTHeader; |
| }; |
| |
| static jitlink::Block &createHeaderBlock(jitlink::LinkGraph &G, |
| jitlink::Section &HeaderSection) { |
| HeaderBlockContent Hdr = {}; |
| |
| // Set up magic |
| Hdr.DOSHeader.Magic[0] = 'M'; |
| Hdr.DOSHeader.Magic[1] = 'Z'; |
| Hdr.DOSHeader.AddressOfNewExeHeader = |
| offsetof(HeaderBlockContent, NTHeader); |
| uint32_t PEMagic = *reinterpret_cast<const uint32_t *>(COFF::PEMagic); |
| Hdr.NTHeader.PEMagic = PEMagic; |
| Hdr.NTHeader.OptionalHeader.Header.Magic = COFF::PE32Header::PE32_PLUS; |
| |
| switch (G.getTargetTriple().getArch()) { |
| case Triple::x86_64: |
| Hdr.NTHeader.FileHeader.Machine = COFF::IMAGE_FILE_MACHINE_AMD64; |
| break; |
| default: |
| llvm_unreachable("Unrecognized architecture"); |
| } |
| |
| auto HeaderContent = G.allocateString( |
| StringRef(reinterpret_cast<const char *>(&Hdr), sizeof(Hdr))); |
| |
| return G.createContentBlock(HeaderSection, HeaderContent, ExecutorAddr(), 8, |
| 0); |
| } |
| |
| static void addImageBaseRelocationEdge(jitlink::Block &B, |
| jitlink::Symbol &ImageBase) { |
| auto ImageBaseOffset = offsetof(HeaderBlockContent, NTHeader) + |
| offsetof(NTHeader, OptionalHeader) + |
| offsetof(object::pe32plus_header, ImageBase); |
| B.addEdge(jitlink::x86_64::Pointer64, ImageBaseOffset, ImageBase, 0); |
| } |
| |
| static MaterializationUnit::Interface |
| createHeaderInterface(COFFPlatform &MOP, |
| const SymbolStringPtr &HeaderStartSymbol) { |
| SymbolFlagsMap HeaderSymbolFlags; |
| |
| HeaderSymbolFlags[HeaderStartSymbol] = JITSymbolFlags::Exported; |
| |
| return MaterializationUnit::Interface(std::move(HeaderSymbolFlags), |
| HeaderStartSymbol); |
| } |
| |
| COFFPlatform &CP; |
| }; |
| |
| } // end anonymous namespace |
| |
| namespace llvm { |
| namespace orc { |
| |
| Expected<std::unique_ptr<COFFPlatform>> |
| COFFPlatform::Create(ExecutionSession &ES, ObjectLinkingLayer &ObjLinkingLayer, |
| JITDylib &PlatformJD, const char *OrcRuntimePath, |
| LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime, |
| const char *VCRuntimePath, |
| 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 COFFPlatform triple: " + |
| EPC.getTargetTriple().str(), |
| inconvertibleErrorCode()); |
| |
| // Create default aliases if the caller didn't supply any. |
| if (!RuntimeAliases) |
| RuntimeAliases = standardPlatformAliases(ES); |
| |
| // Define the aliases. |
| if (auto Err = PlatformJD.define(symbolAliases(std::move(*RuntimeAliases)))) |
| return std::move(Err); |
| |
| auto &HostFuncJD = ES.createBareJITDylib("$<PlatformRuntimeHostFuncJD>"); |
| |
| // Add JIT-dispatch function support symbols. |
| if (auto Err = HostFuncJD.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); |
| |
| PlatformJD.addToLinkOrder(HostFuncJD); |
| |
| // Create the instance. |
| Error Err = Error::success(); |
| auto P = std::unique_ptr<COFFPlatform>(new COFFPlatform( |
| ES, ObjLinkingLayer, PlatformJD, OrcRuntimePath, |
| std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath, Err)); |
| if (Err) |
| return std::move(Err); |
| return std::move(P); |
| } |
| |
| Expected<MemoryBufferRef> COFFPlatform::getPerJDObjectFile() { |
| auto PerJDObj = OrcRuntimeArchive->findSym("__orc_rt_coff_per_jd_marker"); |
| if (!PerJDObj) |
| return PerJDObj.takeError(); |
| |
| if (!*PerJDObj) |
| return make_error<StringError>("Could not find per jd object file", |
| inconvertibleErrorCode()); |
| |
| auto Buffer = (*PerJDObj)->getAsBinary(); |
| if (!Buffer) |
| return Buffer.takeError(); |
| |
| return (*Buffer)->getMemoryBufferRef(); |
| } |
| |
| 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}; |
| } |
| } |
| |
| Error COFFPlatform::setupJITDylib(JITDylib &JD) { |
| if (auto Err = JD.define(std::make_unique<COFFHeaderMaterializationUnit>( |
| *this, COFFHeaderStartSymbol))) |
| return Err; |
| |
| if (auto Err = ES.lookup({&JD}, COFFHeaderStartSymbol).takeError()) |
| return Err; |
| |
| // Define the CXX aliases. |
| SymbolAliasMap CXXAliases; |
| addAliases(ES, CXXAliases, requiredCXXAliases()); |
| if (auto Err = JD.define(symbolAliases(std::move(CXXAliases)))) |
| return Err; |
| |
| auto PerJDObj = getPerJDObjectFile(); |
| if (!PerJDObj) |
| return PerJDObj.takeError(); |
| |
| auto I = getObjectFileInterface(ES, *PerJDObj); |
| if (!I) |
| return I.takeError(); |
| |
| if (auto Err = ObjLinkingLayer.add( |
| JD, MemoryBuffer::getMemBuffer(*PerJDObj, false), std::move(*I))) |
| return Err; |
| |
| if (!Bootstrapping) { |
| auto ImportedLibs = StaticVCRuntime |
| ? VCRuntimeBootstrap->loadStaticVCRuntime(JD) |
| : VCRuntimeBootstrap->loadDynamicVCRuntime(JD); |
| if (!ImportedLibs) |
| return ImportedLibs.takeError(); |
| for (auto &Lib : *ImportedLibs) |
| if (auto Err = LoadDynLibrary(JD, Lib)) |
| return Err; |
| if (StaticVCRuntime) |
| if (auto Err = VCRuntimeBootstrap->initializeStaticVCRuntime(JD)) |
| return Err; |
| } |
| |
| JD.addGenerator(DLLImportDefinitionGenerator::Create(ES, ObjLinkingLayer)); |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::teardownJITDylib(JITDylib &JD) { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| auto I = JITDylibToHeaderAddr.find(&JD); |
| if (I != JITDylibToHeaderAddr.end()) { |
| assert(HeaderAddrToJITDylib.count(I->second) && |
| "HeaderAddrToJITDylib missing entry"); |
| HeaderAddrToJITDylib.erase(I->second); |
| JITDylibToHeaderAddr.erase(I); |
| } |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::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() << "COFFPlatform: Registered init symbol " << *InitSym << " for MU " |
| << MU.getName() << "\n"; |
| }); |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::notifyRemoving(ResourceTracker &RT) { |
| llvm_unreachable("Not supported yet"); |
| } |
| |
| SymbolAliasMap COFFPlatform::standardPlatformAliases(ExecutionSession &ES) { |
| SymbolAliasMap Aliases; |
| addAliases(ES, Aliases, standardRuntimeUtilityAliases()); |
| return Aliases; |
| } |
| |
| ArrayRef<std::pair<const char *, const char *>> |
| COFFPlatform::requiredCXXAliases() { |
| static const std::pair<const char *, const char *> RequiredCXXAliases[] = { |
| {"_CxxThrowException", "__orc_rt_coff_cxx_throw_exception"}, |
| {"_onexit", "__orc_rt_coff_onexit_per_jd"}, |
| {"atexit", "__orc_rt_coff_atexit_per_jd"}}; |
| |
| return ArrayRef<std::pair<const char *, const char *>>(RequiredCXXAliases); |
| } |
| |
| ArrayRef<std::pair<const char *, const char *>> |
| COFFPlatform::standardRuntimeUtilityAliases() { |
| static const std::pair<const char *, const char *> |
| StandardRuntimeUtilityAliases[] = { |
| {"__orc_rt_run_program", "__orc_rt_coff_run_program"}, |
| {"__orc_rt_jit_dlerror", "__orc_rt_coff_jit_dlerror"}, |
| {"__orc_rt_jit_dlopen", "__orc_rt_coff_jit_dlopen"}, |
| {"__orc_rt_jit_dlclose", "__orc_rt_coff_jit_dlclose"}, |
| {"__orc_rt_jit_dlsym", "__orc_rt_coff_jit_dlsym"}, |
| {"__orc_rt_log_error", "__orc_rt_log_error_to_stderr"}}; |
| |
| return ArrayRef<std::pair<const char *, const char *>>( |
| StandardRuntimeUtilityAliases); |
| } |
| |
| bool COFFPlatform::supportedTarget(const Triple &TT) { |
| switch (TT.getArch()) { |
| case Triple::x86_64: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| COFFPlatform::COFFPlatform(ExecutionSession &ES, |
| ObjectLinkingLayer &ObjLinkingLayer, |
| JITDylib &PlatformJD, const char *OrcRuntimePath, |
| LoadDynamicLibrary LoadDynLibrary, |
| bool StaticVCRuntime, const char *VCRuntimePath, |
| Error &Err) |
| : ES(ES), ObjLinkingLayer(ObjLinkingLayer), |
| LoadDynLibrary(std::move(LoadDynLibrary)), |
| StaticVCRuntime(StaticVCRuntime), |
| COFFHeaderStartSymbol(ES.intern("__ImageBase")) { |
| ErrorAsOutParameter _(&Err); |
| |
| // Create a generator for the ORC runtime archive. |
| auto OrcRuntimeArchiveGenerator = |
| StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer, OrcRuntimePath); |
| if (!OrcRuntimeArchiveGenerator) { |
| Err = OrcRuntimeArchiveGenerator.takeError(); |
| return; |
| } |
| |
| auto ArchiveBuffer = MemoryBuffer::getFile(OrcRuntimePath); |
| if (!ArchiveBuffer) { |
| Err = createFileError(OrcRuntimePath, ArchiveBuffer.getError()); |
| return; |
| } |
| OrcRuntimeArchiveBuffer = std::move(*ArchiveBuffer); |
| OrcRuntimeArchive = |
| std::make_unique<object::Archive>(*OrcRuntimeArchiveBuffer, Err); |
| if (Err) |
| return; |
| |
| Bootstrapping.store(true); |
| ObjLinkingLayer.addPlugin(std::make_unique<COFFPlatformPlugin>(*this)); |
| |
| // Load vc runtime |
| auto VCRT = |
| COFFVCRuntimeBootstrapper::Create(ES, ObjLinkingLayer, VCRuntimePath); |
| if (!VCRT) { |
| Err = VCRT.takeError(); |
| return; |
| } |
| VCRuntimeBootstrap = std::move(*VCRT); |
| |
| for (auto &Lib : (*OrcRuntimeArchiveGenerator)->getImportedDynamicLibraries()) |
| DylibsToPreload.insert(Lib); |
| |
| auto ImportedLibs = |
| StaticVCRuntime ? VCRuntimeBootstrap->loadStaticVCRuntime(PlatformJD) |
| : VCRuntimeBootstrap->loadDynamicVCRuntime(PlatformJD); |
| if (!ImportedLibs) { |
| Err = ImportedLibs.takeError(); |
| return; |
| } |
| |
| for (auto &Lib : *ImportedLibs) |
| DylibsToPreload.insert(Lib); |
| |
| PlatformJD.addGenerator(std::move(*OrcRuntimeArchiveGenerator)); |
| |
| // 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; |
| } |
| |
| for (auto& Lib : DylibsToPreload) |
| if (auto E2 = LoadDynLibrary(PlatformJD, Lib)) { |
| Err = std::move(E2); |
| return; |
| } |
| |
| if (StaticVCRuntime) |
| if (auto E2 = VCRuntimeBootstrap->initializeStaticVCRuntime(PlatformJD)) { |
| Err = std::move(E2); |
| return; |
| } |
| |
| // 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 = bootstrapCOFFRuntime(PlatformJD)) { |
| Err = std::move(E2); |
| return; |
| } |
| |
| Bootstrapping.store(false); |
| JDBootstrapStates.clear(); |
| } |
| |
| Expected<COFFPlatform::JITDylibDepMap> |
| COFFPlatform::buildJDDepMap(JITDylib &JD) { |
| return ES.runSessionLocked([&]() -> Expected<JITDylibDepMap> { |
| JITDylibDepMap JDDepMap; |
| |
| SmallVector<JITDylib *, 16> Worklist({&JD}); |
| while (!Worklist.empty()) { |
| auto CurJD = Worklist.back(); |
| Worklist.pop_back(); |
| |
| auto &DM = JDDepMap[CurJD]; |
| CurJD->withLinkOrderDo([&](const JITDylibSearchOrder &O) { |
| DM.reserve(O.size()); |
| for (auto &KV : O) { |
| if (KV.first == CurJD) |
| continue; |
| { |
| // Bare jitdylibs not known to the platform |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| if (!JITDylibToHeaderAddr.count(KV.first)) { |
| LLVM_DEBUG({ |
| dbgs() << "JITDylib unregistered to COFFPlatform detected in " |
| "LinkOrder: " |
| << CurJD->getName() << "\n"; |
| }); |
| continue; |
| } |
| } |
| DM.push_back(KV.first); |
| // Push unvisited entry. |
| if (!JDDepMap.count(KV.first)) { |
| Worklist.push_back(KV.first); |
| JDDepMap[KV.first] = {}; |
| } |
| } |
| }); |
| } |
| return std::move(JDDepMap); |
| }); |
| } |
| |
| void COFFPlatform::pushInitializersLoop(PushInitializersSendResultFn SendResult, |
| JITDylibSP JD, |
| JITDylibDepMap &JDDepMap) { |
| SmallVector<JITDylib *, 16> Worklist({JD.get()}); |
| DenseSet<JITDylib *> Visited({JD.get()}); |
| DenseMap<JITDylib *, SymbolLookupSet> NewInitSymbols; |
| ES.runSessionLocked([&]() { |
| while (!Worklist.empty()) { |
| auto CurJD = Worklist.back(); |
| Worklist.pop_back(); |
| |
| auto RISItr = RegisteredInitSymbols.find(CurJD); |
| if (RISItr != RegisteredInitSymbols.end()) { |
| NewInitSymbols[CurJD] = std::move(RISItr->second); |
| RegisteredInitSymbols.erase(RISItr); |
| } |
| |
| for (auto *DepJD : JDDepMap[CurJD]) |
| if (!Visited.count(DepJD)) { |
| Worklist.push_back(DepJD); |
| Visited.insert(DepJD); |
| } |
| } |
| }); |
| |
| // If there are no further init symbols to look up then send the link order |
| // (as a list of header addresses) to the caller. |
| if (NewInitSymbols.empty()) { |
| // Build the dep info map to return. |
| COFFJITDylibDepInfoMap DIM; |
| DIM.reserve(JDDepMap.size()); |
| for (auto &KV : JDDepMap) { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| COFFJITDylibDepInfo DepInfo; |
| DepInfo.reserve(KV.second.size()); |
| for (auto &Dep : KV.second) { |
| DepInfo.push_back(JITDylibToHeaderAddr[Dep]); |
| } |
| auto H = JITDylibToHeaderAddr[KV.first]; |
| DIM.push_back(std::make_pair(H, std::move(DepInfo))); |
| } |
| SendResult(DIM); |
| return; |
| } |
| |
| // Otherwise issue a lookup and re-run this phase when it completes. |
| lookupInitSymbolsAsync( |
| [this, SendResult = std::move(SendResult), &JD, |
| JDDepMap = std::move(JDDepMap)](Error Err) mutable { |
| if (Err) |
| SendResult(std::move(Err)); |
| else |
| pushInitializersLoop(std::move(SendResult), JD, JDDepMap); |
| }, |
| ES, std::move(NewInitSymbols)); |
| } |
| |
| void COFFPlatform::rt_pushInitializers(PushInitializersSendResultFn SendResult, |
| ExecutorAddr JDHeaderAddr) { |
| JITDylibSP JD; |
| { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| auto I = HeaderAddrToJITDylib.find(JDHeaderAddr); |
| if (I != HeaderAddrToJITDylib.end()) |
| JD = I->second; |
| } |
| |
| LLVM_DEBUG({ |
| dbgs() << "COFFPlatform::rt_pushInitializers(" << JDHeaderAddr << ") "; |
| if (JD) |
| dbgs() << "pushing initializers for " << JD->getName() << "\n"; |
| else |
| dbgs() << "No JITDylib for header address.\n"; |
| }); |
| |
| if (!JD) { |
| SendResult( |
| make_error<StringError>("No JITDylib with header addr " + |
| formatv("{0:x}", JDHeaderAddr.getValue()), |
| inconvertibleErrorCode())); |
| return; |
| } |
| |
| auto JDDepMap = buildJDDepMap(*JD); |
| if (!JDDepMap) { |
| SendResult(JDDepMap.takeError()); |
| return; |
| } |
| |
| pushInitializersLoop(std::move(SendResult), JD, *JDDepMap); |
| } |
| |
| void COFFPlatform::rt_lookupSymbol(SendSymbolAddressFn SendResult, |
| ExecutorAddr Handle, StringRef SymbolName) { |
| LLVM_DEBUG({ |
| dbgs() << "COFFPlatform::rt_lookupSymbol(\"" |
| << formatv("{0:x}", Handle.getValue()) << "\")\n"; |
| }); |
| |
| JITDylib *JD = nullptr; |
| |
| { |
| std::lock_guard<std::mutex> Lock(PlatformMutex); |
| auto I = HeaderAddrToJITDylib.find(Handle); |
| if (I != HeaderAddrToJITDylib.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 COFFPlatform::associateRuntimeSupportFunctions(JITDylib &PlatformJD) { |
| ExecutionSession::JITDispatchHandlerAssociationMap WFs; |
| |
| using LookupSymbolSPSSig = |
| SPSExpected<SPSExecutorAddr>(SPSExecutorAddr, SPSString); |
| WFs[ES.intern("__orc_rt_coff_symbol_lookup_tag")] = |
| ES.wrapAsyncWithSPS<LookupSymbolSPSSig>(this, |
| &COFFPlatform::rt_lookupSymbol); |
| using PushInitializersSPSSig = |
| SPSExpected<SPSCOFFJITDylibDepInfoMap>(SPSExecutorAddr); |
| WFs[ES.intern("__orc_rt_coff_push_initializers_tag")] = |
| ES.wrapAsyncWithSPS<PushInitializersSPSSig>( |
| this, &COFFPlatform::rt_pushInitializers); |
| |
| return ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs)); |
| } |
| |
| Error COFFPlatform::runBootstrapInitializers(JDBootstrapState &BState) { |
| llvm::sort(BState.Initializers); |
| if (auto Err = |
| runBootstrapSubsectionInitializers(BState, ".CRT$XIA", ".CRT$XIZ")) |
| return Err; |
| |
| if (auto Err = runSymbolIfExists(*BState.JD, "__run_after_c_init")) |
| return Err; |
| |
| if (auto Err = |
| runBootstrapSubsectionInitializers(BState, ".CRT$XCA", ".CRT$XCZ")) |
| return Err; |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::runBootstrapSubsectionInitializers(JDBootstrapState &BState, |
| StringRef Start, |
| StringRef End) { |
| for (auto &Initializer : BState.Initializers) |
| if (Initializer.first >= Start && Initializer.first <= End && |
| Initializer.second) { |
| auto Res = |
| ES.getExecutorProcessControl().runAsVoidFunction(Initializer.second); |
| if (!Res) |
| return Res.takeError(); |
| } |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::bootstrapCOFFRuntime(JITDylib &PlatformJD) { |
| // Lookup of runtime symbols causes the collection of initializers if |
| // it's static linking setting. |
| if (auto Err = lookupAndRecordAddrs( |
| ES, LookupKind::Static, makeJITDylibSearchOrder(&PlatformJD), |
| { |
| {ES.intern("__orc_rt_coff_platform_bootstrap"), |
| &orc_rt_coff_platform_bootstrap}, |
| {ES.intern("__orc_rt_coff_platform_shutdown"), |
| &orc_rt_coff_platform_shutdown}, |
| {ES.intern("__orc_rt_coff_register_jitdylib"), |
| &orc_rt_coff_register_jitdylib}, |
| {ES.intern("__orc_rt_coff_deregister_jitdylib"), |
| &orc_rt_coff_deregister_jitdylib}, |
| {ES.intern("__orc_rt_coff_register_object_sections"), |
| &orc_rt_coff_register_object_sections}, |
| {ES.intern("__orc_rt_coff_deregister_object_sections"), |
| &orc_rt_coff_deregister_object_sections}, |
| })) |
| return Err; |
| |
| // Call bootstrap functions |
| if (auto Err = ES.callSPSWrapper<void()>(orc_rt_coff_platform_bootstrap)) |
| return Err; |
| |
| // Do the pending jitdylib registration actions that we couldn't do |
| // because orc runtime was not linked fully. |
| for (auto KV : JDBootstrapStates) { |
| auto &JDBState = KV.second; |
| if (auto Err = ES.callSPSWrapper<void(SPSString, SPSExecutorAddr)>( |
| orc_rt_coff_register_jitdylib, JDBState.JDName, |
| JDBState.HeaderAddr)) |
| return Err; |
| |
| for (auto &ObjSectionMap : JDBState.ObjectSectionsMaps) |
| if (auto Err = ES.callSPSWrapper<void(SPSExecutorAddr, |
| SPSCOFFObjectSectionsMap, bool)>( |
| orc_rt_coff_register_object_sections, JDBState.HeaderAddr, |
| ObjSectionMap, false)) |
| return Err; |
| } |
| |
| // Run static initializers collected in bootstrap stage. |
| for (auto KV : JDBootstrapStates) { |
| auto &JDBState = KV.second; |
| if (auto Err = runBootstrapInitializers(JDBState)) |
| return Err; |
| } |
| |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::runSymbolIfExists(JITDylib &PlatformJD, |
| StringRef SymbolName) { |
| ExecutorAddr jit_function; |
| auto AfterCLookupErr = lookupAndRecordAddrs( |
| ES, LookupKind::Static, makeJITDylibSearchOrder(&PlatformJD), |
| {{ES.intern(SymbolName), &jit_function}}); |
| if (!AfterCLookupErr) { |
| auto Res = ES.getExecutorProcessControl().runAsVoidFunction(jit_function); |
| if (!Res) |
| return Res.takeError(); |
| return Error::success(); |
| } |
| if (!AfterCLookupErr.isA<SymbolsNotFound>()) |
| return AfterCLookupErr; |
| consumeError(std::move(AfterCLookupErr)); |
| return Error::success(); |
| } |
| |
| void COFFPlatform::COFFPlatformPlugin::modifyPassConfig( |
| MaterializationResponsibility &MR, jitlink::LinkGraph &LG, |
| jitlink::PassConfiguration &Config) { |
| |
| bool IsBootstrapping = CP.Bootstrapping.load(); |
| |
| if (auto InitSymbol = MR.getInitializerSymbol()) { |
| if (InitSymbol == CP.COFFHeaderStartSymbol) { |
| Config.PostAllocationPasses.push_back( |
| [this, &MR, IsBootstrapping](jitlink::LinkGraph &G) { |
| return associateJITDylibHeaderSymbol(G, MR, IsBootstrapping); |
| }); |
| return; |
| } |
| Config.PrePrunePasses.push_back([this, &MR](jitlink::LinkGraph &G) { |
| return preserveInitializerSections(G, MR); |
| }); |
| } |
| |
| if (!IsBootstrapping) |
| Config.PostFixupPasses.push_back( |
| [this, &JD = MR.getTargetJITDylib()](jitlink::LinkGraph &G) { |
| return registerObjectPlatformSections(G, JD); |
| }); |
| else |
| Config.PostFixupPasses.push_back( |
| [this, &JD = MR.getTargetJITDylib()](jitlink::LinkGraph &G) { |
| return registerObjectPlatformSectionsInBootstrap(G, JD); |
| }); |
| } |
| |
| ObjectLinkingLayer::Plugin::SyntheticSymbolDependenciesMap |
| COFFPlatform::COFFPlatformPlugin::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(); |
| } |
| |
| Error COFFPlatform::COFFPlatformPlugin::associateJITDylibHeaderSymbol( |
| jitlink::LinkGraph &G, MaterializationResponsibility &MR, |
| bool IsBootstraping) { |
| auto I = llvm::find_if(G.defined_symbols(), [this](jitlink::Symbol *Sym) { |
| return Sym->getName() == *CP.COFFHeaderStartSymbol; |
| }); |
| assert(I != G.defined_symbols().end() && "Missing COFF header start symbol"); |
| |
| auto &JD = MR.getTargetJITDylib(); |
| std::lock_guard<std::mutex> Lock(CP.PlatformMutex); |
| auto HeaderAddr = (*I)->getAddress(); |
| CP.JITDylibToHeaderAddr[&JD] = HeaderAddr; |
| CP.HeaderAddrToJITDylib[HeaderAddr] = &JD; |
| if (!IsBootstraping) { |
| G.allocActions().push_back( |
| {cantFail(WrapperFunctionCall::Create< |
| SPSArgList<SPSString, SPSExecutorAddr>>( |
| CP.orc_rt_coff_register_jitdylib, JD.getName(), HeaderAddr)), |
| cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>( |
| CP.orc_rt_coff_deregister_jitdylib, HeaderAddr))}); |
| } else { |
| G.allocActions().push_back( |
| {{}, |
| cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>( |
| CP.orc_rt_coff_deregister_jitdylib, HeaderAddr))}); |
| JDBootstrapState BState; |
| BState.JD = &JD; |
| BState.JDName = JD.getName(); |
| BState.HeaderAddr = HeaderAddr; |
| CP.JDBootstrapStates.emplace(&JD, BState); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::COFFPlatformPlugin::registerObjectPlatformSections( |
| jitlink::LinkGraph &G, JITDylib &JD) { |
| COFFObjectSectionsMap ObjSecs; |
| auto HeaderAddr = CP.JITDylibToHeaderAddr[&JD]; |
| assert(HeaderAddr && "Must be registered jitdylib"); |
| for (auto &S : G.sections()) { |
| jitlink::SectionRange Range(S); |
| if (Range.getSize()) |
| ObjSecs.push_back(std::make_pair(S.getName().str(), Range.getRange())); |
| } |
| |
| G.allocActions().push_back( |
| {cantFail(WrapperFunctionCall::Create<SPSCOFFRegisterObjectSectionsArgs>( |
| CP.orc_rt_coff_register_object_sections, HeaderAddr, ObjSecs, true)), |
| cantFail( |
| WrapperFunctionCall::Create<SPSCOFFDeregisterObjectSectionsArgs>( |
| CP.orc_rt_coff_deregister_object_sections, HeaderAddr, |
| ObjSecs))}); |
| |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::COFFPlatformPlugin::preserveInitializerSections( |
| jitlink::LinkGraph &G, MaterializationResponsibility &MR) { |
| JITLinkSymbolSet InitSectionSymbols; |
| for (auto &Sec : G.sections()) |
| if (COFFPlatform::isInitializerSection(Sec.getName())) |
| for (auto *B : Sec.blocks()) |
| if (!B->edges_empty()) |
| InitSectionSymbols.insert( |
| &G.addAnonymousSymbol(*B, 0, 0, false, true)); |
| |
| std::lock_guard<std::mutex> Lock(PluginMutex); |
| InitSymbolDeps[&MR] = InitSectionSymbols; |
| return Error::success(); |
| } |
| |
| Error COFFPlatform::COFFPlatformPlugin:: |
| registerObjectPlatformSectionsInBootstrap(jitlink::LinkGraph &G, |
| JITDylib &JD) { |
| std::lock_guard<std::mutex> Lock(CP.PlatformMutex); |
| auto HeaderAddr = CP.JITDylibToHeaderAddr[&JD]; |
| COFFObjectSectionsMap ObjSecs; |
| for (auto &S : G.sections()) { |
| jitlink::SectionRange Range(S); |
| if (Range.getSize()) |
| ObjSecs.push_back(std::make_pair(S.getName().str(), Range.getRange())); |
| } |
| |
| G.allocActions().push_back( |
| {{}, |
| cantFail( |
| WrapperFunctionCall::Create<SPSCOFFDeregisterObjectSectionsArgs>( |
| CP.orc_rt_coff_deregister_object_sections, HeaderAddr, |
| ObjSecs))}); |
| |
| auto &BState = CP.JDBootstrapStates[&JD]; |
| BState.ObjectSectionsMaps.push_back(std::move(ObjSecs)); |
| |
| // Collect static initializers |
| for (auto &S : G.sections()) |
| if (COFFPlatform::isInitializerSection(S.getName())) |
| for (auto *B : S.blocks()) { |
| if (B->edges_empty()) |
| continue; |
| for (auto &E : B->edges()) |
| BState.Initializers.push_back(std::make_pair( |
| S.getName().str(), |
| ExecutorAddr(E.getTarget().getAddress() + E.getAddend()))); |
| } |
| |
| return Error::success(); |
| } |
| |
| } // End namespace orc. |
| } // End namespace llvm. |