#include "llvm-jitlink.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
#include "llvm/ExecutionEngine/Orc/DebuggerSupportPlugin.h"
#include "llvm/ExecutionEngine/Orc/ELFNixPlatform.h"
#include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h"
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include <cstring>
#include <list>
#include <string>
#ifdef LLVM_ON_UNIX
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#define DEBUG_TYPE "llvm_jitlink"
using namespace llvm;
using namespace llvm::jitlink;
using namespace llvm::orc;
static cl::OptionCategory JITLinkCategory("JITLink Options");
static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,
cl::desc("input files"),
cl::cat(JITLinkCategory));
static cl::list<std::string>
LibrarySearchPaths("L",
cl::desc("Add dir to the list of library search paths"),
cl::Prefix, cl::cat(JITLinkCategory));
static cl::list<std::string>
Libraries("l",
cl::desc("Link against library X in the library search paths"),
cl::Prefix, cl::cat(JITLinkCategory));
static cl::list<std::string>
LibrariesHidden("hidden-l",
cl::desc("Link against library X in the library search "
"paths with hidden visibility"),
cl::Prefix, cl::cat(JITLinkCategory));
static cl::list<std::string>
LoadHidden("load_hidden",
cl::desc("Link against library X with hidden visibility"),
cl::cat(JITLinkCategory));
static cl::opt<bool> NoExec("noexec", cl::desc("Do not execute loaded code"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::list<std::string>
CheckFiles("check", cl::desc("File containing verifier checks"),
cl::cat(JITLinkCategory));
static cl::opt<std::string>
CheckName("check-name", cl::desc("Name of checks to match against"),
cl::init("jitlink-check"), cl::cat(JITLinkCategory));
static cl::opt<std::string>
EntryPointName("entry", cl::desc("Symbol to call as main entry point"),
cl::init(""), cl::cat(JITLinkCategory));
static cl::list<std::string> JITDylibs(
"jd",
cl::desc("Specifies the JITDylib to be used for any subsequent "
"input file, -L<seacrh-path>, and -l<library> arguments"),
cl::cat(JITLinkCategory));
static cl::list<std::string>
Dylibs("preload",
cl::desc("Pre-load dynamic libraries (e.g. language runtimes "
"required by the ORC runtime)"),
cl::cat(JITLinkCategory));
static cl::list<std::string> InputArgv("args", cl::Positional,
cl::desc("<program arguments>..."),
cl::PositionalEatsArgs,
cl::cat(JITLinkCategory));
static cl::opt<bool>
DebuggerSupport("debugger-support",
cl::desc("Enable debugger suppport (default = !-noexec)"),
cl::init(true), cl::Hidden, cl::cat(JITLinkCategory));
static cl::opt<bool>
NoProcessSymbols("no-process-syms",
cl::desc("Do not resolve to llvm-jitlink process symbols"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::list<std::string> AbsoluteDefs(
"abs",
cl::desc("Inject absolute symbol definitions (syntax: <name>=<addr>)"),
cl::cat(JITLinkCategory));
static cl::list<std::string>
Aliases("alias", cl::desc("Inject symbol aliases (syntax: <name>=<addr>)"),
cl::cat(JITLinkCategory));
static cl::list<std::string> TestHarnesses("harness", cl::Positional,
cl::desc("Test harness files"),
cl::PositionalEatsArgs,
cl::cat(JITLinkCategory));
static cl::opt<bool> ShowInitialExecutionSessionState(
"show-init-es",
cl::desc("Print ExecutionSession state before resolving entry point"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<bool> ShowEntryExecutionSessionState(
"show-entry-es",
cl::desc("Print ExecutionSession state after resolving entry point"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<bool> ShowAddrs(
"show-addrs",
cl::desc("Print registered symbol, section, got and stub addresses"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<bool> ShowLinkGraph(
"show-graph",
cl::desc("Print the link graph after fixups have been applied"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<bool> ShowSizes(
"show-sizes",
cl::desc("Show sizes pre- and post-dead stripping, and allocations"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<bool> ShowTimes("show-times",
cl::desc("Show times for llvm-jitlink phases"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<std::string> SlabAllocateSizeString(
"slab-allocate",
cl::desc("Allocate from a slab of the given size "
"(allowable suffixes: Kb, Mb, Gb. default = "
"Kb)"),
cl::init(""), cl::cat(JITLinkCategory));
static cl::opt<uint64_t> SlabAddress(
"slab-address",
cl::desc("Set slab target address (requires -slab-allocate and -noexec)"),
cl::init(~0ULL), cl::cat(JITLinkCategory));
static cl::opt<uint64_t> SlabPageSize(
"slab-page-size",
cl::desc("Set page size for slab (requires -slab-allocate and -noexec)"),
cl::init(0), cl::cat(JITLinkCategory));
static cl::opt<bool> ShowRelocatedSectionContents(
"show-relocated-section-contents",
cl::desc("show section contents after fixups have been applied"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<bool> PhonyExternals(
"phony-externals",
cl::desc("resolve all otherwise unresolved externals to null"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<std::string> OutOfProcessExecutor(
"oop-executor", cl::desc("Launch an out-of-process executor to run code"),
cl::ValueOptional, cl::cat(JITLinkCategory));
static cl::opt<std::string> OutOfProcessExecutorConnect(
"oop-executor-connect",
cl::desc("Connect to an out-of-process executor via TCP"),
cl::cat(JITLinkCategory));
static cl::opt<std::string>
OrcRuntime("orc-runtime", cl::desc("Use ORC runtime from given path"),
cl::init(""), cl::cat(JITLinkCategory));
static cl::opt<bool> AddSelfRelocations(
"add-self-relocations",
cl::desc("Add relocations to function pointers to the current function"),
cl::init(false), cl::cat(JITLinkCategory));
static cl::opt<bool>
ShowErrFailedToMaterialize("show-err-failed-to-materialize",
cl::desc("Show FailedToMaterialize errors"),
cl::init(false), cl::cat(JITLinkCategory));
static ExitOnError ExitOnErr;
static LLVM_ATTRIBUTE_USED void linkComponents() {
errs() << (void *)&llvm_orc_registerEHFrameSectionWrapper
<< (void *)&llvm_orc_deregisterEHFrameSectionWrapper
<< (void *)&llvm_orc_registerJITLoaderGDBWrapper
<< (void *)&llvm_orc_registerJITLoaderGDBAllocAction;
}
static bool UseTestResultOverride = false;
static int64_t TestResultOverride = 0;
extern "C" LLVM_ATTRIBUTE_USED void
llvm_jitlink_setTestResultOverride(int64_t Value) {
TestResultOverride = Value;
UseTestResultOverride = true;
}
static Error addSelfRelocations(LinkGraph &G);
namespace {
template <typename ErrT>
class ConditionalPrintErr {
public:
ConditionalPrintErr(bool C) : C(C) {}
void operator()(ErrT &EI) {
if (C) {
errs() << "llvm-jitlink error: ";
EI.log(errs());
errs() << "\n";
}
}
private:
bool C;
};
Expected<std::unique_ptr<MemoryBuffer>> getFile(const Twine &FileName) {
if (auto F = MemoryBuffer::getFile(FileName))
return std::move(*F);
else
return createFileError(FileName, F.getError());
}
void reportLLVMJITLinkError(Error Err) {
handleAllErrors(
std::move(Err),
ConditionalPrintErr<orc::FailedToMaterialize>(ShowErrFailedToMaterialize),
ConditionalPrintErr<ErrorInfoBase>(true));
}
}
namespace llvm {
static raw_ostream &
operator<<(raw_ostream &OS, const Session::MemoryRegionInfo &MRI) {
return OS << "target addr = "
<< format("0x%016" PRIx64, MRI.getTargetAddress())
<< ", content: " << (const void *)MRI.getContent().data() << " -- "
<< (const void *)(MRI.getContent().data() + MRI.getContent().size())
<< " (" << MRI.getContent().size() << " bytes)";
}
static raw_ostream &
operator<<(raw_ostream &OS, const Session::SymbolInfoMap &SIM) {
OS << "Symbols:\n";
for (auto &SKV : SIM)
OS << " \"" << SKV.first() << "\" " << SKV.second << "\n";
return OS;
}
static raw_ostream &
operator<<(raw_ostream &OS, const Session::FileInfo &FI) {
for (auto &SIKV : FI.SectionInfos)
OS << " Section \"" << SIKV.first() << "\": " << SIKV.second << "\n";
for (auto &GOTKV : FI.GOTEntryInfos)
OS << " GOT \"" << GOTKV.first() << "\": " << GOTKV.second << "\n";
for (auto &StubKV : FI.StubInfos)
OS << " Stub \"" << StubKV.first() << "\": " << StubKV.second << "\n";
return OS;
}
static raw_ostream &
operator<<(raw_ostream &OS, const Session::FileInfoMap &FIM) {
for (auto &FIKV : FIM)
OS << "File \"" << FIKV.first() << "\":\n" << FIKV.second;
return OS;
}
static Error applyHarnessPromotions(Session &S, LinkGraph &G) {
if (S.HarnessFiles.empty() || S.HarnessFiles.count(G.getName()))
return Error::success();
LLVM_DEBUG(dbgs() << "Applying promotions to graph " << G.getName() << "\n");
std::vector<Symbol *> DefinitionsToRemove;
for (auto *Sym : G.defined_symbols()) {
if (!Sym->hasName())
continue;
if (Sym->getLinkage() == Linkage::Weak) {
if (!S.CanonicalWeakDefs.count(Sym->getName()) ||
S.CanonicalWeakDefs[Sym->getName()] != G.getName()) {
LLVM_DEBUG({
dbgs() << " Externalizing weak symbol " << Sym->getName() << "\n";
});
DefinitionsToRemove.push_back(Sym);
} else {
LLVM_DEBUG({
dbgs() << " Making weak symbol " << Sym->getName() << " strong\n";
});
if (S.HarnessExternals.count(Sym->getName()))
Sym->setScope(Scope::Default);
else
Sym->setScope(Scope::Hidden);
Sym->setLinkage(Linkage::Strong);
}
} else if (S.HarnessExternals.count(Sym->getName())) {
LLVM_DEBUG(dbgs() << " Promoting " << Sym->getName() << "\n");
Sym->setScope(Scope::Default);
Sym->setLive(true);
continue;
} else if (S.HarnessDefinitions.count(Sym->getName())) {
LLVM_DEBUG(dbgs() << " Externalizing " << Sym->getName() << "\n");
DefinitionsToRemove.push_back(Sym);
}
}
for (auto *Sym : DefinitionsToRemove)
G.makeExternal(*Sym);
return Error::success();
}
static uint64_t computeTotalBlockSizes(LinkGraph &G) {
uint64_t TotalSize = 0;
for (auto *B : G.blocks())
TotalSize += B->getSize();
return TotalSize;
}
static void dumpSectionContents(raw_ostream &OS, LinkGraph &G) {
constexpr orc::ExecutorAddrDiff DumpWidth = 16;
static_assert(isPowerOf2_64(DumpWidth), "DumpWidth must be a power of two");
std::vector<Section *> Sections;
for (auto &S : G.sections())
Sections.push_back(&S);
llvm::sort(Sections, [](const Section *LHS, const Section *RHS) {
if (llvm::empty(LHS->symbols()) && llvm::empty(RHS->symbols()))
return false;
if (llvm::empty(LHS->symbols()))
return false;
if (llvm::empty(RHS->symbols()))
return true;
SectionRange LHSRange(*LHS);
SectionRange RHSRange(*RHS);
return LHSRange.getStart() < RHSRange.getStart();
});
for (auto *S : Sections) {
OS << S->getName() << " content:";
if (llvm::empty(S->symbols())) {
OS << "\n section empty\n";
continue;
}
std::vector<Symbol *> Syms(S->symbols().begin(), S->symbols().end());
llvm::sort(Syms, [](const Symbol *LHS, const Symbol *RHS) {
return LHS->getAddress() < RHS->getAddress();
});
orc::ExecutorAddr NextAddr(Syms.front()->getAddress().getValue() &
~(DumpWidth - 1));
for (auto *Sym : Syms) {
bool IsZeroFill = Sym->getBlock().isZeroFill();
auto SymStart = Sym->getAddress();
auto SymSize = Sym->getSize();
auto SymEnd = SymStart + SymSize;
const uint8_t *SymData = IsZeroFill ? nullptr
: reinterpret_cast<const uint8_t *>(
Sym->getSymbolContent().data());
while (NextAddr != SymStart) {
if (NextAddr % DumpWidth == 0)
OS << formatv("\n{0:x16}:", NextAddr);
OS << " ";
++NextAddr;
}
while (NextAddr != SymEnd) {
if (NextAddr % DumpWidth == 0)
OS << formatv("\n{0:x16}:", NextAddr);
if (IsZeroFill)
OS << " 00";
else
OS << formatv(" {0:x-2}", SymData[NextAddr - SymStart]);
++NextAddr;
}
}
OS << "\n";
}
}
class JITLinkSlabAllocator final : public JITLinkMemoryManager {
private:
struct FinalizedAllocInfo {
FinalizedAllocInfo(sys::MemoryBlock Mem,
std::vector<shared::WrapperFunctionCall> DeallocActions)
: Mem(Mem), DeallocActions(std::move(DeallocActions)) {}
sys::MemoryBlock Mem;
std::vector<shared::WrapperFunctionCall> DeallocActions;
};
public:
static Expected<std::unique_ptr<JITLinkSlabAllocator>>
Create(uint64_t SlabSize) {
Error Err = Error::success();
std::unique_ptr<JITLinkSlabAllocator> Allocator(
new JITLinkSlabAllocator(SlabSize, Err));
if (Err)
return std::move(Err);
return std::move(Allocator);
}
void allocate(const JITLinkDylib *JD, LinkGraph &G,
OnAllocatedFunction OnAllocated) override {
class IPMMAlloc : public InFlightAlloc {
public:
IPMMAlloc(JITLinkSlabAllocator &Parent, BasicLayout BL,
sys::MemoryBlock StandardSegs, sys::MemoryBlock FinalizeSegs)
: Parent(Parent), BL(std::move(BL)),
StandardSegs(std::move(StandardSegs)),
FinalizeSegs(std::move(FinalizeSegs)) {}
void finalize(OnFinalizedFunction OnFinalized) override {
if (auto Err = applyProtections()) {
OnFinalized(std::move(Err));
return;
}
auto DeallocActions = runFinalizeActions(BL.graphAllocActions());
if (!DeallocActions) {
OnFinalized(DeallocActions.takeError());
return;
}
if (auto Err = Parent.freeBlock(FinalizeSegs)) {
OnFinalized(
joinErrors(std::move(Err), runDeallocActions(*DeallocActions)));
return;
}
OnFinalized(FinalizedAlloc(ExecutorAddr::fromPtr(
new FinalizedAllocInfo(StandardSegs, std::move(*DeallocActions)))));
}
void abandon(OnAbandonedFunction OnAbandoned) override {
OnAbandoned(joinErrors(Parent.freeBlock(StandardSegs),
Parent.freeBlock(FinalizeSegs)));
}
private:
Error applyProtections() {
for (auto &KV : BL.segments()) {
const auto &Group = KV.first;
auto &Seg = KV.second;
auto Prot = toSysMemoryProtectionFlags(Group.getMemProt());
uint64_t SegSize =
alignTo(Seg.ContentSize + Seg.ZeroFillSize, Parent.PageSize);
sys::MemoryBlock MB(Seg.WorkingMem, SegSize);
if (auto EC = sys::Memory::protectMappedMemory(MB, Prot))
return errorCodeToError(EC);
if (Prot & sys::Memory::MF_EXEC)
sys::Memory::InvalidateInstructionCache(MB.base(),
MB.allocatedSize());
}
return Error::success();
}
JITLinkSlabAllocator &Parent;
BasicLayout BL;
sys::MemoryBlock StandardSegs;
sys::MemoryBlock FinalizeSegs;
};
BasicLayout BL(G);
auto SegsSizes = BL.getContiguousPageBasedLayoutSizes(PageSize);
if (!SegsSizes) {
OnAllocated(SegsSizes.takeError());
return;
}
char *AllocBase = nullptr;
{
std::lock_guard<std::mutex> Lock(SlabMutex);
if (SegsSizes->total() > SlabRemaining.allocatedSize()) {
OnAllocated(make_error<StringError>(
"Slab allocator out of memory: request for " +
formatv("{0:x}", SegsSizes->total()) +
" bytes exceeds remaining capacity of " +
formatv("{0:x}", SlabRemaining.allocatedSize()) + " bytes",
inconvertibleErrorCode()));
return;
}
AllocBase = reinterpret_cast<char *>(SlabRemaining.base());
SlabRemaining =
sys::MemoryBlock(AllocBase + SegsSizes->total(),
SlabRemaining.allocatedSize() - SegsSizes->total());
}
sys::MemoryBlock StandardSegs(AllocBase, SegsSizes->StandardSegs);
sys::MemoryBlock FinalizeSegs(AllocBase + SegsSizes->StandardSegs,
SegsSizes->FinalizeSegs);
auto NextStandardSegAddr = ExecutorAddr::fromPtr(StandardSegs.base());
auto NextFinalizeSegAddr = ExecutorAddr::fromPtr(FinalizeSegs.base());
LLVM_DEBUG({
dbgs() << "JITLinkSlabAllocator allocated:\n";
if (SegsSizes->StandardSegs)
dbgs() << formatv(" [ {0:x16} -- {1:x16} ]", NextStandardSegAddr,
NextStandardSegAddr + StandardSegs.allocatedSize())
<< " to stardard segs\n";
else
dbgs() << " no standard segs\n";
if (SegsSizes->FinalizeSegs)
dbgs() << formatv(" [ {0:x16} -- {1:x16} ]", NextFinalizeSegAddr,
NextFinalizeSegAddr + FinalizeSegs.allocatedSize())
<< " to finalize segs\n";
else
dbgs() << " no finalize segs\n";
});
for (auto &KV : BL.segments()) {
auto &Group = KV.first;
auto &Seg = KV.second;
auto &SegAddr =
(Group.getMemDeallocPolicy() == MemDeallocPolicy::Standard)
? NextStandardSegAddr
: NextFinalizeSegAddr;
LLVM_DEBUG({
dbgs() << " " << Group << " -> " << formatv("{0:x16}", SegAddr)
<< "\n";
});
Seg.WorkingMem = SegAddr.toPtr<char *>();
Seg.Addr = SegAddr + SlabDelta;
SegAddr += alignTo(Seg.ContentSize + Seg.ZeroFillSize, PageSize);
if (Seg.ZeroFillSize != 0)
memset(Seg.WorkingMem + Seg.ContentSize, 0, Seg.ZeroFillSize);
}
if (auto Err = BL.apply()) {
OnAllocated(std::move(Err));
return;
}
OnAllocated(std::unique_ptr<InProcessMemoryManager::InFlightAlloc>(
new IPMMAlloc(*this, std::move(BL), std::move(StandardSegs),
std::move(FinalizeSegs))));
}
void deallocate(std::vector<FinalizedAlloc> FinalizedAllocs,
OnDeallocatedFunction OnDeallocated) override {
Error Err = Error::success();
for (auto &FA : FinalizedAllocs) {
std::unique_ptr<FinalizedAllocInfo> FAI(
FA.release().toPtr<FinalizedAllocInfo *>());
Err = joinErrors(std::move(Err), freeBlock(FAI->Mem));
}
OnDeallocated(std::move(Err));
}
private:
JITLinkSlabAllocator(uint64_t SlabSize, Error &Err) {
ErrorAsOutParameter _(&Err);
if (!SlabPageSize) {
if (auto PageSizeOrErr = sys::Process::getPageSize())
PageSize = *PageSizeOrErr;
else {
Err = PageSizeOrErr.takeError();
return;
}
if (PageSize == 0) {
Err = make_error<StringError>("Page size is zero",
inconvertibleErrorCode());
return;
}
} else
PageSize = SlabPageSize;
if (!isPowerOf2_64(PageSize)) {
Err = make_error<StringError>("Page size is not a power of 2",
inconvertibleErrorCode());
return;
}
SlabSize = (SlabSize + PageSize - 1) & ~(PageSize - 1);
const sys::Memory::ProtectionFlags ReadWrite =
static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ |
sys::Memory::MF_WRITE);
std::error_code EC;
SlabRemaining =
sys::Memory::allocateMappedMemory(SlabSize, nullptr, ReadWrite, EC);
if (EC) {
Err = errorCodeToError(EC);
return;
}
if (SlabAddress != ~0ULL)
SlabDelta = ExecutorAddr(SlabAddress) -
ExecutorAddr::fromPtr(SlabRemaining.base());
}
Error freeBlock(sys::MemoryBlock MB) {
return Error::success();
}
std::mutex SlabMutex;
sys::MemoryBlock SlabRemaining;
uint64_t PageSize = 0;
int64_t SlabDelta = 0;
};
Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
SizeString = SizeString.trim();
uint64_t Units = 1024;
if (SizeString.endswith_insensitive("kb"))
SizeString = SizeString.drop_back(2).rtrim();
else if (SizeString.endswith_insensitive("mb")) {
Units = 1024 * 1024;
SizeString = SizeString.drop_back(2).rtrim();
} else if (SizeString.endswith_insensitive("gb")) {
Units = 1024 * 1024 * 1024;
SizeString = SizeString.drop_back(2).rtrim();
}
uint64_t SlabSize = 0;
if (SizeString.getAsInteger(10, SlabSize))
return make_error<StringError>("Invalid numeric format for slab size",
inconvertibleErrorCode());
return SlabSize * Units;
}
static std::unique_ptr<JITLinkMemoryManager> createMemoryManager() {
if (!SlabAllocateSizeString.empty()) {
auto SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString));
return ExitOnErr(JITLinkSlabAllocator::Create(SlabSize));
}
return ExitOnErr(InProcessMemoryManager::Create());
}
static Expected<MaterializationUnit::Interface>
getTestObjectFileInterface(Session &S, MemoryBufferRef O) {
auto I = getObjectFileInterface(S.ES, O);
if (!I)
return I.takeError();
I->SymbolFlags.clear();
auto Obj = cantFail(object::ObjectFile::createObjectFile(O));
if (I->InitSymbol)
I->SymbolFlags[I->InitSymbol] =
JITSymbolFlags::MaterializationSideEffectsOnly;
for (auto &Sym : Obj->symbols()) {
Expected<uint32_t> SymFlagsOrErr = Sym.getFlags();
if (!SymFlagsOrErr)
return SymFlagsOrErr.takeError();
if ((*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined))
continue;
auto Name = Sym.getName();
if (!Name)
return Name.takeError();
if (auto SymType = Sym.getType()) {
if (*SymType == object::SymbolRef::ST_File)
continue;
} else
return SymType.takeError();
auto SymFlags = JITSymbolFlags::fromObjectSymbol(Sym);
if (!SymFlags)
return SymFlags.takeError();
if (SymFlags->isWeak()) {
if (S.HarnessDefinitions.count(*Name) || S.CanonicalWeakDefs.count(*Name))
continue;
S.CanonicalWeakDefs[*Name] = O.getBufferIdentifier();
*SymFlags &= ~JITSymbolFlags::Weak;
if (!S.HarnessExternals.count(*Name))
*SymFlags &= ~JITSymbolFlags::Exported;
} else if (S.HarnessExternals.count(*Name)) {
*SymFlags |= JITSymbolFlags::Exported;
} else if (S.HarnessDefinitions.count(*Name) ||
!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global))
continue;
auto InternedName = S.ES.intern(*Name);
I->SymbolFlags[InternedName] = std::move(*SymFlags);
}
return I;
}
static Error loadProcessSymbols(Session &S) {
auto FilterMainEntryPoint =
[EPName = S.ES.intern(EntryPointName)](SymbolStringPtr Name) {
return Name != EPName;
};
S.MainJD->addGenerator(
ExitOnErr(orc::EPCDynamicLibrarySearchGenerator::GetForTargetProcess(
S.ES, std::move(FilterMainEntryPoint))));
return Error::success();
}
static Error loadDylibs(Session &S) {
LLVM_DEBUG(dbgs() << "Loading dylibs...\n");
for (const auto &Dylib : Dylibs) {
LLVM_DEBUG(dbgs() << " " << Dylib << "\n");
auto G = orc::EPCDynamicLibrarySearchGenerator::Load(S.ES, Dylib.c_str());
if (!G)
return G.takeError();
S.MainJD->addGenerator(std::move(*G));
}
return Error::success();
}
static Expected<std::unique_ptr<ExecutorProcessControl>> launchExecutor() {
#ifndef LLVM_ON_UNIX
return make_error<StringError>("-" + OutOfProcessExecutor.ArgStr +
" not supported on non-unix platforms",
inconvertibleErrorCode());
#elif !LLVM_ENABLE_THREADS
return make_error<StringError>(
"-" + OutOfProcessExecutor.ArgStr +
" requires threads, but LLVM was built with "
"LLVM_ENABLE_THREADS=Off",
inconvertibleErrorCode());
#else
constexpr int ReadEnd = 0;
constexpr int WriteEnd = 1;
int ToExecutor[2];
int FromExecutor[2];
pid_t ChildPID;
if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
return make_error<StringError>("Unable to create pipe for executor",
inconvertibleErrorCode());
ChildPID = fork();
if (ChildPID == 0) {
close(ToExecutor[WriteEnd]);
close(FromExecutor[ReadEnd]);
std::unique_ptr<char[]> ExecutorPath, FDSpecifier;
{
ExecutorPath = std::make_unique<char[]>(OutOfProcessExecutor.size() + 1);
strcpy(ExecutorPath.get(), OutOfProcessExecutor.data());
std::string FDSpecifierStr("filedescs=");
FDSpecifierStr += utostr(ToExecutor[ReadEnd]);
FDSpecifierStr += ',';
FDSpecifierStr += utostr(FromExecutor[WriteEnd]);
FDSpecifier = std::make_unique<char[]>(FDSpecifierStr.size() + 1);
strcpy(FDSpecifier.get(), FDSpecifierStr.c_str());
}
char *const Args[] = {ExecutorPath.get(), FDSpecifier.get(), nullptr};
int RC = execvp(ExecutorPath.get(), Args);
if (RC != 0) {
errs() << "unable to launch out-of-process executor \""
<< ExecutorPath.get() << "\"\n";
exit(1);
}
}
close(ToExecutor[ReadEnd]);
close(FromExecutor[WriteEnd]);
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
std::make_unique<DynamicThreadPoolTaskDispatcher>(),
SimpleRemoteEPC::Setup(), FromExecutor[ReadEnd], ToExecutor[WriteEnd]);
#endif
}
#if LLVM_ON_UNIX && LLVM_ENABLE_THREADS
static Error createTCPSocketError(Twine Details) {
return make_error<StringError>(
formatv("Failed to connect TCP socket '{0}': {1}",
OutOfProcessExecutorConnect, Details),
inconvertibleErrorCode());
}
static Expected<int> connectTCPSocket(std::string Host, std::string PortStr) {
addrinfo *AI;
addrinfo Hints{};
Hints.ai_family = AF_INET;
Hints.ai_socktype = SOCK_STREAM;
Hints.ai_flags = AI_NUMERICSERV;
if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
return createTCPSocketError("Address resolution failed (" +
StringRef(gai_strerror(EC)) + ")");
int SockFD;
addrinfo *Server;
for (Server = AI; Server != nullptr; Server = Server->ai_next) {
if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
continue;
if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
break;
close(SockFD);
}
freeaddrinfo(AI);
if (Server == nullptr)
return createTCPSocketError(std::strerror(errno));
return SockFD;
}
#endif
static Expected<std::unique_ptr<ExecutorProcessControl>> connectToExecutor() {
#ifndef LLVM_ON_UNIX
return make_error<StringError>("-" + OutOfProcessExecutorConnect.ArgStr +
" not supported on non-unix platforms",
inconvertibleErrorCode());
#elif !LLVM_ENABLE_THREADS
return make_error<StringError>(
"-" + OutOfProcessExecutorConnect.ArgStr +
" requires threads, but LLVM was built with "
"LLVM_ENABLE_THREADS=Off",
inconvertibleErrorCode());
#else
StringRef Host, PortStr;
std::tie(Host, PortStr) = StringRef(OutOfProcessExecutorConnect).split(':');
if (Host.empty())
return createTCPSocketError("Host name for -" +
OutOfProcessExecutorConnect.ArgStr +
" can not be empty");
if (PortStr.empty())
return createTCPSocketError("Port number in -" +
OutOfProcessExecutorConnect.ArgStr +
" can not be empty");
int Port = 0;
if (PortStr.getAsInteger(10, Port))
return createTCPSocketError("Port number '" + PortStr +
"' is not a valid integer");
Expected<int> SockFD = connectTCPSocket(Host.str(), PortStr.str());
if (!SockFD)
return SockFD.takeError();
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
std::make_unique<DynamicThreadPoolTaskDispatcher>(),
SimpleRemoteEPC::Setup(), *SockFD, *SockFD);
#endif
}
class PhonyExternalsGenerator : public DefinitionGenerator {
public:
Error tryToGenerate(LookupState &LS, LookupKind K, JITDylib &JD,
JITDylibLookupFlags JDLookupFlags,
const SymbolLookupSet &LookupSet) override {
SymbolMap PhonySymbols;
for (auto &KV : LookupSet)
PhonySymbols[KV.first] = JITEvaluatedSymbol(0, JITSymbolFlags::Exported);
return JD.define(absoluteSymbols(std::move(PhonySymbols)));
}
};
Expected<std::unique_ptr<Session>> Session::Create(Triple TT) {
std::unique_ptr<ExecutorProcessControl> EPC;
if (OutOfProcessExecutor.getNumOccurrences()) {
if (auto REPC = launchExecutor())
EPC = std::move(*REPC);
else
return REPC.takeError();
} else if (OutOfProcessExecutorConnect.getNumOccurrences()) {
if (auto REPC = connectToExecutor())
EPC = std::move(*REPC);
else
return REPC.takeError();
} else {
auto PageSize = sys::Process::getPageSize();
if (!PageSize)
return PageSize.takeError();
EPC = std::make_unique<SelfExecutorProcessControl>(
std::make_shared<SymbolStringPool>(),
std::make_unique<InPlaceTaskDispatcher>(), std::move(TT), *PageSize,
createMemoryManager());
}
Error Err = Error::success();
std::unique_ptr<Session> S(new Session(std::move(EPC), Err));
if (Err)
return std::move(Err);
return std::move(S);
}
Session::~Session() {
if (auto Err = ES.endSession())
ES.reportError(std::move(Err));
}
Session::Session(std::unique_ptr<ExecutorProcessControl> EPC, Error &Err)
: ES(std::move(EPC)),
ObjLayer(ES, ES.getExecutorProcessControl().getMemMgr()) {
class JITLinkSessionPlugin : public ObjectLinkingLayer::Plugin {
public:
JITLinkSessionPlugin(Session &S) : S(S) {}
void modifyPassConfig(MaterializationResponsibility &MR, LinkGraph &G,
PassConfiguration &PassConfig) override {
S.modifyPassConfig(G.getTargetTriple(), PassConfig);
}
Error notifyFailed(MaterializationResponsibility &MR) override {
return Error::success();
}
Error notifyRemovingResources(ResourceKey K) override {
return Error::success();
}
void notifyTransferringResources(ResourceKey DstKey,
ResourceKey SrcKey) override {}
private:
Session &S;
};
ErrorAsOutParameter _(&Err);
ES.setErrorReporter(reportLLVMJITLinkError);
if (auto MainJDOrErr = ES.createJITDylib("main"))
MainJD = &*MainJDOrErr;
else {
Err = MainJDOrErr.takeError();
return;
}
if (!NoProcessSymbols)
ExitOnErr(loadProcessSymbols(*this));
ExitOnErr(loadDylibs(*this));
auto &TT = ES.getExecutorProcessControl().getTargetTriple();
if (DebuggerSupport && TT.isOSBinFormatMachO())
ObjLayer.addPlugin(ExitOnErr(
GDBJITDebugInfoRegistrationPlugin::Create(this->ES, *MainJD, TT)));
if (TT.isOSBinFormatMachO() && !OrcRuntime.empty()) {
if (auto P =
MachOPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str()))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
return;
}
} else if (TT.isOSBinFormatELF() && !OrcRuntime.empty()) {
if (auto P =
ELFNixPlatform::Create(ES, ObjLayer, *MainJD, OrcRuntime.c_str()))
ES.setPlatform(std::move(*P));
else {
Err = P.takeError();
return;
}
} else if (TT.isOSBinFormatELF()) {
if (!NoExec)
ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>(
ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES))));
if (DebuggerSupport)
ObjLayer.addPlugin(std::make_unique<DebugObjectManagerPlugin>(
ES, ExitOnErr(createJITLoaderGDBRegistrar(this->ES))));
}
ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));
for (auto &HarnessFile : TestHarnesses) {
HarnessFiles.insert(HarnessFile);
auto ObjBuffer = ExitOnErr(getFile(HarnessFile));
auto ObjInterface =
ExitOnErr(getObjectFileInterface(ES, ObjBuffer->getMemBufferRef()));
for (auto &KV : ObjInterface.SymbolFlags)
HarnessDefinitions.insert(*KV.first);
auto Obj = ExitOnErr(
object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()));
for (auto &Sym : Obj->symbols()) {
uint32_t SymFlags = ExitOnErr(Sym.getFlags());
auto Name = ExitOnErr(Sym.getName());
if (Name.empty())
continue;
if (SymFlags & object::BasicSymbolRef::SF_Undefined)
HarnessExternals.insert(Name);
}
}
for (auto &DefName : HarnessDefinitions)
HarnessExternals.erase(DefName.getKey());
}
void Session::dumpSessionInfo(raw_ostream &OS) {
OS << "Registered addresses:\n" << SymbolInfos << FileInfos;
}
void Session::modifyPassConfig(const Triple &TT,
PassConfiguration &PassConfig) {
if (!CheckFiles.empty())
PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) {
auto &EPC = ES.getExecutorProcessControl();
if (EPC.getTargetTriple().getObjectFormat() == Triple::ELF)
return registerELFGraphInfo(*this, G);
if (EPC.getTargetTriple().getObjectFormat() == Triple::MachO)
return registerMachOGraphInfo(*this, G);
if (EPC.getTargetTriple().isOSWindows())
return registerCOFFGraphInfo(*this, G);
return make_error<StringError>("Unsupported object format for GOT/stub "
"registration",
inconvertibleErrorCode());
});
if (ShowLinkGraph)
PassConfig.PostFixupPasses.push_back([](LinkGraph &G) -> Error {
outs() << "Link graph \"" << G.getName() << "\" post-fixup:\n";
G.dump(outs());
return Error::success();
});
PassConfig.PrePrunePasses.push_back(
[this](LinkGraph &G) { return applyHarnessPromotions(*this, G); });
if (ShowSizes) {
PassConfig.PrePrunePasses.push_back([this](LinkGraph &G) -> Error {
SizeBeforePruning += computeTotalBlockSizes(G);
return Error::success();
});
PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) -> Error {
SizeAfterFixups += computeTotalBlockSizes(G);
return Error::success();
});
}
if (ShowRelocatedSectionContents)
PassConfig.PostFixupPasses.push_back([](LinkGraph &G) -> Error {
outs() << "Relocated section contents for " << G.getName() << ":\n";
dumpSectionContents(outs(), G);
return Error::success();
});
if (AddSelfRelocations)
PassConfig.PostPrunePasses.push_back(addSelfRelocations);
}
Expected<Session::FileInfo &> Session::findFileInfo(StringRef FileName) {
auto FileInfoItr = FileInfos.find(FileName);
if (FileInfoItr == FileInfos.end())
return make_error<StringError>("file \"" + FileName + "\" not recognized",
inconvertibleErrorCode());
return FileInfoItr->second;
}
Expected<Session::MemoryRegionInfo &>
Session::findSectionInfo(StringRef FileName, StringRef SectionName) {
auto FI = findFileInfo(FileName);
if (!FI)
return FI.takeError();
auto SecInfoItr = FI->SectionInfos.find(SectionName);
if (SecInfoItr == FI->SectionInfos.end())
return make_error<StringError>("no section \"" + SectionName +
"\" registered for file \"" + FileName +
"\"",
inconvertibleErrorCode());
return SecInfoItr->second;
}
Expected<Session::MemoryRegionInfo &>
Session::findStubInfo(StringRef FileName, StringRef TargetName) {
auto FI = findFileInfo(FileName);
if (!FI)
return FI.takeError();
auto StubInfoItr = FI->StubInfos.find(TargetName);
if (StubInfoItr == FI->StubInfos.end())
return make_error<StringError>("no stub for \"" + TargetName +
"\" registered for file \"" + FileName +
"\"",
inconvertibleErrorCode());
return StubInfoItr->second;
}
Expected<Session::MemoryRegionInfo &>
Session::findGOTEntryInfo(StringRef FileName, StringRef TargetName) {
auto FI = findFileInfo(FileName);
if (!FI)
return FI.takeError();
auto GOTInfoItr = FI->GOTEntryInfos.find(TargetName);
if (GOTInfoItr == FI->GOTEntryInfos.end())
return make_error<StringError>("no GOT entry for \"" + TargetName +
"\" registered for file \"" + FileName +
"\"",
inconvertibleErrorCode());
return GOTInfoItr->second;
}
bool Session::isSymbolRegistered(StringRef SymbolName) {
return SymbolInfos.count(SymbolName);
}
Expected<Session::MemoryRegionInfo &>
Session::findSymbolInfo(StringRef SymbolName, Twine ErrorMsgStem) {
auto SymInfoItr = SymbolInfos.find(SymbolName);
if (SymInfoItr == SymbolInfos.end())
return make_error<StringError>(ErrorMsgStem + ": symbol " + SymbolName +
" not found",
inconvertibleErrorCode());
return SymInfoItr->second;
}
}
static Triple getFirstFileTriple() {
static Triple FirstTT = []() {
assert(!InputFiles.empty() && "InputFiles can not be empty");
for (auto InputFile : InputFiles) {
auto ObjBuffer = ExitOnErr(getFile(InputFile));
file_magic Magic = identify_magic(ObjBuffer->getBuffer());
switch (Magic) {
case file_magic::coff_object:
case file_magic::elf_relocatable:
case file_magic::macho_object: {
auto Obj = ExitOnErr(
object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()));
Triple TT = Obj->makeTriple();
if (Magic == file_magic::coff_object)
TT.setOS(Triple::OSType::Win32);
return TT;
}
default:
break;
}
}
return Triple();
}();
return FirstTT;
}
static Error sanitizeArguments(const Triple &TT, const char *ArgV0) {
if (NoExec && !InputArgv.empty())
errs() << "Warning: --args passed to -noexec run will be ignored.\n";
if (EntryPointName.empty())
EntryPointName = TT.getObjectFormat() == Triple::MachO ? "_main" : "main";
if (DebuggerSupport.getNumOccurrences() == 0 && NoExec)
DebuggerSupport = false;
if (SlabAllocateSizeString != "") {
if (OutOfProcessExecutor.getNumOccurrences() ||
OutOfProcessExecutorConnect.getNumOccurrences())
return make_error<StringError>(
"-slab-allocate cannot be used with -oop-executor or "
"-oop-executor-connect",
inconvertibleErrorCode());
}
if (SlabAddress != ~0ULL) {
if (SlabAllocateSizeString == "" || !NoExec)
return make_error<StringError>(
"-slab-address requires -slab-allocate and -noexec",
inconvertibleErrorCode());
if (SlabPageSize == 0)
errs() << "Warning: -slab-address used without -slab-page-size.\n";
}
if (SlabPageSize != 0) {
if (SlabAllocateSizeString == "")
return make_error<StringError>("-slab-page-size requires -slab-allocate",
inconvertibleErrorCode());
if (!NoExec) {
if (auto RealPageSize = sys::Process::getPageSize()) {
if (SlabPageSize % *RealPageSize)
return make_error<StringError>(
"-slab-page-size must be a multiple of real page size for exec "
"tests (did you mean to use -noexec ?)\n",
inconvertibleErrorCode());
} else {
errs() << "Could not retrieve process page size:\n";
logAllUnhandledErrors(RealPageSize.takeError(), errs(), "");
errs() << "Executing with slab page size = "
<< formatv("{0:x}", SlabPageSize) << ".\n"
<< "Tool may crash if " << formatv("{0:x}", SlabPageSize)
<< " is not a multiple of the real process page size.\n"
<< "(did you mean to use -noexec ?)";
}
}
}
if (!!OutOfProcessExecutor.getNumOccurrences() &&
!!OutOfProcessExecutorConnect.getNumOccurrences())
return make_error<StringError>(
"Only one of -" + OutOfProcessExecutor.ArgStr + " and -" +
OutOfProcessExecutorConnect.ArgStr + " can be specified",
inconvertibleErrorCode());
if (!!OutOfProcessExecutor.getNumOccurrences() &&
OutOfProcessExecutor.empty()) {
SmallString<256> OOPExecutorPath(sys::fs::getMainExecutable(
ArgV0, reinterpret_cast<void *>(&sanitizeArguments)));
sys::path::remove_filename(OOPExecutorPath);
sys::path::append(OOPExecutorPath, "llvm-jitlink-executor");
OutOfProcessExecutor = OOPExecutorPath.str().str();
}
return Error::success();
}
static void addPhonyExternalsGenerator(Session &S) {
S.MainJD->addGenerator(std::make_unique<PhonyExternalsGenerator>());
}
static Error createJITDylibs(Session &S,
std::map<unsigned, JITDylib *> &IdxToJD) {
LLVM_DEBUG(dbgs() << "Creating JITDylibs...\n");
{
IdxToJD[0] = S.MainJD;
S.JDSearchOrder.push_back({S.MainJD, JITDylibLookupFlags::MatchAllSymbols});
LLVM_DEBUG(dbgs() << " 0: " << S.MainJD->getName() << "\n");
for (auto JDItr = JITDylibs.begin(), JDEnd = JITDylibs.end();
JDItr != JDEnd; ++JDItr) {
auto JD = S.ES.createJITDylib(*JDItr);
if (!JD)
return JD.takeError();
unsigned JDIdx = JITDylibs.getPosition(JDItr - JITDylibs.begin());
IdxToJD[JDIdx] = &*JD;
S.JDSearchOrder.push_back({&*JD, JITDylibLookupFlags::MatchAllSymbols});
LLVM_DEBUG(dbgs() << " " << JDIdx << ": " << JD->getName() << "\n");
}
}
LLVM_DEBUG({
dbgs() << "Dylib search order is [ ";
for (auto &KV : S.JDSearchOrder)
dbgs() << KV.first->getName() << " ";
dbgs() << "]\n";
});
return Error::success();
}
static Error addAbsoluteSymbols(Session &S,
const std::map<unsigned, JITDylib *> &IdxToJD) {
LLVM_DEBUG(dbgs() << "Defining absolute symbols...\n");
for (auto AbsDefItr = AbsoluteDefs.begin(), AbsDefEnd = AbsoluteDefs.end();
AbsDefItr != AbsDefEnd; ++AbsDefItr) {
unsigned AbsDefArgIdx =
AbsoluteDefs.getPosition(AbsDefItr - AbsoluteDefs.begin());
auto &JD = *std::prev(IdxToJD.lower_bound(AbsDefArgIdx))->second;
StringRef AbsDefStmt = *AbsDefItr;
size_t EqIdx = AbsDefStmt.find_first_of('=');
if (EqIdx == StringRef::npos)
return make_error<StringError>("Invalid absolute define \"" + AbsDefStmt +
"\". Syntax: <name>=<addr>",
inconvertibleErrorCode());
StringRef Name = AbsDefStmt.substr(0, EqIdx).trim();
StringRef AddrStr = AbsDefStmt.substr(EqIdx + 1).trim();
uint64_t Addr;
if (AddrStr.getAsInteger(0, Addr))
return make_error<StringError>("Invalid address expression \"" + AddrStr +
"\" in absolute symbol definition \"" +
AbsDefStmt + "\"",
inconvertibleErrorCode());
JITEvaluatedSymbol AbsDef(Addr, JITSymbolFlags::Exported);
if (auto Err = JD.define(absoluteSymbols({{S.ES.intern(Name), AbsDef}})))
return Err;
S.SymbolInfos[Name] = {ArrayRef<char>(), Addr};
}
return Error::success();
}
static Error addAliases(Session &S,
const std::map<unsigned, JITDylib *> &IdxToJD) {
LLVM_DEBUG(dbgs() << "Defining aliases...\n");
for (auto AliasItr = Aliases.begin(), AliasEnd = Aliases.end();
AliasItr != AliasEnd; ++AliasItr) {
unsigned AliasArgIdx = Aliases.getPosition(AliasItr - Aliases.begin());
auto &JD = *std::prev(IdxToJD.lower_bound(AliasArgIdx))->second;
StringRef AliasStmt = *AliasItr;
size_t EqIdx = AliasStmt.find_first_of('=');
if (EqIdx == StringRef::npos)
return make_error<StringError>("Invalid alias definition \"" + AliasStmt +
"\". Syntax: <name>=<addr>",
inconvertibleErrorCode());
StringRef Alias = AliasStmt.substr(0, EqIdx).trim();
StringRef Aliasee = AliasStmt.substr(EqIdx + 1).trim();
SymbolAliasMap SAM;
SAM[S.ES.intern(Alias)] = {S.ES.intern(Aliasee), JITSymbolFlags::Exported};
if (auto Err = JD.define(symbolAliases(std::move(SAM))))
return Err;
}
return Error::success();
}
static Error addTestHarnesses(Session &S) {
LLVM_DEBUG(dbgs() << "Adding test harness objects...\n");
for (auto HarnessFile : TestHarnesses) {
LLVM_DEBUG(dbgs() << " " << HarnessFile << "\n");
auto ObjBuffer = getFile(HarnessFile);
if (!ObjBuffer)
return ObjBuffer.takeError();
if (auto Err = S.ObjLayer.add(*S.MainJD, std::move(*ObjBuffer)))
return Err;
}
return Error::success();
}
static Error addObjects(Session &S,
const std::map<unsigned, JITDylib *> &IdxToJD) {
LLVM_DEBUG(dbgs() << "Adding objects...\n");
for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
InputFileItr != InputFileEnd; ++InputFileItr) {
unsigned InputFileArgIdx =
InputFiles.getPosition(InputFileItr - InputFiles.begin());
const std::string &InputFile = *InputFileItr;
if (StringRef(InputFile).endswith(".a") ||
StringRef(InputFile).endswith(".lib"))
continue;
auto &JD = *std::prev(IdxToJD.lower_bound(InputFileArgIdx))->second;
LLVM_DEBUG(dbgs() << " " << InputFileArgIdx << ": \"" << InputFile
<< "\" to " << JD.getName() << "\n";);
auto ObjBuffer = getFile(InputFile);
if (!ObjBuffer)
return ObjBuffer.takeError();
if (S.HarnessFiles.empty()) {
if (auto Err = S.ObjLayer.add(JD, std::move(*ObjBuffer)))
return Err;
} else {
auto ObjInterface =
getTestObjectFileInterface(S, (*ObjBuffer)->getMemBufferRef());
if (!ObjInterface)
return ObjInterface.takeError();
if (auto Err = S.ObjLayer.add(JD, std::move(*ObjBuffer),
std::move(*ObjInterface)))
return Err;
}
}
return Error::success();
}
static Expected<MaterializationUnit::Interface>
getObjectFileInterfaceHidden(ExecutionSession &ES, MemoryBufferRef ObjBuffer) {
auto I = getObjectFileInterface(ES, ObjBuffer);
if (I) {
for (auto &KV : I->SymbolFlags)
KV.second &= ~JITSymbolFlags::Exported;
}
return I;
}
static Error addLibraries(Session &S,
const std::map<unsigned, JITDylib *> &IdxToJD) {
DenseMap<const JITDylib *, SmallVector<StringRef, 2>> JDSearchPaths;
for (auto LSPItr = LibrarySearchPaths.begin(),
LSPEnd = LibrarySearchPaths.end();
LSPItr != LSPEnd; ++LSPItr) {
unsigned LibrarySearchPathIdx =
LibrarySearchPaths.getPosition(LSPItr - LibrarySearchPaths.begin());
auto &JD = *std::prev(IdxToJD.lower_bound(LibrarySearchPathIdx))->second;
StringRef LibrarySearchPath = *LSPItr;
if (sys::fs::get_file_type(LibrarySearchPath) !=
sys::fs::file_type::directory_file)
return make_error<StringError>("While linking " + JD.getName() + ", -L" +
LibrarySearchPath +
" does not point to a directory",
inconvertibleErrorCode());
JDSearchPaths[&JD].push_back(*LSPItr);
}
LLVM_DEBUG({
if (!JDSearchPaths.empty())
dbgs() << "Search paths:\n";
for (auto &KV : JDSearchPaths) {
dbgs() << " " << KV.first->getName() << ": [";
for (auto &LibSearchPath : KV.second)
dbgs() << " \"" << LibSearchPath << "\"";
dbgs() << " ]\n";
}
});
struct LibraryLoad {
StringRef LibName;
bool IsPath = false;
unsigned Position;
StringRef *CandidateExtensions;
enum { Standard, Hidden } Modifier;
};
std::vector<LibraryLoad> LibraryLoads;
for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
InputFileItr != InputFileEnd; ++InputFileItr) {
StringRef InputFile = *InputFileItr;
if (!InputFile.endswith(".a") && !InputFile.endswith(".lib"))
continue;
LibraryLoad LL;
LL.LibName = InputFile;
LL.IsPath = true;
LL.Position = InputFiles.getPosition(InputFileItr - InputFiles.begin());
LL.CandidateExtensions = nullptr;
LL.Modifier = LibraryLoad::Standard;
LibraryLoads.push_back(std::move(LL));
}
for (auto LibItr = LoadHidden.begin(), LibEnd = LoadHidden.end();
LibItr != LibEnd; ++LibItr) {
LibraryLoad LL;
LL.LibName = *LibItr;
LL.IsPath = true;
LL.Position = LoadHidden.getPosition(LibItr - LoadHidden.begin());
LL.CandidateExtensions = nullptr;
LL.Modifier = LibraryLoad::Hidden;
LibraryLoads.push_back(std::move(LL));
}
StringRef StandardExtensions[] = {".so", ".dylib", ".dll", ".a", ".lib"};
StringRef ArchiveExtensionsOnly[] = {".a", ".lib"};
for (auto LibItr = Libraries.begin(), LibEnd = Libraries.end();
LibItr != LibEnd; ++LibItr) {
LibraryLoad LL;
LL.LibName = *LibItr;
LL.Position = Libraries.getPosition(LibItr - Libraries.begin());
LL.CandidateExtensions = StandardExtensions;
LL.Modifier = LibraryLoad::Standard;
LibraryLoads.push_back(std::move(LL));
}
for (auto LibHiddenItr = LibrariesHidden.begin(),
LibHiddenEnd = LibrariesHidden.end();
LibHiddenItr != LibHiddenEnd; ++LibHiddenItr) {
LibraryLoad LL;
LL.LibName = *LibHiddenItr;
LL.Position =
LibrariesHidden.getPosition(LibHiddenItr - LibrariesHidden.begin());
LL.CandidateExtensions = ArchiveExtensionsOnly;
LL.Modifier = LibraryLoad::Hidden;
LibraryLoads.push_back(std::move(LL));
}
if (!LibrariesHidden.empty() || !LoadHidden.empty())
S.ObjLayer.setOverrideObjectFlagsWithResponsibilityFlags(true);
llvm::sort(LibraryLoads, [](const LibraryLoad &LHS, const LibraryLoad &RHS) {
return LHS.Position < RHS.Position;
});
auto AddArchive = [&](const char *Path, const LibraryLoad &LL)
-> Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>> {
unique_function<Expected<MaterializationUnit::Interface>(
ExecutionSession & ES, MemoryBufferRef ObjBuffer)>
GetObjFileInterface;
switch (LL.Modifier) {
case LibraryLoad::Standard:
GetObjFileInterface = getObjectFileInterface;
break;
case LibraryLoad::Hidden:
GetObjFileInterface = getObjectFileInterfaceHidden;
break;
}
return StaticLibraryDefinitionGenerator::Load(
S.ObjLayer, Path, S.ES.getExecutorProcessControl().getTargetTriple(),
std::move(GetObjFileInterface));
};
for (auto &LL : LibraryLoads) {
bool LibFound = false;
auto &JD = *std::prev(IdxToJD.lower_bound(LL.Position))->second;
if (auto *LJD = S.ES.getJITDylibByName(LL.LibName)) {
JD.addToLinkOrder(*LJD);
continue;
}
if (LL.IsPath) {
auto G = AddArchive(LL.LibName.str().c_str(), LL);
if (!G)
return createFileError(LL.LibName, G.takeError());
JD.addGenerator(std::move(*G));
LLVM_DEBUG({
dbgs() << "Adding generator for static library " << LL.LibName << " to "
<< JD.getName() << "\n";
});
continue;
}
auto JDSearchPathsItr = JDSearchPaths.find(&JD);
if (JDSearchPathsItr != JDSearchPaths.end()) {
for (StringRef SearchPath : JDSearchPathsItr->second) {
for (const char *LibExt : {".dylib", ".so", ".dll", ".a", ".lib"}) {
SmallVector<char, 256> LibPath;
LibPath.reserve(SearchPath.size() + strlen("lib") +
LL.LibName.size() + strlen(LibExt) +
2); llvm::copy(SearchPath, std::back_inserter(LibPath));
if (StringRef(LibExt) != ".lib" && StringRef(LibExt) != ".dll")
sys::path::append(LibPath, "lib" + LL.LibName + LibExt);
else
sys::path::append(LibPath, LL.LibName + LibExt);
LibPath.push_back('\0');
if (sys::fs::get_file_type(LibPath.data()) !=
sys::fs::file_type::regular_file) {
continue;
}
file_magic Magic;
if (auto EC = identify_magic(LibPath, Magic)) {
LLVM_DEBUG({
dbgs() << "Library search found \"" << LibPath
<< "\", but could not identify file type (" << EC.message()
<< "). Skipping.\n";
});
continue;
}
LibFound = true;
switch (Magic) {
case file_magic::elf_shared_object:
case file_magic::macho_dynamically_linked_shared_lib: {
auto G =
EPCDynamicLibrarySearchGenerator::Load(S.ES, LibPath.data());
if (!G)
return G.takeError();
LLVM_DEBUG({
dbgs() << "Adding generator for dynamic library "
<< LibPath.data() << " to " << JD.getName() << "\n";
});
JD.addGenerator(std::move(*G));
break;
}
case file_magic::archive:
case file_magic::macho_universal_binary: {
auto G = AddArchive(LibPath.data(), LL);
if (!G)
return G.takeError();
JD.addGenerator(std::move(*G));
LLVM_DEBUG({
dbgs() << "Adding generator for static library " << LibPath.data()
<< " to " << JD.getName() << "\n";
});
break;
}
default:
LLVM_DEBUG({
dbgs() << "Library search found \"" << LibPath
<< "\", but file type is not supported. Skipping.\n";
});
LibFound = false;
break;
}
if (LibFound)
break;
}
if (LibFound)
break;
}
}
if (!LibFound)
return make_error<StringError>("While linking " + JD.getName() +
", could not find library for -l" +
LL.LibName,
inconvertibleErrorCode());
}
return Error::success();
}
static Error addSessionInputs(Session &S) {
std::map<unsigned, JITDylib *> IdxToJD;
if (auto Err = createJITDylibs(S, IdxToJD))
return Err;
if (auto Err = addAbsoluteSymbols(S, IdxToJD))
return Err;
if (auto Err = addAliases(S, IdxToJD))
return Err;
if (!TestHarnesses.empty())
if (auto Err = addTestHarnesses(S))
return Err;
if (auto Err = addObjects(S, IdxToJD))
return Err;
if (auto Err = addLibraries(S, IdxToJD))
return Err;
return Error::success();
}
namespace {
struct TargetInfo {
const Target *TheTarget;
std::unique_ptr<MCSubtargetInfo> STI;
std::unique_ptr<MCRegisterInfo> MRI;
std::unique_ptr<MCAsmInfo> MAI;
std::unique_ptr<MCContext> Ctx;
std::unique_ptr<MCDisassembler> Disassembler;
std::unique_ptr<MCInstrInfo> MII;
std::unique_ptr<MCInstrAnalysis> MIA;
std::unique_ptr<MCInstPrinter> InstPrinter;
};
}
static TargetInfo getTargetInfo(const Triple &TT) {
auto TripleName = TT.str();
std::string ErrorStr;
const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, ErrorStr);
if (!TheTarget)
ExitOnErr(make_error<StringError>("Error accessing target '" + TripleName +
"': " + ErrorStr,
inconvertibleErrorCode()));
std::unique_ptr<MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(TripleName, "", ""));
if (!STI)
ExitOnErr(
make_error<StringError>("Unable to create subtarget for " + TripleName,
inconvertibleErrorCode()));
std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
if (!MRI)
ExitOnErr(make_error<StringError>("Unable to create target register info "
"for " +
TripleName,
inconvertibleErrorCode()));
MCTargetOptions MCOptions;
std::unique_ptr<MCAsmInfo> MAI(
TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
if (!MAI)
ExitOnErr(make_error<StringError>("Unable to create target asm info " +
TripleName,
inconvertibleErrorCode()));
auto Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(),
MRI.get(), STI.get());
std::unique_ptr<MCDisassembler> Disassembler(
TheTarget->createMCDisassembler(*STI, *Ctx));
if (!Disassembler)
ExitOnErr(make_error<StringError>("Unable to create disassembler for " +
TripleName,
inconvertibleErrorCode()));
std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo());
if (!MII)
ExitOnErr(make_error<StringError>("Unable to create instruction info for" +
TripleName,
inconvertibleErrorCode()));
std::unique_ptr<MCInstrAnalysis> MIA(
TheTarget->createMCInstrAnalysis(MII.get()));
if (!MIA)
ExitOnErr(make_error<StringError>(
"Unable to create instruction analysis for" + TripleName,
inconvertibleErrorCode()));
std::unique_ptr<MCInstPrinter> InstPrinter(
TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI));
if (!InstPrinter)
ExitOnErr(make_error<StringError>(
"Unable to create instruction printer for" + TripleName,
inconvertibleErrorCode()));
return {TheTarget, std::move(STI), std::move(MRI),
std::move(MAI), std::move(Ctx), std::move(Disassembler),
std::move(MII), std::move(MIA), std::move(InstPrinter)};
}
static Error runChecks(Session &S) {
const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple();
if (CheckFiles.empty())
return Error::success();
LLVM_DEBUG(dbgs() << "Running checks...\n");
auto TI = getTargetInfo(TT);
auto IsSymbolValid = [&S](StringRef Symbol) {
return S.isSymbolRegistered(Symbol);
};
auto GetSymbolInfo = [&S](StringRef Symbol) {
return S.findSymbolInfo(Symbol, "Can not get symbol info");
};
auto GetSectionInfo = [&S](StringRef FileName, StringRef SectionName) {
return S.findSectionInfo(FileName, SectionName);
};
auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName) {
return S.findStubInfo(FileName, SectionName);
};
auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) {
return S.findGOTEntryInfo(FileName, SectionName);
};
RuntimeDyldChecker Checker(
IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo,
TT.isLittleEndian() ? support::little : support::big,
TI.Disassembler.get(), TI.InstPrinter.get(), dbgs());
std::string CheckLineStart = "# " + CheckName + ":";
for (auto &CheckFile : CheckFiles) {
auto CheckerFileBuf = ExitOnErr(getFile(CheckFile));
if (!Checker.checkAllRulesInBuffer(CheckLineStart, &*CheckerFileBuf))
ExitOnErr(make_error<StringError>(
"Some checks in " + CheckFile + " failed", inconvertibleErrorCode()));
}
return Error::success();
}
static Error addSelfRelocations(LinkGraph &G) {
auto TI = getTargetInfo(G.getTargetTriple());
for (auto *Sym : G.defined_symbols())
if (Sym->isCallable())
if (auto Err = addFunctionPointerRelocationsToCurrentSymbol(
*Sym, G, *TI.Disassembler, *TI.MIA))
return Err;
return Error::success();
}
static void dumpSessionStats(Session &S) {
if (!ShowSizes)
return;
if (!OrcRuntime.empty())
outs() << "Note: Session stats include runtime and entry point lookup, but "
"not JITDylib initialization/deinitialization.\n";
if (ShowSizes)
outs() << " Total size of all blocks before pruning: "
<< S.SizeBeforePruning
<< "\n Total size of all blocks after fixups: " << S.SizeAfterFixups
<< "\n";
}
static Expected<JITEvaluatedSymbol> getMainEntryPoint(Session &S) {
return S.ES.lookup(S.JDSearchOrder, S.ES.intern(EntryPointName));
}
static Expected<JITEvaluatedSymbol> getOrcRuntimeEntryPoint(Session &S) {
std::string RuntimeEntryPoint = "__orc_rt_run_program_wrapper";
const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple();
if (TT.getObjectFormat() == Triple::MachO)
RuntimeEntryPoint = '_' + RuntimeEntryPoint;
return S.ES.lookup(S.JDSearchOrder, S.ES.intern(RuntimeEntryPoint));
}
static Expected<JITEvaluatedSymbol> getEntryPoint(Session &S) {
JITEvaluatedSymbol EntryPoint;
if (auto EP = getMainEntryPoint(S))
EntryPoint = *EP;
else
return EP.takeError();
LLVM_DEBUG({
dbgs() << "Using entry point \"" << EntryPointName
<< "\": " << formatv("{0:x16}", EntryPoint.getAddress()) << "\n";
});
if (!OrcRuntime.empty()) {
if (auto EP = getOrcRuntimeEntryPoint(S))
EntryPoint = *EP;
else
return EP.takeError();
LLVM_DEBUG({
dbgs() << "(called via __orc_rt_run_program_wrapper at "
<< formatv("{0:x16}", EntryPoint.getAddress()) << ")\n";
});
}
return EntryPoint;
}
static Expected<int> runWithRuntime(Session &S, ExecutorAddr EntryPointAddr) {
StringRef DemangledEntryPoint = EntryPointName;
const auto &TT = S.ES.getExecutorProcessControl().getTargetTriple();
if (TT.getObjectFormat() == Triple::MachO &&
DemangledEntryPoint.front() == '_')
DemangledEntryPoint = DemangledEntryPoint.drop_front();
using llvm::orc::shared::SPSString;
using SPSRunProgramSig =
int64_t(SPSString, SPSString, shared::SPSSequence<SPSString>);
int64_t Result;
if (auto Err = S.ES.callSPSWrapper<SPSRunProgramSig>(
EntryPointAddr, Result, S.MainJD->getName(), DemangledEntryPoint,
static_cast<std::vector<std::string> &>(InputArgv)))
return std::move(Err);
return Result;
}
static Expected<int> runWithoutRuntime(Session &S,
ExecutorAddr EntryPointAddr) {
return S.ES.getExecutorProcessControl().runAsMain(EntryPointAddr, InputArgv);
}
namespace {
struct JITLinkTimers {
TimerGroup JITLinkTG{"llvm-jitlink timers", "timers for llvm-jitlink phases"};
Timer LoadObjectsTimer{"load", "time to load/add object files", JITLinkTG};
Timer LinkTimer{"link", "time to link object files", JITLinkTG};
Timer RunTimer{"run", "time to execute jitlink'd code", JITLinkTG};
};
}
int main(int argc, char *argv[]) {
InitLLVM X(argc, argv);
InitializeAllTargetInfos();
InitializeAllTargetMCs();
InitializeAllDisassemblers();
cl::HideUnrelatedOptions({&JITLinkCategory, &getColorCategory()});
cl::ParseCommandLineOptions(argc, argv, "llvm jitlink tool");
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
std::unique_ptr<JITLinkTimers> Timers =
ShowTimes ? std::make_unique<JITLinkTimers>() : nullptr;
ExitOnErr(sanitizeArguments(getFirstFileTriple(), argv[0]));
auto S = ExitOnErr(Session::Create(getFirstFileTriple()));
{
TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr);
ExitOnErr(addSessionInputs(*S));
}
if (PhonyExternals)
addPhonyExternalsGenerator(*S);
if (ShowInitialExecutionSessionState)
S->ES.dump(outs());
Expected<JITEvaluatedSymbol> EntryPoint(nullptr);
{
ExpectedAsOutParameter<JITEvaluatedSymbol> _(&EntryPoint);
TimeRegion TR(Timers ? &Timers->LinkTimer : nullptr);
EntryPoint = getEntryPoint(*S);
}
if (ShowEntryExecutionSessionState)
S->ES.dump(outs());
if (ShowAddrs)
S->dumpSessionInfo(outs());
dumpSessionStats(*S);
if (!EntryPoint) {
if (Timers)
Timers->JITLinkTG.printAll(errs());
reportLLVMJITLinkError(EntryPoint.takeError());
exit(1);
}
ExitOnErr(runChecks(*S));
if (NoExec)
return 0;
int Result = 0;
{
LLVM_DEBUG(dbgs() << "Running \"" << EntryPointName << "\"...\n");
TimeRegion TR(Timers ? &Timers->RunTimer : nullptr);
if (!OrcRuntime.empty())
Result =
ExitOnErr(runWithRuntime(*S, ExecutorAddr(EntryPoint->getAddress())));
else
Result = ExitOnErr(
runWithoutRuntime(*S, ExecutorAddr(EntryPoint->getAddress())));
}
ExitOnErr(S->ES.endSession());
S.reset();
if (Timers)
Timers->JITLinkTG.printAll(errs());
if (UseTestResultOverride)
Result = TestResultOverride;
return Result;
}