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