
Previously we stripped Weak flags from JITDylib symbol table entries once they were resolved (there was no particularly good reason for this). Now we want to retain them and query them when setting the Linkage on external symbols in LinkGraphs during symbol resolution (this was the motivation for 75404e9ef88). Making weak linkage of external definitions discoverable in the LinkGraph will in turn allow future plugins to implement correct handling for them (by recording locations that depend on exported weak definitions and pointing all of these at one chosen definition at runtime).
395 lines
12 KiB
C++
395 lines
12 KiB
C++
//===-- RTDyldObjectLinkingLayer.cpp - RuntimeDyld backed ORC ObjectLayer -===//
|
|
//
|
|
// 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/RTDyldObjectLinkingLayer.h"
|
|
#include "llvm/Object/COFF.h"
|
|
|
|
namespace {
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::orc;
|
|
|
|
class JITDylibSearchOrderResolver : public JITSymbolResolver {
|
|
public:
|
|
JITDylibSearchOrderResolver(MaterializationResponsibility &MR) : MR(MR) {}
|
|
|
|
void lookup(const LookupSet &Symbols, OnResolvedFunction OnResolved) override {
|
|
auto &ES = MR.getTargetJITDylib().getExecutionSession();
|
|
SymbolLookupSet InternedSymbols;
|
|
|
|
// Intern the requested symbols: lookup takes interned strings.
|
|
for (auto &S : Symbols)
|
|
InternedSymbols.add(ES.intern(S));
|
|
|
|
// Build an OnResolve callback to unwrap the interned strings and pass them
|
|
// to the OnResolved callback.
|
|
auto OnResolvedWithUnwrap =
|
|
[OnResolved = std::move(OnResolved)](
|
|
Expected<SymbolMap> InternedResult) mutable {
|
|
if (!InternedResult) {
|
|
OnResolved(InternedResult.takeError());
|
|
return;
|
|
}
|
|
|
|
LookupResult Result;
|
|
for (auto &KV : *InternedResult)
|
|
Result[*KV.first] = std::move(KV.second);
|
|
OnResolved(Result);
|
|
};
|
|
|
|
// Register dependencies for all symbols contained in this set.
|
|
auto RegisterDependencies = [&](const SymbolDependenceMap &Deps) {
|
|
MR.addDependenciesForAll(Deps);
|
|
};
|
|
|
|
JITDylibSearchOrder LinkOrder;
|
|
MR.getTargetJITDylib().withLinkOrderDo(
|
|
[&](const JITDylibSearchOrder &LO) { LinkOrder = LO; });
|
|
ES.lookup(LookupKind::Static, LinkOrder, InternedSymbols,
|
|
SymbolState::Resolved, std::move(OnResolvedWithUnwrap),
|
|
RegisterDependencies);
|
|
}
|
|
|
|
Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) override {
|
|
LookupSet Result;
|
|
|
|
for (auto &KV : MR.getSymbols()) {
|
|
if (Symbols.count(*KV.first))
|
|
Result.insert(*KV.first);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
private:
|
|
MaterializationResponsibility &MR;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
namespace llvm {
|
|
namespace orc {
|
|
|
|
char RTDyldObjectLinkingLayer::ID;
|
|
|
|
using BaseT = RTTIExtends<RTDyldObjectLinkingLayer, ObjectLayer>;
|
|
|
|
RTDyldObjectLinkingLayer::RTDyldObjectLinkingLayer(
|
|
ExecutionSession &ES, GetMemoryManagerFunction GetMemoryManager)
|
|
: BaseT(ES), GetMemoryManager(GetMemoryManager) {
|
|
ES.registerResourceManager(*this);
|
|
}
|
|
|
|
RTDyldObjectLinkingLayer::~RTDyldObjectLinkingLayer() {
|
|
assert(MemMgrs.empty() && "Layer destroyed with resources still attached");
|
|
}
|
|
|
|
void RTDyldObjectLinkingLayer::emit(
|
|
std::unique_ptr<MaterializationResponsibility> R,
|
|
std::unique_ptr<MemoryBuffer> O) {
|
|
assert(O && "Object must not be null");
|
|
|
|
auto &ES = getExecutionSession();
|
|
|
|
auto Obj = object::ObjectFile::createObjectFile(*O);
|
|
|
|
if (!Obj) {
|
|
getExecutionSession().reportError(Obj.takeError());
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
// Collect the internal symbols from the object file: We will need to
|
|
// filter these later.
|
|
auto InternalSymbols = std::make_shared<std::set<StringRef>>();
|
|
{
|
|
SymbolFlagsMap ExtraSymbolsToClaim;
|
|
for (auto &Sym : (*Obj)->symbols()) {
|
|
|
|
// Skip file symbols.
|
|
if (auto SymType = Sym.getType()) {
|
|
if (*SymType == object::SymbolRef::ST_File)
|
|
continue;
|
|
} else {
|
|
ES.reportError(SymType.takeError());
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
Expected<uint32_t> SymFlagsOrErr = Sym.getFlags();
|
|
if (!SymFlagsOrErr) {
|
|
// TODO: Test this error.
|
|
ES.reportError(SymFlagsOrErr.takeError());
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
// Try to claim responsibility of weak symbols
|
|
// if AutoClaimObjectSymbols flag is set.
|
|
if (AutoClaimObjectSymbols &&
|
|
(*SymFlagsOrErr & object::BasicSymbolRef::SF_Weak)) {
|
|
auto SymName = Sym.getName();
|
|
if (!SymName) {
|
|
ES.reportError(SymName.takeError());
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
// Already included in responsibility set, skip it
|
|
SymbolStringPtr SymbolName = ES.intern(*SymName);
|
|
if (R->getSymbols().count(SymbolName))
|
|
continue;
|
|
|
|
auto SymFlags = JITSymbolFlags::fromObjectSymbol(Sym);
|
|
if (!SymFlags) {
|
|
ES.reportError(SymFlags.takeError());
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
|
|
ExtraSymbolsToClaim[SymbolName] = *SymFlags;
|
|
continue;
|
|
}
|
|
|
|
// Don't include symbols that aren't global.
|
|
if (!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global)) {
|
|
if (auto SymName = Sym.getName())
|
|
InternalSymbols->insert(*SymName);
|
|
else {
|
|
ES.reportError(SymName.takeError());
|
|
R->failMaterialization();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ExtraSymbolsToClaim.empty()) {
|
|
if (auto Err = R->defineMaterializing(ExtraSymbolsToClaim)) {
|
|
ES.reportError(std::move(Err));
|
|
R->failMaterialization();
|
|
}
|
|
}
|
|
}
|
|
|
|
auto MemMgr = GetMemoryManager();
|
|
auto &MemMgrRef = *MemMgr;
|
|
|
|
// Switch to shared ownership of MR so that it can be captured by both
|
|
// lambdas below.
|
|
std::shared_ptr<MaterializationResponsibility> SharedR(std::move(R));
|
|
|
|
JITDylibSearchOrderResolver Resolver(*SharedR);
|
|
|
|
jitLinkForORC(
|
|
object::OwningBinary<object::ObjectFile>(std::move(*Obj), std::move(O)),
|
|
MemMgrRef, Resolver, ProcessAllSections,
|
|
[this, SharedR, &MemMgrRef, InternalSymbols](
|
|
const object::ObjectFile &Obj,
|
|
RuntimeDyld::LoadedObjectInfo &LoadedObjInfo,
|
|
std::map<StringRef, JITEvaluatedSymbol> ResolvedSymbols) {
|
|
return onObjLoad(*SharedR, Obj, MemMgrRef, LoadedObjInfo,
|
|
ResolvedSymbols, *InternalSymbols);
|
|
},
|
|
[this, SharedR, MemMgr = std::move(MemMgr)](
|
|
object::OwningBinary<object::ObjectFile> Obj,
|
|
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
|
|
Error Err) mutable {
|
|
onObjEmit(*SharedR, std::move(Obj), std::move(MemMgr),
|
|
std::move(LoadedObjInfo), std::move(Err));
|
|
});
|
|
}
|
|
|
|
void RTDyldObjectLinkingLayer::registerJITEventListener(JITEventListener &L) {
|
|
std::lock_guard<std::mutex> Lock(RTDyldLayerMutex);
|
|
assert(!llvm::is_contained(EventListeners, &L) &&
|
|
"Listener has already been registered");
|
|
EventListeners.push_back(&L);
|
|
}
|
|
|
|
void RTDyldObjectLinkingLayer::unregisterJITEventListener(JITEventListener &L) {
|
|
std::lock_guard<std::mutex> Lock(RTDyldLayerMutex);
|
|
auto I = llvm::find(EventListeners, &L);
|
|
assert(I != EventListeners.end() && "Listener not registered");
|
|
EventListeners.erase(I);
|
|
}
|
|
|
|
Error RTDyldObjectLinkingLayer::onObjLoad(
|
|
MaterializationResponsibility &R, const object::ObjectFile &Obj,
|
|
RuntimeDyld::MemoryManager &MemMgr,
|
|
RuntimeDyld::LoadedObjectInfo &LoadedObjInfo,
|
|
std::map<StringRef, JITEvaluatedSymbol> Resolved,
|
|
std::set<StringRef> &InternalSymbols) {
|
|
SymbolFlagsMap ExtraSymbolsToClaim;
|
|
SymbolMap Symbols;
|
|
|
|
// Hack to support COFF constant pool comdats introduced during compilation:
|
|
// (See http://llvm.org/PR40074)
|
|
if (auto *COFFObj = dyn_cast<object::COFFObjectFile>(&Obj)) {
|
|
auto &ES = getExecutionSession();
|
|
|
|
// For all resolved symbols that are not already in the responsibilty set:
|
|
// check whether the symbol is in a comdat section and if so mark it as
|
|
// weak.
|
|
for (auto &Sym : COFFObj->symbols()) {
|
|
// getFlags() on COFF symbols can't fail.
|
|
uint32_t SymFlags = cantFail(Sym.getFlags());
|
|
if (SymFlags & object::BasicSymbolRef::SF_Undefined)
|
|
continue;
|
|
auto Name = Sym.getName();
|
|
if (!Name)
|
|
return Name.takeError();
|
|
auto I = Resolved.find(*Name);
|
|
|
|
// Skip unresolved symbols, internal symbols, and symbols that are
|
|
// already in the responsibility set.
|
|
if (I == Resolved.end() || InternalSymbols.count(*Name) ||
|
|
R.getSymbols().count(ES.intern(*Name)))
|
|
continue;
|
|
auto Sec = Sym.getSection();
|
|
if (!Sec)
|
|
return Sec.takeError();
|
|
if (*Sec == COFFObj->section_end())
|
|
continue;
|
|
auto &COFFSec = *COFFObj->getCOFFSection(**Sec);
|
|
if (COFFSec.Characteristics & COFF::IMAGE_SCN_LNK_COMDAT)
|
|
I->second.setFlags(I->second.getFlags() | JITSymbolFlags::Weak);
|
|
}
|
|
}
|
|
|
|
for (auto &KV : Resolved) {
|
|
// Scan the symbols and add them to the Symbols map for resolution.
|
|
|
|
// We never claim internal symbols.
|
|
if (InternalSymbols.count(KV.first))
|
|
continue;
|
|
|
|
auto InternedName = getExecutionSession().intern(KV.first);
|
|
auto Flags = KV.second.getFlags();
|
|
auto I = R.getSymbols().find(InternedName);
|
|
if (I != R.getSymbols().end()) {
|
|
// Override object flags and claim responsibility for symbols if
|
|
// requested.
|
|
if (OverrideObjectFlags)
|
|
Flags = I->second;
|
|
else {
|
|
// RuntimeDyld/MCJIT's weak tracking isn't compatible with ORC's. Even
|
|
// if we're not overriding flags in general we should set the weak flag
|
|
// according to the MaterializationResponsibility object symbol table.
|
|
if (I->second.isWeak())
|
|
Flags |= JITSymbolFlags::Weak;
|
|
}
|
|
} else if (AutoClaimObjectSymbols)
|
|
ExtraSymbolsToClaim[InternedName] = Flags;
|
|
|
|
Symbols[InternedName] = JITEvaluatedSymbol(KV.second.getAddress(), Flags);
|
|
}
|
|
|
|
if (!ExtraSymbolsToClaim.empty()) {
|
|
if (auto Err = R.defineMaterializing(ExtraSymbolsToClaim))
|
|
return Err;
|
|
|
|
// If we claimed responsibility for any weak symbols but were rejected then
|
|
// we need to remove them from the resolved set.
|
|
for (auto &KV : ExtraSymbolsToClaim)
|
|
if (KV.second.isWeak() && !R.getSymbols().count(KV.first))
|
|
Symbols.erase(KV.first);
|
|
}
|
|
|
|
if (auto Err = R.notifyResolved(Symbols)) {
|
|
R.failMaterialization();
|
|
return Err;
|
|
}
|
|
|
|
if (NotifyLoaded)
|
|
NotifyLoaded(R, Obj, LoadedObjInfo);
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
void RTDyldObjectLinkingLayer::onObjEmit(
|
|
MaterializationResponsibility &R,
|
|
object::OwningBinary<object::ObjectFile> O,
|
|
std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr,
|
|
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo, Error Err) {
|
|
if (Err) {
|
|
getExecutionSession().reportError(std::move(Err));
|
|
R.failMaterialization();
|
|
return;
|
|
}
|
|
|
|
if (auto Err = R.notifyEmitted()) {
|
|
getExecutionSession().reportError(std::move(Err));
|
|
R.failMaterialization();
|
|
return;
|
|
}
|
|
|
|
std::unique_ptr<object::ObjectFile> Obj;
|
|
std::unique_ptr<MemoryBuffer> ObjBuffer;
|
|
std::tie(Obj, ObjBuffer) = O.takeBinary();
|
|
|
|
// Run EventListener notifyLoaded callbacks.
|
|
{
|
|
std::lock_guard<std::mutex> Lock(RTDyldLayerMutex);
|
|
for (auto *L : EventListeners)
|
|
L->notifyObjectLoaded(pointerToJITTargetAddress(MemMgr.get()), *Obj,
|
|
*LoadedObjInfo);
|
|
}
|
|
|
|
if (NotifyEmitted)
|
|
NotifyEmitted(R, std::move(ObjBuffer));
|
|
|
|
if (auto Err = R.withResourceKeyDo(
|
|
[&](ResourceKey K) { MemMgrs[K].push_back(std::move(MemMgr)); })) {
|
|
getExecutionSession().reportError(std::move(Err));
|
|
R.failMaterialization();
|
|
}
|
|
}
|
|
|
|
Error RTDyldObjectLinkingLayer::handleRemoveResources(ResourceKey K) {
|
|
|
|
std::vector<MemoryManagerUP> MemMgrsToRemove;
|
|
|
|
getExecutionSession().runSessionLocked([&] {
|
|
auto I = MemMgrs.find(K);
|
|
if (I != MemMgrs.end()) {
|
|
std::swap(MemMgrsToRemove, I->second);
|
|
MemMgrs.erase(I);
|
|
}
|
|
});
|
|
|
|
{
|
|
std::lock_guard<std::mutex> Lock(RTDyldLayerMutex);
|
|
for (auto &MemMgr : MemMgrsToRemove) {
|
|
for (auto *L : EventListeners)
|
|
L->notifyFreeingObject(pointerToJITTargetAddress(MemMgr.get()));
|
|
MemMgr->deregisterEHFrames();
|
|
}
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
void RTDyldObjectLinkingLayer::handleTransferResources(ResourceKey DstKey,
|
|
ResourceKey SrcKey) {
|
|
auto I = MemMgrs.find(SrcKey);
|
|
if (I != MemMgrs.end()) {
|
|
auto &SrcMemMgrs = I->second;
|
|
auto &DstMemMgrs = MemMgrs[DstKey];
|
|
DstMemMgrs.reserve(DstMemMgrs.size() + SrcMemMgrs.size());
|
|
for (auto &MemMgr : SrcMemMgrs)
|
|
DstMemMgrs.push_back(std::move(MemMgr));
|
|
|
|
// Erase SrcKey entry using value rather than iterator I: I may have been
|
|
// invalidated when we looked up DstKey.
|
|
MemMgrs.erase(SrcKey);
|
|
}
|
|
}
|
|
|
|
} // End namespace orc.
|
|
} // End namespace llvm.
|