mirror of
https://github.com/RPCS3/llvm.git
synced 2026-01-31 01:25:19 +01:00
Clients who want to regain ownership of object buffers after they have been linked may now use the NotifyEmitted callback for this purpose. Note: Currently NotifyEmitted is only called if linking succeeds. If linking fails the buffer is always discarded. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@359735 91177308-0d34-0410-b5e6-96231b3b80d8
473 lines
18 KiB
C++
473 lines
18 KiB
C++
//===- RTDyldObjectLinkingLayer.h - RTDyld-based jit linking ---*- C++ -*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Contains the definition for an RTDyld-based, in-process object linking layer.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H
|
|
#define LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ExecutionEngine/JITSymbol.h"
|
|
#include "llvm/ExecutionEngine/Orc/Core.h"
|
|
#include "llvm/ExecutionEngine/Orc/Layer.h"
|
|
#include "llvm/ExecutionEngine/Orc/Legacy.h"
|
|
#include "llvm/ExecutionEngine/RuntimeDyld.h"
|
|
#include "llvm/Object/ObjectFile.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <functional>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace llvm {
|
|
namespace orc {
|
|
|
|
class RTDyldObjectLinkingLayer : public ObjectLayer {
|
|
public:
|
|
/// Functor for receiving object-loaded notifications.
|
|
using NotifyLoadedFunction =
|
|
std::function<void(VModuleKey, const object::ObjectFile &Obj,
|
|
const RuntimeDyld::LoadedObjectInfo &)>;
|
|
|
|
/// Functor for receiving finalization notifications.
|
|
using NotifyEmittedFunction =
|
|
std::function<void(VModuleKey, std::unique_ptr<MemoryBuffer>)>;
|
|
|
|
using GetMemoryManagerFunction =
|
|
std::function<std::unique_ptr<RuntimeDyld::MemoryManager>()>;
|
|
|
|
/// Construct an ObjectLinkingLayer with the given NotifyLoaded,
|
|
/// and NotifyEmitted functors.
|
|
RTDyldObjectLinkingLayer(ExecutionSession &ES,
|
|
GetMemoryManagerFunction GetMemoryManager);
|
|
|
|
/// Emit the object.
|
|
void emit(MaterializationResponsibility R,
|
|
std::unique_ptr<MemoryBuffer> O) override;
|
|
|
|
/// Set the NotifyLoaded callback.
|
|
RTDyldObjectLinkingLayer &setNotifyLoaded(NotifyLoadedFunction NotifyLoaded) {
|
|
this->NotifyLoaded = std::move(NotifyLoaded);
|
|
return *this;
|
|
}
|
|
|
|
/// Set the NotifyEmitted callback.
|
|
RTDyldObjectLinkingLayer &
|
|
setNotifyEmitted(NotifyEmittedFunction NotifyEmitted) {
|
|
this->NotifyEmitted = std::move(NotifyEmitted);
|
|
return *this;
|
|
}
|
|
|
|
/// Set the 'ProcessAllSections' flag.
|
|
///
|
|
/// If set to true, all sections in each object file will be allocated using
|
|
/// the memory manager, rather than just the sections required for execution.
|
|
///
|
|
/// This is kludgy, and may be removed in the future.
|
|
RTDyldObjectLinkingLayer &setProcessAllSections(bool ProcessAllSections) {
|
|
this->ProcessAllSections = ProcessAllSections;
|
|
return *this;
|
|
}
|
|
|
|
/// Instructs this RTDyldLinkingLayer2 instance to override the symbol flags
|
|
/// returned by RuntimeDyld for any given object file with the flags supplied
|
|
/// by the MaterializationResponsibility instance. This is a workaround to
|
|
/// support symbol visibility in COFF, which does not use the libObject's
|
|
/// SF_Exported flag. Use only when generating / adding COFF object files.
|
|
///
|
|
/// FIXME: We should be able to remove this if/when COFF properly tracks
|
|
/// exported symbols.
|
|
RTDyldObjectLinkingLayer &
|
|
setOverrideObjectFlagsWithResponsibilityFlags(bool OverrideObjectFlags) {
|
|
this->OverrideObjectFlags = OverrideObjectFlags;
|
|
return *this;
|
|
}
|
|
|
|
/// If set, this RTDyldObjectLinkingLayer instance will claim responsibility
|
|
/// for any symbols provided by a given object file that were not already in
|
|
/// the MaterializationResponsibility instance. Setting this flag allows
|
|
/// higher-level program representations (e.g. LLVM IR) to be added based on
|
|
/// only a subset of the symbols they provide, without having to write
|
|
/// intervening layers to scan and add the additional symbols. This trades
|
|
/// diagnostic quality for convenience however: If all symbols are enumerated
|
|
/// up-front then clashes can be detected and reported early (and usually
|
|
/// deterministically). If this option is set, clashes for the additional
|
|
/// symbols may not be detected until late, and detection may depend on
|
|
/// the flow of control through JIT'd code. Use with care.
|
|
RTDyldObjectLinkingLayer &
|
|
setAutoClaimResponsibilityForObjectSymbols(bool AutoClaimObjectSymbols) {
|
|
this->AutoClaimObjectSymbols = AutoClaimObjectSymbols;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
Error onObjLoad(VModuleKey K, MaterializationResponsibility &R,
|
|
object::ObjectFile &Obj,
|
|
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> LoadedObjInfo,
|
|
std::map<StringRef, JITEvaluatedSymbol> Resolved,
|
|
std::set<StringRef> &InternalSymbols);
|
|
|
|
void onObjEmit(VModuleKey K, std::unique_ptr<MemoryBuffer> ObjBuffer,
|
|
MaterializationResponsibility &R, Error Err);
|
|
|
|
mutable std::mutex RTDyldLayerMutex;
|
|
GetMemoryManagerFunction GetMemoryManager;
|
|
NotifyLoadedFunction NotifyLoaded;
|
|
NotifyEmittedFunction NotifyEmitted;
|
|
bool ProcessAllSections = false;
|
|
bool OverrideObjectFlags = false;
|
|
bool AutoClaimObjectSymbols = false;
|
|
std::vector<std::unique_ptr<RuntimeDyld::MemoryManager>> MemMgrs;
|
|
};
|
|
|
|
class LegacyRTDyldObjectLinkingLayerBase {
|
|
public:
|
|
using ObjectPtr = std::unique_ptr<MemoryBuffer>;
|
|
|
|
protected:
|
|
|
|
/// Holds an object to be allocated/linked as a unit in the JIT.
|
|
///
|
|
/// An instance of this class will be created for each object added
|
|
/// via JITObjectLayer::addObject. Deleting the instance (via
|
|
/// removeObject) frees its memory, removing all symbol definitions that
|
|
/// had been provided by this instance. Higher level layers are responsible
|
|
/// for taking any action required to handle the missing symbols.
|
|
class LinkedObject {
|
|
public:
|
|
LinkedObject() = default;
|
|
LinkedObject(const LinkedObject&) = delete;
|
|
void operator=(const LinkedObject&) = delete;
|
|
virtual ~LinkedObject() = default;
|
|
|
|
virtual Error finalize() = 0;
|
|
|
|
virtual JITSymbol::GetAddressFtor
|
|
getSymbolMaterializer(std::string Name) = 0;
|
|
|
|
virtual void mapSectionAddress(const void *LocalAddress,
|
|
JITTargetAddress TargetAddr) const = 0;
|
|
|
|
JITSymbol getSymbol(StringRef Name, bool ExportedSymbolsOnly) {
|
|
auto SymEntry = SymbolTable.find(Name);
|
|
if (SymEntry == SymbolTable.end())
|
|
return nullptr;
|
|
if (!SymEntry->second.getFlags().isExported() && ExportedSymbolsOnly)
|
|
return nullptr;
|
|
if (!Finalized)
|
|
return JITSymbol(getSymbolMaterializer(Name),
|
|
SymEntry->second.getFlags());
|
|
return JITSymbol(SymEntry->second);
|
|
}
|
|
|
|
protected:
|
|
StringMap<JITEvaluatedSymbol> SymbolTable;
|
|
bool Finalized = false;
|
|
};
|
|
};
|
|
|
|
/// Bare bones object linking layer.
|
|
///
|
|
/// This class is intended to be used as the base layer for a JIT. It allows
|
|
/// object files to be loaded into memory, linked, and the addresses of their
|
|
/// symbols queried. All objects added to this layer can see each other's
|
|
/// symbols.
|
|
class LegacyRTDyldObjectLinkingLayer : public LegacyRTDyldObjectLinkingLayerBase {
|
|
public:
|
|
|
|
using LegacyRTDyldObjectLinkingLayerBase::ObjectPtr;
|
|
|
|
/// Functor for receiving object-loaded notifications.
|
|
using NotifyLoadedFtor =
|
|
std::function<void(VModuleKey, const object::ObjectFile &Obj,
|
|
const RuntimeDyld::LoadedObjectInfo &)>;
|
|
|
|
/// Functor for receiving finalization notifications.
|
|
using NotifyFinalizedFtor =
|
|
std::function<void(VModuleKey, const object::ObjectFile &Obj,
|
|
const RuntimeDyld::LoadedObjectInfo &)>;
|
|
|
|
/// Functor for receiving deallocation notifications.
|
|
using NotifyFreedFtor = std::function<void(VModuleKey, const object::ObjectFile &Obj)>;
|
|
|
|
private:
|
|
using OwnedObject = object::OwningBinary<object::ObjectFile>;
|
|
|
|
template <typename MemoryManagerPtrT>
|
|
class ConcreteLinkedObject : public LinkedObject {
|
|
public:
|
|
ConcreteLinkedObject(LegacyRTDyldObjectLinkingLayer &Parent, VModuleKey K,
|
|
OwnedObject Obj, MemoryManagerPtrT MemMgr,
|
|
std::shared_ptr<SymbolResolver> Resolver,
|
|
bool ProcessAllSections)
|
|
: K(std::move(K)),
|
|
Parent(Parent),
|
|
MemMgr(std::move(MemMgr)),
|
|
PFC(llvm::make_unique<PreFinalizeContents>(
|
|
std::move(Obj), std::move(Resolver),
|
|
ProcessAllSections)) {
|
|
buildInitialSymbolTable(PFC->Obj);
|
|
}
|
|
|
|
~ConcreteLinkedObject() override {
|
|
if (this->Parent.NotifyFreed && ObjForNotify.getBinary())
|
|
this->Parent.NotifyFreed(K, *ObjForNotify.getBinary());
|
|
|
|
MemMgr->deregisterEHFrames();
|
|
}
|
|
|
|
Error finalize() override {
|
|
assert(PFC && "mapSectionAddress called on finalized LinkedObject");
|
|
|
|
JITSymbolResolverAdapter ResolverAdapter(Parent.ES, *PFC->Resolver,
|
|
nullptr);
|
|
PFC->RTDyld = llvm::make_unique<RuntimeDyld>(*MemMgr, ResolverAdapter);
|
|
PFC->RTDyld->setProcessAllSections(PFC->ProcessAllSections);
|
|
|
|
Finalized = true;
|
|
|
|
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> Info =
|
|
PFC->RTDyld->loadObject(*PFC->Obj.getBinary());
|
|
|
|
// Copy the symbol table out of the RuntimeDyld instance.
|
|
{
|
|
auto SymTab = PFC->RTDyld->getSymbolTable();
|
|
for (auto &KV : SymTab)
|
|
SymbolTable[KV.first] = KV.second;
|
|
}
|
|
|
|
if (Parent.NotifyLoaded)
|
|
Parent.NotifyLoaded(K, *PFC->Obj.getBinary(), *Info);
|
|
|
|
PFC->RTDyld->finalizeWithMemoryManagerLocking();
|
|
|
|
if (PFC->RTDyld->hasError())
|
|
return make_error<StringError>(PFC->RTDyld->getErrorString(),
|
|
inconvertibleErrorCode());
|
|
|
|
if (Parent.NotifyFinalized)
|
|
Parent.NotifyFinalized(K, *PFC->Obj.getBinary(), *Info);
|
|
|
|
// Release resources.
|
|
if (this->Parent.NotifyFreed)
|
|
ObjForNotify = std::move(PFC->Obj); // needed for callback
|
|
PFC = nullptr;
|
|
return Error::success();
|
|
}
|
|
|
|
JITSymbol::GetAddressFtor getSymbolMaterializer(std::string Name) override {
|
|
return [this, Name]() -> Expected<JITTargetAddress> {
|
|
// The symbol may be materialized between the creation of this lambda
|
|
// and its execution, so we need to double check.
|
|
if (!this->Finalized)
|
|
if (auto Err = this->finalize())
|
|
return std::move(Err);
|
|
return this->getSymbol(Name, false).getAddress();
|
|
};
|
|
}
|
|
|
|
void mapSectionAddress(const void *LocalAddress,
|
|
JITTargetAddress TargetAddr) const override {
|
|
assert(PFC && "mapSectionAddress called on finalized LinkedObject");
|
|
assert(PFC->RTDyld && "mapSectionAddress called on raw LinkedObject");
|
|
PFC->RTDyld->mapSectionAddress(LocalAddress, TargetAddr);
|
|
}
|
|
|
|
private:
|
|
void buildInitialSymbolTable(const OwnedObject &Obj) {
|
|
for (auto &Symbol : Obj.getBinary()->symbols()) {
|
|
if (Symbol.getFlags() & object::SymbolRef::SF_Undefined)
|
|
continue;
|
|
Expected<StringRef> SymbolName = Symbol.getName();
|
|
// FIXME: Raise an error for bad symbols.
|
|
if (!SymbolName) {
|
|
consumeError(SymbolName.takeError());
|
|
continue;
|
|
}
|
|
// FIXME: Raise an error for bad symbols.
|
|
auto Flags = JITSymbolFlags::fromObjectSymbol(Symbol);
|
|
if (!Flags) {
|
|
consumeError(Flags.takeError());
|
|
continue;
|
|
}
|
|
SymbolTable.insert(
|
|
std::make_pair(*SymbolName, JITEvaluatedSymbol(0, *Flags)));
|
|
}
|
|
}
|
|
|
|
// Contains the information needed prior to finalization: the object files,
|
|
// memory manager, resolver, and flags needed for RuntimeDyld.
|
|
struct PreFinalizeContents {
|
|
PreFinalizeContents(OwnedObject Obj,
|
|
std::shared_ptr<SymbolResolver> Resolver,
|
|
bool ProcessAllSections)
|
|
: Obj(std::move(Obj)),
|
|
Resolver(std::move(Resolver)),
|
|
ProcessAllSections(ProcessAllSections) {}
|
|
|
|
OwnedObject Obj;
|
|
std::shared_ptr<SymbolResolver> Resolver;
|
|
bool ProcessAllSections;
|
|
std::unique_ptr<RuntimeDyld> RTDyld;
|
|
};
|
|
|
|
VModuleKey K;
|
|
LegacyRTDyldObjectLinkingLayer &Parent;
|
|
MemoryManagerPtrT MemMgr;
|
|
OwnedObject ObjForNotify;
|
|
std::unique_ptr<PreFinalizeContents> PFC;
|
|
};
|
|
|
|
template <typename MemoryManagerPtrT>
|
|
std::unique_ptr<ConcreteLinkedObject<MemoryManagerPtrT>>
|
|
createLinkedObject(LegacyRTDyldObjectLinkingLayer &Parent, VModuleKey K,
|
|
OwnedObject Obj, MemoryManagerPtrT MemMgr,
|
|
std::shared_ptr<SymbolResolver> Resolver,
|
|
bool ProcessAllSections) {
|
|
using LOS = ConcreteLinkedObject<MemoryManagerPtrT>;
|
|
return llvm::make_unique<LOS>(Parent, std::move(K), std::move(Obj),
|
|
std::move(MemMgr), std::move(Resolver),
|
|
ProcessAllSections);
|
|
}
|
|
|
|
public:
|
|
struct Resources {
|
|
std::shared_ptr<RuntimeDyld::MemoryManager> MemMgr;
|
|
std::shared_ptr<SymbolResolver> Resolver;
|
|
};
|
|
|
|
using ResourcesGetter = std::function<Resources(VModuleKey)>;
|
|
|
|
/// Construct an ObjectLinkingLayer with the given NotifyLoaded,
|
|
/// and NotifyFinalized functors.
|
|
LegacyRTDyldObjectLinkingLayer(
|
|
ExecutionSession &ES, ResourcesGetter GetResources,
|
|
NotifyLoadedFtor NotifyLoaded = NotifyLoadedFtor(),
|
|
NotifyFinalizedFtor NotifyFinalized = NotifyFinalizedFtor(),
|
|
NotifyFreedFtor NotifyFreed = NotifyFreedFtor())
|
|
: ES(ES), GetResources(std::move(GetResources)),
|
|
NotifyLoaded(std::move(NotifyLoaded)),
|
|
NotifyFinalized(std::move(NotifyFinalized)),
|
|
NotifyFreed(std::move(NotifyFreed)),
|
|
ProcessAllSections(false) {
|
|
}
|
|
|
|
/// Set the 'ProcessAllSections' flag.
|
|
///
|
|
/// If set to true, all sections in each object file will be allocated using
|
|
/// the memory manager, rather than just the sections required for execution.
|
|
///
|
|
/// This is kludgy, and may be removed in the future.
|
|
void setProcessAllSections(bool ProcessAllSections) {
|
|
this->ProcessAllSections = ProcessAllSections;
|
|
}
|
|
|
|
/// Add an object to the JIT.
|
|
Error addObject(VModuleKey K, ObjectPtr ObjBuffer) {
|
|
|
|
auto Obj =
|
|
object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef());
|
|
if (!Obj)
|
|
return Obj.takeError();
|
|
|
|
assert(!LinkedObjects.count(K) && "VModuleKey already in use");
|
|
|
|
auto R = GetResources(K);
|
|
|
|
LinkedObjects[K] = createLinkedObject(
|
|
*this, K, OwnedObject(std::move(*Obj), std::move(ObjBuffer)),
|
|
std::move(R.MemMgr), std::move(R.Resolver), ProcessAllSections);
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
/// Remove the object associated with VModuleKey K.
|
|
///
|
|
/// All memory allocated for the object will be freed, and the sections and
|
|
/// symbols it provided will no longer be available. No attempt is made to
|
|
/// re-emit the missing symbols, and any use of these symbols (directly or
|
|
/// indirectly) will result in undefined behavior. If dependence tracking is
|
|
/// required to detect or resolve such issues it should be added at a higher
|
|
/// layer.
|
|
Error removeObject(VModuleKey K) {
|
|
assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
|
|
// How do we invalidate the symbols in H?
|
|
LinkedObjects.erase(K);
|
|
return Error::success();
|
|
}
|
|
|
|
/// Search for the given named symbol.
|
|
/// @param Name The name of the symbol to search for.
|
|
/// @param ExportedSymbolsOnly If true, search only for exported symbols.
|
|
/// @return A handle for the given named symbol, if it exists.
|
|
JITSymbol findSymbol(StringRef Name, bool ExportedSymbolsOnly) {
|
|
for (auto &KV : LinkedObjects)
|
|
if (auto Sym = KV.second->getSymbol(Name, ExportedSymbolsOnly))
|
|
return Sym;
|
|
else if (auto Err = Sym.takeError())
|
|
return std::move(Err);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// Search for the given named symbol in the context of the loaded
|
|
/// object represented by the VModuleKey K.
|
|
/// @param K The VModuleKey for the object to search in.
|
|
/// @param Name The name of the symbol to search for.
|
|
/// @param ExportedSymbolsOnly If true, search only for exported symbols.
|
|
/// @return A handle for the given named symbol, if it is found in the
|
|
/// given object.
|
|
JITSymbol findSymbolIn(VModuleKey K, StringRef Name,
|
|
bool ExportedSymbolsOnly) {
|
|
assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
|
|
return LinkedObjects[K]->getSymbol(Name, ExportedSymbolsOnly);
|
|
}
|
|
|
|
/// Map section addresses for the object associated with the
|
|
/// VModuleKey K.
|
|
void mapSectionAddress(VModuleKey K, const void *LocalAddress,
|
|
JITTargetAddress TargetAddr) {
|
|
assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
|
|
LinkedObjects[K]->mapSectionAddress(LocalAddress, TargetAddr);
|
|
}
|
|
|
|
/// Immediately emit and finalize the object represented by the given
|
|
/// VModuleKey.
|
|
/// @param K VModuleKey for object to emit/finalize.
|
|
Error emitAndFinalize(VModuleKey K) {
|
|
assert(LinkedObjects.count(K) && "VModuleKey not associated with object");
|
|
return LinkedObjects[K]->finalize();
|
|
}
|
|
|
|
private:
|
|
ExecutionSession &ES;
|
|
|
|
ResourcesGetter GetResources;
|
|
NotifyLoadedFtor NotifyLoaded;
|
|
NotifyFinalizedFtor NotifyFinalized;
|
|
NotifyFreedFtor NotifyFreed;
|
|
|
|
// NB! `LinkedObjects` needs to be destroyed before `NotifyFreed` because
|
|
// `~ConcreteLinkedObject` calls `NotifyFreed`
|
|
std::map<VModuleKey, std::unique_ptr<LinkedObject>> LinkedObjects;
|
|
bool ProcessAllSections = false;
|
|
};
|
|
|
|
} // end namespace orc
|
|
} // end namespace llvm
|
|
|
|
#endif // LLVM_EXECUTIONENGINE_ORC_RTDYLDOBJECTLINKINGLAYER_H
|