
Use SymbolStringPtr for Symbol names in LinkGraph. This reduces string interning on the boundary between JITLink and ORC, and allows pointer comparisons (rather than string comparisons) between Symbol names. This should improve the performance and readability of code that bridges between JITLink and ORC (e.g. ObjectLinkingLayer and ObjectLinkingLayer::Plugins). To enable use of SymbolStringPtr a std::shared_ptr<SymbolStringPool> is added to LinkGraph and threaded through to its construction sites in LLVM and Bolt. All LinkGraphs that are to have symbol names compared by pointer equality must point to the same SymbolStringPool instance, which in ORC sessions should be the pool attached to the ExecutionSession. --------- Co-authored-by: Lang Hames <lhames@gmail.com>
922 lines
32 KiB
C++
922 lines
32 KiB
C++
//===------- 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/AbsoluteSymbols.h"
|
|
#include "llvm/ExecutionEngine/Orc/DebugUtils.h"
|
|
#include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h"
|
|
#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
|
|
#include "llvm/ExecutionEngine/Orc/Shared/ObjectFormats.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;
|
|
llvm::endianness Endianness;
|
|
const auto &TT = CP.getExecutionSession().getTargetTriple();
|
|
|
|
switch (TT.getArch()) {
|
|
case Triple::x86_64:
|
|
PointerSize = 8;
|
|
Endianness = llvm::endianness::little;
|
|
break;
|
|
default:
|
|
llvm_unreachable("Unrecognized architecture");
|
|
}
|
|
|
|
auto G = std::make_unique<jitlink::LinkGraph>(
|
|
"<COFFHeaderMU>", CP.getExecutionSession().getSymbolStringPool(), 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.allocateContent(
|
|
ArrayRef<char>(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(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
|
|
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
|
|
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
|
|
const char *VCRuntimePath,
|
|
std::optional<SymbolAliasMap> RuntimeAliases) {
|
|
|
|
auto &ES = ObjLinkingLayer.getExecutionSession();
|
|
|
|
// If the target is not supported then bail out immediately.
|
|
if (!supportedTarget(ES.getTargetTriple()))
|
|
return make_error<StringError>("Unsupported COFFPlatform triple: " +
|
|
ES.getTargetTriple().str(),
|
|
inconvertibleErrorCode());
|
|
|
|
auto &EPC = ES.getExecutorProcessControl();
|
|
|
|
auto GeneratorArchive =
|
|
object::Archive::create(OrcRuntimeArchiveBuffer->getMemBufferRef());
|
|
if (!GeneratorArchive)
|
|
return GeneratorArchive.takeError();
|
|
|
|
auto OrcRuntimeArchiveGenerator = StaticLibraryDefinitionGenerator::Create(
|
|
ObjLinkingLayer, nullptr, std::move(*GeneratorArchive));
|
|
if (!OrcRuntimeArchiveGenerator)
|
|
return OrcRuntimeArchiveGenerator.takeError();
|
|
|
|
// We need a second instance of the archive (for now) for the Platform. We
|
|
// can `cantFail` this call, since if it were going to fail it would have
|
|
// failed above.
|
|
auto RuntimeArchive = cantFail(
|
|
object::Archive::create(OrcRuntimeArchiveBuffer->getMemBufferRef()));
|
|
|
|
// 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,
|
|
JITSymbolFlags::Exported}},
|
|
{ES.intern("__orc_rt_jit_dispatch_ctx"),
|
|
{EPC.getJITDispatchInfo().JITDispatchContext,
|
|
JITSymbolFlags::Exported}}})))
|
|
return std::move(Err);
|
|
|
|
PlatformJD.addToLinkOrder(HostFuncJD);
|
|
|
|
// Create the instance.
|
|
Error Err = Error::success();
|
|
auto P = std::unique_ptr<COFFPlatform>(new COFFPlatform(
|
|
ObjLinkingLayer, PlatformJD, std::move(*OrcRuntimeArchiveGenerator),
|
|
std::move(OrcRuntimeArchiveBuffer), std::move(RuntimeArchive),
|
|
std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath, Err));
|
|
if (Err)
|
|
return std::move(Err);
|
|
return std::move(P);
|
|
}
|
|
|
|
Expected<std::unique_ptr<COFFPlatform>>
|
|
COFFPlatform::Create(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
|
|
const char *OrcRuntimePath,
|
|
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
|
|
const char *VCRuntimePath,
|
|
std::optional<SymbolAliasMap> RuntimeAliases) {
|
|
|
|
auto ArchiveBuffer = MemoryBuffer::getFile(OrcRuntimePath);
|
|
if (!ArchiveBuffer)
|
|
return createFileError(OrcRuntimePath, ArchiveBuffer.getError());
|
|
|
|
return Create(ObjLinkingLayer, PlatformJD, std::move(*ArchiveBuffer),
|
|
std::move(LoadDynLibrary), StaticVCRuntime, VCRuntimePath,
|
|
std::move(RuntimeAliases));
|
|
}
|
|
|
|
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(
|
|
ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
|
|
std::unique_ptr<StaticLibraryDefinitionGenerator> OrcRuntimeGenerator,
|
|
std::unique_ptr<MemoryBuffer> OrcRuntimeArchiveBuffer,
|
|
std::unique_ptr<object::Archive> OrcRuntimeArchive,
|
|
LoadDynamicLibrary LoadDynLibrary, bool StaticVCRuntime,
|
|
const char *VCRuntimePath, Error &Err)
|
|
: ES(ObjLinkingLayer.getExecutionSession()),
|
|
ObjLinkingLayer(ObjLinkingLayer),
|
|
LoadDynLibrary(std::move(LoadDynLibrary)),
|
|
OrcRuntimeArchiveBuffer(std::move(OrcRuntimeArchiveBuffer)),
|
|
OrcRuntimeArchive(std::move(OrcRuntimeArchive)),
|
|
StaticVCRuntime(StaticVCRuntime),
|
|
COFFHeaderStartSymbol(ES.intern("__ImageBase")) {
|
|
ErrorAsOutParameter _(Err);
|
|
|
|
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 : OrcRuntimeGenerator->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(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;
|
|
}
|
|
|
|
for (auto& Lib : DylibsToPreload)
|
|
if (auto E2 = this->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.insert(DepJD).second)
|
|
Worklist.push_back(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),
|
|
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(\"" << Handle << "\")\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 " << Handle << "\n");
|
|
SendResult(make_error<StringError>("No JITDylib associated with handle " +
|
|
formatv("{0:x}", Handle),
|
|
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(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);
|
|
});
|
|
}
|
|
|
|
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) {
|
|
|
|
if (const auto &InitSymName = MR.getInitializerSymbol()) {
|
|
|
|
jitlink::Symbol *InitSym = nullptr;
|
|
|
|
for (auto &InitSection : G.sections()) {
|
|
// Skip non-init sections.
|
|
if (!isCOFFInitializerSection(InitSection.getName()) ||
|
|
InitSection.empty())
|
|
continue;
|
|
|
|
// Create the init symbol if it has not been created already and attach it
|
|
// to the first block.
|
|
if (!InitSym) {
|
|
auto &B = **InitSection.blocks().begin();
|
|
InitSym = &G.addDefinedSymbol(
|
|
B, 0, *InitSymName, B.getSize(), jitlink::Linkage::Strong,
|
|
jitlink::Scope::SideEffectsOnly, false, true);
|
|
}
|
|
|
|
// Add keep-alive edges to anonymous symbols in all other init blocks.
|
|
for (auto *B : InitSection.blocks()) {
|
|
if (B == &InitSym->getBlock())
|
|
continue;
|
|
|
|
auto &S = G.addAnonymousSymbol(*B, 0, B->getSize(), false, true);
|
|
InitSym->getBlock().addEdge(jitlink::Edge::KeepAlive, 0, S, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (isCOFFInitializerSection(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(), E.getTarget().getAddress() + E.getAddend()));
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
} // End namespace orc.
|
|
} // End namespace llvm.
|