llvm-project/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp

1033 lines
37 KiB
C++

//===----- ELFNixPlatform.cpp - Utilities for executing ELFNix in Orc -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h"
#include "llvm/ExecutionEngine/JITLink/aarch64.h"
#include "llvm/ExecutionEngine/JITLink/loongarch.h"
#include "llvm/ExecutionEngine/JITLink/ppc64.h"
#include "llvm/ExecutionEngine/JITLink/x86_64.h"
#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h"
#include "llvm/Support/Debug.h"
#include <optional>
#define DEBUG_TYPE "orc"
using namespace llvm;
using namespace llvm::orc;
using namespace llvm::orc::shared;
namespace {
template <typename SPSSerializer, typename... ArgTs>
shared::WrapperFunctionCall::ArgDataBufferType
getArgDataBufferType(const ArgTs &...Args) {
shared::WrapperFunctionCall::ArgDataBufferType ArgData;
ArgData.resize(SPSSerializer::size(Args...));
SPSOutputBuffer OB(ArgData.empty() ? nullptr : ArgData.data(),
ArgData.size());
if (SPSSerializer::serialize(OB, Args...))
return ArgData;
return {};
}
std::unique_ptr<jitlink::LinkGraph> createPlatformGraph(ELFNixPlatform &MOP,
std::string Name) {
auto &ES = MOP.getExecutionSession();
return std::make_unique<jitlink::LinkGraph>(
std::move(Name), ES.getSymbolStringPool(), ES.getTargetTriple(),
SubtargetFeatures(), jitlink::getGenericEdgeKindName);
}
// Creates a Bootstrap-Complete LinkGraph to run deferred actions.
class ELFNixPlatformCompleteBootstrapMaterializationUnit
: public MaterializationUnit {
public:
ELFNixPlatformCompleteBootstrapMaterializationUnit(
ELFNixPlatform &MOP, StringRef PlatformJDName,
SymbolStringPtr CompleteBootstrapSymbol, DeferredRuntimeFnMap DeferredAAs,
ExecutorAddr ELFNixHeaderAddr, ExecutorAddr PlatformBootstrap,
ExecutorAddr PlatformShutdown, ExecutorAddr RegisterJITDylib,
ExecutorAddr DeregisterJITDylib)
: MaterializationUnit(
{{{CompleteBootstrapSymbol, JITSymbolFlags::None}}, nullptr}),
MOP(MOP), PlatformJDName(PlatformJDName),
CompleteBootstrapSymbol(std::move(CompleteBootstrapSymbol)),
DeferredAAsMap(std::move(DeferredAAs)),
ELFNixHeaderAddr(ELFNixHeaderAddr),
PlatformBootstrap(PlatformBootstrap),
PlatformShutdown(PlatformShutdown), RegisterJITDylib(RegisterJITDylib),
DeregisterJITDylib(DeregisterJITDylib) {}
StringRef getName() const override {
return "ELFNixPlatformCompleteBootstrap";
}
void materialize(std::unique_ptr<MaterializationResponsibility> R) override {
using namespace jitlink;
auto G = createPlatformGraph(MOP, "<OrcRTCompleteBootstrap>");
auto &PlaceholderSection =
G->createSection("__orc_rt_cplt_bs", MemProt::Read);
auto &PlaceholderBlock =
G->createZeroFillBlock(PlaceholderSection, 1, ExecutorAddr(), 1, 0);
G->addDefinedSymbol(PlaceholderBlock, 0, *CompleteBootstrapSymbol, 1,
Linkage::Strong, Scope::Hidden, false, true);
// 1. Bootstrap the platform support code.
G->allocActions().push_back(
{cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
PlatformBootstrap, ELFNixHeaderAddr)),
cantFail(
WrapperFunctionCall::Create<SPSArgList<>>(PlatformShutdown))});
// 2. Register the platform JITDylib.
G->allocActions().push_back(
{cantFail(WrapperFunctionCall::Create<
SPSArgList<SPSString, SPSExecutorAddr>>(
RegisterJITDylib, PlatformJDName, ELFNixHeaderAddr)),
cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
DeregisterJITDylib, ELFNixHeaderAddr))});
// 4. Add the deferred actions to the graph.
for (auto &[Fn, CallDatas] : DeferredAAsMap) {
for (auto &CallData : CallDatas) {
G->allocActions().push_back(
{WrapperFunctionCall(Fn.first->Addr, std::move(CallData.first)),
WrapperFunctionCall(Fn.second->Addr, std::move(CallData.second))});
}
}
MOP.getObjectLinkingLayer().emit(std::move(R), std::move(G));
}
void discard(const JITDylib &JD, const SymbolStringPtr &Sym) override {}
private:
ELFNixPlatform &MOP;
StringRef PlatformJDName;
SymbolStringPtr CompleteBootstrapSymbol;
DeferredRuntimeFnMap DeferredAAsMap;
ExecutorAddr ELFNixHeaderAddr;
ExecutorAddr PlatformBootstrap;
ExecutorAddr PlatformShutdown;
ExecutorAddr RegisterJITDylib;
ExecutorAddr DeregisterJITDylib;
};
class DSOHandleMaterializationUnit : public MaterializationUnit {
public:
DSOHandleMaterializationUnit(ELFNixPlatform &ENP,
const SymbolStringPtr &DSOHandleSymbol)
: MaterializationUnit(
createDSOHandleSectionInterface(ENP, DSOHandleSymbol)),
ENP(ENP) {}
StringRef getName() const override { return "DSOHandleMU"; }
void materialize(std::unique_ptr<MaterializationResponsibility> R) override {
auto &ES = ENP.getExecutionSession();
jitlink::Edge::Kind EdgeKind;
switch (ES.getTargetTriple().getArch()) {
case Triple::x86_64:
EdgeKind = jitlink::x86_64::Pointer64;
break;
case Triple::aarch64:
EdgeKind = jitlink::aarch64::Pointer64;
break;
case Triple::ppc64:
EdgeKind = jitlink::ppc64::Pointer64;
break;
case Triple::ppc64le:
EdgeKind = jitlink::ppc64::Pointer64;
break;
case Triple::loongarch64:
EdgeKind = jitlink::loongarch::Pointer64;
break;
default:
llvm_unreachable("Unrecognized architecture");
}
// void *__dso_handle = &__dso_handle;
auto G = std::make_unique<jitlink::LinkGraph>(
"<DSOHandleMU>", ES.getSymbolStringPool(), ES.getTargetTriple(),
SubtargetFeatures(), jitlink::getGenericEdgeKindName);
auto &DSOHandleSection =
G->createSection(".data.__dso_handle", MemProt::Read);
auto &DSOHandleBlock = G->createContentBlock(
DSOHandleSection, getDSOHandleContent(G->getPointerSize()),
orc::ExecutorAddr(), 8, 0);
auto &DSOHandleSymbol = G->addDefinedSymbol(
DSOHandleBlock, 0, *R->getInitializerSymbol(), DSOHandleBlock.getSize(),
jitlink::Linkage::Strong, jitlink::Scope::Default, false, true);
DSOHandleBlock.addEdge(EdgeKind, 0, DSOHandleSymbol, 0);
ENP.getObjectLinkingLayer().emit(std::move(R), std::move(G));
}
void discard(const JITDylib &JD, const SymbolStringPtr &Sym) override {}
private:
static MaterializationUnit::Interface
createDSOHandleSectionInterface(ELFNixPlatform &ENP,
const SymbolStringPtr &DSOHandleSymbol) {
SymbolFlagsMap SymbolFlags;
SymbolFlags[DSOHandleSymbol] = JITSymbolFlags::Exported;
return MaterializationUnit::Interface(std::move(SymbolFlags),
DSOHandleSymbol);
}
ArrayRef<char> getDSOHandleContent(size_t PointerSize) {
static const char Content[8] = {0};
assert(PointerSize <= sizeof Content);
return {Content, PointerSize};
}
ELFNixPlatform &ENP;
};
} // end anonymous namespace
namespace llvm {
namespace orc {
Expected<std::unique_ptr<ELFNixPlatform>>
ELFNixPlatform::Create(ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD,
std::unique_ptr<DefinitionGenerator> OrcRuntime,
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 ELFNixPlatform triple: " +
ES.getTargetTriple().str(),
inconvertibleErrorCode());
auto &EPC = ES.getExecutorProcessControl();
// Create default aliases if the caller didn't supply any.
if (!RuntimeAliases) {
auto StandardRuntimeAliases = standardPlatformAliases(ES, PlatformJD);
if (!StandardRuntimeAliases)
return StandardRuntimeAliases.takeError();
RuntimeAliases = std::move(*StandardRuntimeAliases);
}
// Define the aliases.
if (auto Err = PlatformJD.define(symbolAliases(std::move(*RuntimeAliases))))
return std::move(Err);
// Add JIT-dispatch function support symbols.
if (auto Err = PlatformJD.define(
absoluteSymbols({{ES.intern("__orc_rt_jit_dispatch"),
{EPC.getJITDispatchInfo().JITDispatchFunction,
JITSymbolFlags::Exported}},
{ES.intern("__orc_rt_jit_dispatch_ctx"),
{EPC.getJITDispatchInfo().JITDispatchContext,
JITSymbolFlags::Exported}}})))
return std::move(Err);
// Create the instance.
Error Err = Error::success();
auto P = std::unique_ptr<ELFNixPlatform>(new ELFNixPlatform(
ObjLinkingLayer, PlatformJD, std::move(OrcRuntime), Err));
if (Err)
return std::move(Err);
return std::move(P);
}
Expected<std::unique_ptr<ELFNixPlatform>>
ELFNixPlatform::Create(ObjectLinkingLayer &ObjLinkingLayer,
JITDylib &PlatformJD, const char *OrcRuntimePath,
std::optional<SymbolAliasMap> RuntimeAliases) {
// Create a generator for the ORC runtime archive.
auto OrcRuntimeArchiveGenerator =
StaticLibraryDefinitionGenerator::Load(ObjLinkingLayer, OrcRuntimePath);
if (!OrcRuntimeArchiveGenerator)
return OrcRuntimeArchiveGenerator.takeError();
return Create(ObjLinkingLayer, PlatformJD,
std::move(*OrcRuntimeArchiveGenerator),
std::move(RuntimeAliases));
}
Error ELFNixPlatform::setupJITDylib(JITDylib &JD) {
if (auto Err = JD.define(std::make_unique<DSOHandleMaterializationUnit>(
*this, DSOHandleSymbol)))
return Err;
return ES.lookup({&JD}, DSOHandleSymbol).takeError();
}
Error ELFNixPlatform::teardownJITDylib(JITDylib &JD) {
std::lock_guard<std::mutex> Lock(PlatformMutex);
auto I = JITDylibToHandleAddr.find(&JD);
if (I != JITDylibToHandleAddr.end()) {
assert(HandleAddrToJITDylib.count(I->second) &&
"HandleAddrToJITDylib missing entry");
HandleAddrToJITDylib.erase(I->second);
JITDylibToHandleAddr.erase(I);
}
return Error::success();
}
Error ELFNixPlatform::notifyAdding(ResourceTracker &RT,
const MaterializationUnit &MU) {
auto &JD = RT.getJITDylib();
const auto &InitSym = MU.getInitializerSymbol();
if (!InitSym)
return Error::success();
RegisteredInitSymbols[&JD].add(InitSym,
SymbolLookupFlags::WeaklyReferencedSymbol);
LLVM_DEBUG({
dbgs() << "ELFNixPlatform: Registered init symbol " << *InitSym
<< " for MU " << MU.getName() << "\n";
});
return Error::success();
}
Error ELFNixPlatform::notifyRemoving(ResourceTracker &RT) {
llvm_unreachable("Not supported yet");
}
static void addAliases(ExecutionSession &ES, SymbolAliasMap &Aliases,
ArrayRef<std::pair<const char *, const char *>> AL) {
for (auto &KV : AL) {
auto AliasName = ES.intern(KV.first);
assert(!Aliases.count(AliasName) && "Duplicate symbol name in alias map");
Aliases[std::move(AliasName)] = {ES.intern(KV.second),
JITSymbolFlags::Exported};
}
}
Expected<SymbolAliasMap>
ELFNixPlatform::standardPlatformAliases(ExecutionSession &ES,
JITDylib &PlatformJD) {
SymbolAliasMap Aliases;
addAliases(ES, Aliases, requiredCXXAliases());
addAliases(ES, Aliases, standardRuntimeUtilityAliases());
addAliases(ES, Aliases, standardLazyCompilationAliases());
return Aliases;
}
ArrayRef<std::pair<const char *, const char *>>
ELFNixPlatform::requiredCXXAliases() {
static const std::pair<const char *, const char *> RequiredCXXAliases[] = {
{"__cxa_atexit", "__orc_rt_elfnix_cxa_atexit"},
{"atexit", "__orc_rt_elfnix_atexit"}};
return ArrayRef<std::pair<const char *, const char *>>(RequiredCXXAliases);
}
ArrayRef<std::pair<const char *, const char *>>
ELFNixPlatform::standardRuntimeUtilityAliases() {
static const std::pair<const char *, const char *>
StandardRuntimeUtilityAliases[] = {
{"__orc_rt_run_program", "__orc_rt_elfnix_run_program"},
{"__orc_rt_jit_dlerror", "__orc_rt_elfnix_jit_dlerror"},
{"__orc_rt_jit_dlopen", "__orc_rt_elfnix_jit_dlopen"},
{"__orc_rt_jit_dlupdate", "__orc_rt_elfnix_jit_dlupdate"},
{"__orc_rt_jit_dlclose", "__orc_rt_elfnix_jit_dlclose"},
{"__orc_rt_jit_dlsym", "__orc_rt_elfnix_jit_dlsym"},
{"__orc_rt_log_error", "__orc_rt_log_error_to_stderr"}};
return ArrayRef<std::pair<const char *, const char *>>(
StandardRuntimeUtilityAliases);
}
ArrayRef<std::pair<const char *, const char *>>
ELFNixPlatform::standardLazyCompilationAliases() {
static const std::pair<const char *, const char *>
StandardLazyCompilationAliases[] = {
{"__orc_rt_reenter", "__orc_rt_sysv_reenter"}};
return ArrayRef<std::pair<const char *, const char *>>(
StandardLazyCompilationAliases);
}
bool ELFNixPlatform::supportedTarget(const Triple &TT) {
switch (TT.getArch()) {
case Triple::x86_64:
case Triple::aarch64:
// FIXME: jitlink for ppc64 hasn't been well tested, leave it unsupported
// right now.
case Triple::ppc64le:
case Triple::loongarch64:
return true;
default:
return false;
}
}
ELFNixPlatform::ELFNixPlatform(
ObjectLinkingLayer &ObjLinkingLayer, JITDylib &PlatformJD,
std::unique_ptr<DefinitionGenerator> OrcRuntimeGenerator, Error &Err)
: ES(ObjLinkingLayer.getExecutionSession()), PlatformJD(PlatformJD),
ObjLinkingLayer(ObjLinkingLayer),
DSOHandleSymbol(ES.intern("__dso_handle")) {
ErrorAsOutParameter _(Err);
ObjLinkingLayer.addPlugin(std::make_unique<ELFNixPlatformPlugin>(*this));
PlatformJD.addGenerator(std::move(OrcRuntimeGenerator));
BootstrapInfo BI;
Bootstrap = &BI;
// 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;
}
// Step (2) Request runtime registration functions to trigger
// materialization..
if ((Err = ES.lookup(
makeJITDylibSearchOrder(&PlatformJD),
SymbolLookupSet(
{PlatformBootstrap.Name, PlatformShutdown.Name,
RegisterJITDylib.Name, DeregisterJITDylib.Name,
RegisterInitSections.Name, DeregisterInitSections.Name,
RegisterObjectSections.Name,
DeregisterObjectSections.Name, CreatePThreadKey.Name}))
.takeError()))
return;
// Step (3) Wait for any incidental linker work to complete.
{
std::unique_lock<std::mutex> Lock(BI.Mutex);
BI.CV.wait(Lock, [&]() { return BI.ActiveGraphs == 0; });
Bootstrap = nullptr;
}
// Step (4) Add complete-bootstrap materialization unit and request.
auto BootstrapCompleteSymbol =
ES.intern("__orc_rt_elfnix_complete_bootstrap");
if ((Err = PlatformJD.define(
std::make_unique<ELFNixPlatformCompleteBootstrapMaterializationUnit>(
*this, PlatformJD.getName(), BootstrapCompleteSymbol,
std::move(BI.DeferredRTFnMap), BI.ELFNixHeaderAddr,
PlatformBootstrap.Addr, PlatformShutdown.Addr,
RegisterJITDylib.Addr, DeregisterJITDylib.Addr))))
return;
if ((Err = ES.lookup(makeJITDylibSearchOrder(
&PlatformJD, JITDylibLookupFlags::MatchAllSymbols),
std::move(BootstrapCompleteSymbol))
.takeError()))
return;
// Associate wrapper function tags with JIT-side function implementations.
if (auto E2 = associateRuntimeSupportFunctions(PlatformJD)) {
Err = std::move(E2);
return;
}
}
Error ELFNixPlatform::associateRuntimeSupportFunctions(JITDylib &PlatformJD) {
ExecutionSession::JITDispatchHandlerAssociationMap WFs;
using RecordInitializersSPSSig =
SPSExpected<SPSELFNixJITDylibDepInfoMap>(SPSExecutorAddr);
WFs[ES.intern("__orc_rt_elfnix_push_initializers_tag")] =
ES.wrapAsyncWithSPS<RecordInitializersSPSSig>(
this, &ELFNixPlatform::rt_recordInitializers);
using LookupSymbolSPSSig =
SPSExpected<SPSExecutorAddr>(SPSExecutorAddr, SPSString);
WFs[ES.intern("__orc_rt_elfnix_symbol_lookup_tag")] =
ES.wrapAsyncWithSPS<LookupSymbolSPSSig>(this,
&ELFNixPlatform::rt_lookupSymbol);
return ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
}
void ELFNixPlatform::pushInitializersLoop(
PushInitializersSendResultFn SendResult, JITDylibSP JD) {
DenseMap<JITDylib *, SymbolLookupSet> NewInitSymbols;
DenseMap<JITDylib *, SmallVector<JITDylib *>> JDDepMap;
SmallVector<JITDylib *, 16> Worklist({JD.get()});
ES.runSessionLocked([&]() {
while (!Worklist.empty()) {
// FIXME: Check for defunct dylibs.
auto DepJD = Worklist.back();
Worklist.pop_back();
// If we've already visited this JITDylib on this iteration then continue.
auto [It, Inserted] = JDDepMap.try_emplace(DepJD);
if (!Inserted)
continue;
// Add dep info.
auto &DM = It->second;
DepJD->withLinkOrderDo([&](const JITDylibSearchOrder &O) {
for (auto &KV : O) {
if (KV.first == DepJD)
continue;
DM.push_back(KV.first);
Worklist.push_back(KV.first);
}
});
// Add any registered init symbols.
auto RISItr = RegisteredInitSymbols.find(DepJD);
if (RISItr != RegisteredInitSymbols.end()) {
NewInitSymbols[DepJD] = std::move(RISItr->second);
RegisteredInitSymbols.erase(RISItr);
}
}
});
// 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()) {
// To make the list intelligible to the runtime we need to convert all
// JITDylib pointers to their header addresses. Only include JITDylibs
// that appear in the JITDylibToHandleAddr map (i.e. those that have been
// through setupJITDylib) -- bare JITDylibs aren't managed by the platform.
DenseMap<JITDylib *, ExecutorAddr> HeaderAddrs;
HeaderAddrs.reserve(JDDepMap.size());
{
std::lock_guard<std::mutex> Lock(PlatformMutex);
for (auto &KV : JDDepMap) {
auto I = JITDylibToHandleAddr.find(KV.first);
if (I != JITDylibToHandleAddr.end())
HeaderAddrs[KV.first] = I->second;
}
}
// Build the dep info map to return.
ELFNixJITDylibDepInfoMap DIM;
DIM.reserve(JDDepMap.size());
for (auto &KV : JDDepMap) {
auto HI = HeaderAddrs.find(KV.first);
// Skip unmanaged JITDylibs.
if (HI == HeaderAddrs.end())
continue;
auto H = HI->second;
ELFNixJITDylibDepInfo DepInfo;
for (auto &Dep : KV.second) {
auto HJ = HeaderAddrs.find(Dep);
if (HJ != HeaderAddrs.end())
DepInfo.push_back(HJ->second);
}
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](Error Err) mutable {
if (Err)
SendResult(std::move(Err));
else
pushInitializersLoop(std::move(SendResult), JD);
},
ES, std::move(NewInitSymbols));
}
void ELFNixPlatform::rt_recordInitializers(
PushInitializersSendResultFn SendResult, ExecutorAddr JDHeaderAddr) {
JITDylibSP JD;
{
std::lock_guard<std::mutex> Lock(PlatformMutex);
auto I = HandleAddrToJITDylib.find(JDHeaderAddr);
if (I != HandleAddrToJITDylib.end())
JD = I->second;
}
LLVM_DEBUG({
dbgs() << "ELFNixPlatform::rt_recordInitializers(" << 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;
}
pushInitializersLoop(std::move(SendResult), JD);
}
void ELFNixPlatform::rt_lookupSymbol(SendSymbolAddressFn SendResult,
ExecutorAddr Handle,
StringRef SymbolName) {
LLVM_DEBUG({
dbgs() << "ELFNixPlatform::rt_lookupSymbol(\"" << Handle << "\")\n";
});
JITDylib *JD = nullptr;
{
std::lock_guard<std::mutex> Lock(PlatformMutex);
auto I = HandleAddrToJITDylib.find(Handle);
if (I != HandleAddrToJITDylib.end())
JD = I->second;
}
if (!JD) {
LLVM_DEBUG(dbgs() << " No JITDylib for handle " << 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 ELFNixPlatform::ELFNixPlatformPlugin::bootstrapPipelineStart(
jitlink::LinkGraph &G) {
// Increment the active graphs count in BootstrapInfo.
std::lock_guard<std::mutex> Lock(MP.Bootstrap.load()->Mutex);
++MP.Bootstrap.load()->ActiveGraphs;
return Error::success();
}
Error ELFNixPlatform::ELFNixPlatformPlugin::
bootstrapPipelineRecordRuntimeFunctions(jitlink::LinkGraph &G) {
// Record bootstrap function names.
std::pair<StringRef, ExecutorAddr *> RuntimeSymbols[] = {
{*MP.DSOHandleSymbol, &MP.Bootstrap.load()->ELFNixHeaderAddr},
{*MP.PlatformBootstrap.Name, &MP.PlatformBootstrap.Addr},
{*MP.PlatformShutdown.Name, &MP.PlatformShutdown.Addr},
{*MP.RegisterJITDylib.Name, &MP.RegisterJITDylib.Addr},
{*MP.DeregisterJITDylib.Name, &MP.DeregisterJITDylib.Addr},
{*MP.RegisterObjectSections.Name, &MP.RegisterObjectSections.Addr},
{*MP.DeregisterObjectSections.Name, &MP.DeregisterObjectSections.Addr},
{*MP.RegisterInitSections.Name, &MP.RegisterInitSections.Addr},
{*MP.DeregisterInitSections.Name, &MP.DeregisterInitSections.Addr},
{*MP.CreatePThreadKey.Name, &MP.CreatePThreadKey.Addr}};
bool RegisterELFNixHeader = false;
for (auto *Sym : G.defined_symbols()) {
for (auto &RTSym : RuntimeSymbols) {
if (Sym->hasName() && *Sym->getName() == RTSym.first) {
if (*RTSym.second)
return make_error<StringError>(
"Duplicate " + RTSym.first +
" detected during ELFNixPlatform bootstrap",
inconvertibleErrorCode());
if (*Sym->getName() == *MP.DSOHandleSymbol)
RegisterELFNixHeader = true;
*RTSym.second = Sym->getAddress();
}
}
}
if (RegisterELFNixHeader) {
// If this graph defines the elfnix header symbol then create the internal
// mapping between it and PlatformJD.
std::lock_guard<std::mutex> Lock(MP.PlatformMutex);
MP.JITDylibToHandleAddr[&MP.PlatformJD] =
MP.Bootstrap.load()->ELFNixHeaderAddr;
MP.HandleAddrToJITDylib[MP.Bootstrap.load()->ELFNixHeaderAddr] =
&MP.PlatformJD;
}
return Error::success();
}
Error ELFNixPlatform::ELFNixPlatformPlugin::bootstrapPipelineEnd(
jitlink::LinkGraph &G) {
std::lock_guard<std::mutex> Lock(MP.Bootstrap.load()->Mutex);
assert(MP.Bootstrap && "DeferredAAs reset before bootstrap completed");
--MP.Bootstrap.load()->ActiveGraphs;
// Notify Bootstrap->CV while holding the mutex because the mutex is
// also keeping Bootstrap->CV alive.
if (MP.Bootstrap.load()->ActiveGraphs == 0)
MP.Bootstrap.load()->CV.notify_all();
return Error::success();
}
Error ELFNixPlatform::registerPerObjectSections(
jitlink::LinkGraph &G, const ELFPerObjectSectionsToRegister &POSR,
bool IsBootstrapping) {
using SPSRegisterPerObjSectionsArgs =
SPSArgList<SPSELFPerObjectSectionsToRegister>;
if (LLVM_UNLIKELY(IsBootstrapping)) {
Bootstrap.load()->addArgumentsToRTFnMap(
&RegisterObjectSections, &DeregisterObjectSections,
getArgDataBufferType<SPSRegisterPerObjSectionsArgs>(POSR),
getArgDataBufferType<SPSRegisterPerObjSectionsArgs>(POSR));
return Error::success();
}
G.allocActions().push_back(
{cantFail(WrapperFunctionCall::Create<SPSRegisterPerObjSectionsArgs>(
RegisterObjectSections.Addr, POSR)),
cantFail(WrapperFunctionCall::Create<SPSRegisterPerObjSectionsArgs>(
DeregisterObjectSections.Addr, POSR))});
return Error::success();
}
Expected<uint64_t> ELFNixPlatform::createPThreadKey() {
if (!CreatePThreadKey.Addr)
return make_error<StringError>(
"Attempting to create pthread key in target, but runtime support has "
"not been loaded yet",
inconvertibleErrorCode());
Expected<uint64_t> Result(0);
if (auto Err = ES.callSPSWrapper<SPSExpected<uint64_t>(void)>(
CreatePThreadKey.Addr, Result))
return std::move(Err);
return Result;
}
void ELFNixPlatform::ELFNixPlatformPlugin::modifyPassConfig(
MaterializationResponsibility &MR, jitlink::LinkGraph &LG,
jitlink::PassConfiguration &Config) {
using namespace jitlink;
bool InBootstrapPhase =
&MR.getTargetJITDylib() == &MP.PlatformJD && MP.Bootstrap;
// If we're in the bootstrap phase then increment the active graphs.
if (InBootstrapPhase) {
Config.PrePrunePasses.push_back(
[this](LinkGraph &G) { return bootstrapPipelineStart(G); });
Config.PostAllocationPasses.push_back([this](LinkGraph &G) {
return bootstrapPipelineRecordRuntimeFunctions(G);
});
}
// If the initializer symbol is the __dso_handle symbol then just add
// the DSO handle support passes.
if (auto InitSymbol = MR.getInitializerSymbol()) {
if (InitSymbol == MP.DSOHandleSymbol && !InBootstrapPhase) {
addDSOHandleSupportPasses(MR, Config);
// The DSOHandle materialization unit doesn't require any other
// support, so we can bail out early.
return;
}
/// Preserve init sections.
Config.PrePrunePasses.push_back(
[this, &MR](jitlink::LinkGraph &G) -> Error {
if (auto Err = preserveInitSections(G, MR))
return Err;
return Error::success();
});
}
// Add passes for eh-frame and TLV support.
addEHAndTLVSupportPasses(MR, Config, InBootstrapPhase);
// If the object contains initializers then add passes to record them.
Config.PostFixupPasses.push_back([this, &JD = MR.getTargetJITDylib(),
InBootstrapPhase](jitlink::LinkGraph &G) {
return registerInitSections(G, JD, InBootstrapPhase);
});
// If we're in the bootstrap phase then steal allocation actions and then
// decrement the active graphs.
if (InBootstrapPhase)
Config.PostFixupPasses.push_back(
[this](LinkGraph &G) { return bootstrapPipelineEnd(G); });
}
void ELFNixPlatform::ELFNixPlatformPlugin::addDSOHandleSupportPasses(
MaterializationResponsibility &MR, jitlink::PassConfiguration &Config) {
Config.PostAllocationPasses.push_back([this, &JD = MR.getTargetJITDylib()](
jitlink::LinkGraph &G) -> Error {
auto I = llvm::find_if(G.defined_symbols(), [this](jitlink::Symbol *Sym) {
return Sym->getName() == MP.DSOHandleSymbol;
});
assert(I != G.defined_symbols().end() && "Missing DSO handle symbol");
{
std::lock_guard<std::mutex> Lock(MP.PlatformMutex);
auto HandleAddr = (*I)->getAddress();
MP.HandleAddrToJITDylib[HandleAddr] = &JD;
MP.JITDylibToHandleAddr[&JD] = HandleAddr;
G.allocActions().push_back(
{cantFail(WrapperFunctionCall::Create<
SPSArgList<SPSString, SPSExecutorAddr>>(
MP.RegisterJITDylib.Addr, JD.getName(), HandleAddr)),
cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
MP.DeregisterJITDylib.Addr, HandleAddr))});
}
return Error::success();
});
}
void ELFNixPlatform::ELFNixPlatformPlugin::addEHAndTLVSupportPasses(
MaterializationResponsibility &MR, jitlink::PassConfiguration &Config,
bool IsBootstrapping) {
// Insert TLV lowering at the start of the PostPrunePasses, since we want
// it to run before GOT/PLT lowering.
// TODO: Check that before the fixTLVSectionsAndEdges pass, the GOT/PLT build
// pass has done. Because the TLS descriptor need to be allocate in GOT.
Config.PostPrunePasses.push_back(
[this, &JD = MR.getTargetJITDylib()](jitlink::LinkGraph &G) {
return fixTLVSectionsAndEdges(G, JD);
});
// Add a pass to register the final addresses of the eh-frame and TLV sections
// with the runtime.
Config.PostFixupPasses.push_back([this, IsBootstrapping](
jitlink::LinkGraph &G) -> Error {
ELFPerObjectSectionsToRegister POSR;
if (auto *EHFrameSection = G.findSectionByName(ELFEHFrameSectionName)) {
jitlink::SectionRange R(*EHFrameSection);
if (!R.empty())
POSR.EHFrameSection = R.getRange();
}
// Get a pointer to the thread data section if there is one. It will be used
// below.
jitlink::Section *ThreadDataSection =
G.findSectionByName(ELFThreadDataSectionName);
// Handle thread BSS section if there is one.
if (auto *ThreadBSSSection = G.findSectionByName(ELFThreadBSSSectionName)) {
// If there's already a thread data section in this graph then merge the
// thread BSS section content into it, otherwise just treat the thread
// BSS section as the thread data section.
if (ThreadDataSection)
G.mergeSections(*ThreadDataSection, *ThreadBSSSection);
else
ThreadDataSection = ThreadBSSSection;
}
// Having merged thread BSS (if present) and thread data (if present),
// record the resulting section range.
if (ThreadDataSection) {
jitlink::SectionRange R(*ThreadDataSection);
if (!R.empty())
POSR.ThreadDataSection = R.getRange();
}
if (POSR.EHFrameSection.Start || POSR.ThreadDataSection.Start) {
if (auto Err = MP.registerPerObjectSections(G, POSR, IsBootstrapping))
return Err;
}
return Error::success();
});
}
Error ELFNixPlatform::ELFNixPlatformPlugin::preserveInitSections(
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 (!isELFInitializerSection(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 ELFNixPlatform::ELFNixPlatformPlugin::registerInitSections(
jitlink::LinkGraph &G, JITDylib &JD, bool IsBootstrapping) {
SmallVector<ExecutorAddrRange> ELFNixPlatformSecs;
LLVM_DEBUG(dbgs() << "ELFNixPlatform::registerInitSections\n");
SmallVector<jitlink::Section *> OrderedInitSections;
for (auto &Sec : G.sections())
if (isELFInitializerSection(Sec.getName()))
OrderedInitSections.push_back(&Sec);
// FIXME: This handles priority order within the current graph, but we'll need
// to include priority information in the initializer allocation
// actions in order to respect the ordering across multiple graphs.
llvm::sort(OrderedInitSections, [](const jitlink::Section *LHS,
const jitlink::Section *RHS) {
if (LHS->getName().starts_with(".init_array")) {
if (RHS->getName().starts_with(".init_array")) {
StringRef LHSPrioStr(LHS->getName());
StringRef RHSPrioStr(RHS->getName());
uint64_t LHSPriority;
bool LHSHasPriority = LHSPrioStr.consume_front(".init_array.") &&
!LHSPrioStr.getAsInteger(10, LHSPriority);
uint64_t RHSPriority;
bool RHSHasPriority = RHSPrioStr.consume_front(".init_array.") &&
!RHSPrioStr.getAsInteger(10, RHSPriority);
if (LHSHasPriority)
return RHSHasPriority ? LHSPriority < RHSPriority : true;
else if (RHSHasPriority)
return false;
// If we get here we'll fall through to the
// LHS->getName() < RHS->getName() test below.
} else {
// .init_array[.N] comes before any non-.init_array[.N] section.
return true;
}
}
return LHS->getName() < RHS->getName();
});
for (auto &Sec : OrderedInitSections)
ELFNixPlatformSecs.push_back(jitlink::SectionRange(*Sec).getRange());
// Dump the scraped inits.
LLVM_DEBUG({
dbgs() << "ELFNixPlatform: Scraped " << G.getName() << " init sections:\n";
for (auto &Sec : G.sections()) {
jitlink::SectionRange R(Sec);
dbgs() << " " << Sec.getName() << ": " << R.getRange() << "\n";
}
});
ExecutorAddr HeaderAddr;
{
std::lock_guard<std::mutex> Lock(MP.PlatformMutex);
auto I = MP.JITDylibToHandleAddr.find(&JD);
assert(I != MP.JITDylibToHandleAddr.end() && "No header registered for JD");
assert(I->second && "Null header registered for JD");
HeaderAddr = I->second;
}
using SPSRegisterInitSectionsArgs =
SPSArgList<SPSExecutorAddr, SPSSequence<SPSExecutorAddrRange>>;
if (LLVM_UNLIKELY(IsBootstrapping)) {
MP.Bootstrap.load()->addArgumentsToRTFnMap(
&MP.RegisterInitSections, &MP.DeregisterInitSections,
getArgDataBufferType<SPSRegisterInitSectionsArgs>(HeaderAddr,
ELFNixPlatformSecs),
getArgDataBufferType<SPSRegisterInitSectionsArgs>(HeaderAddr,
ELFNixPlatformSecs));
return Error::success();
}
G.allocActions().push_back(
{cantFail(WrapperFunctionCall::Create<SPSRegisterInitSectionsArgs>(
MP.RegisterInitSections.Addr, HeaderAddr, ELFNixPlatformSecs)),
cantFail(WrapperFunctionCall::Create<SPSRegisterInitSectionsArgs>(
MP.DeregisterInitSections.Addr, HeaderAddr, ELFNixPlatformSecs))});
return Error::success();
}
Error ELFNixPlatform::ELFNixPlatformPlugin::fixTLVSectionsAndEdges(
jitlink::LinkGraph &G, JITDylib &JD) {
auto TLSGetAddrSymbolName = G.intern("__tls_get_addr");
auto TLSDescResolveSymbolName = G.intern("__tlsdesc_resolver");
for (auto *Sym : G.external_symbols()) {
if (Sym->getName() == TLSGetAddrSymbolName) {
auto TLSGetAddr =
MP.getExecutionSession().intern("___orc_rt_elfnix_tls_get_addr");
Sym->setName(std::move(TLSGetAddr));
} else if (Sym->getName() == TLSDescResolveSymbolName) {
auto TLSGetAddr =
MP.getExecutionSession().intern("___orc_rt_elfnix_tlsdesc_resolver");
Sym->setName(std::move(TLSGetAddr));
}
}
auto *TLSInfoEntrySection = G.findSectionByName("$__TLSINFO");
if (TLSInfoEntrySection) {
std::optional<uint64_t> Key;
{
std::lock_guard<std::mutex> Lock(MP.PlatformMutex);
auto I = MP.JITDylibToPThreadKey.find(&JD);
if (I != MP.JITDylibToPThreadKey.end())
Key = I->second;
}
if (!Key) {
if (auto KeyOrErr = MP.createPThreadKey())
Key = *KeyOrErr;
else
return KeyOrErr.takeError();
}
uint64_t PlatformKeyBits =
support::endian::byte_swap(*Key, G.getEndianness());
for (auto *B : TLSInfoEntrySection->blocks()) {
// FIXME: The TLS descriptor byte length may different with different
// ISA
assert(B->getSize() == (G.getPointerSize() * 2) &&
"TLS descriptor must be 2 words length");
auto TLSInfoEntryContent = B->getMutableContent(G);
memcpy(TLSInfoEntryContent.data(), &PlatformKeyBits, G.getPointerSize());
}
}
return Error::success();
}
} // End namespace orc.
} // End namespace llvm.