blob: 7edbea23a044f7a9a1ab018b335d17059f5ba20f [file] [log] [blame]
//===- llvm-jitlink.cpp -- Command line interface/tester for llvm-jitlink -===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This utility provides a simple command line interface to the llvm jitlink
// library, which makes relocatable object files executable in memory. Its
// primary function is as a testing utility for the jitlink library.
//
//===----------------------------------------------------------------------===//
#include "llvm-jitlink.h"
#include "llvm/ExecutionEngine/JITLink/EHFrameSupport.h"
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.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/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.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/DynamicLibrary.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include <list>
#include <string>
#define DEBUG_TYPE "llvm-jitlink"
using namespace llvm;
using namespace llvm::jitlink;
using namespace llvm::orc;
static cl::list<std::string> InputFiles(cl::Positional, cl::OneOrMore,
cl::desc("input files"));
static cl::opt<bool> NoExec("noexec", cl::desc("Do not execute loaded code"),
cl::init(false));
static cl::list<std::string>
CheckFiles("check", cl::desc("File containing verifier checks"),
cl::ZeroOrMore);
static cl::opt<std::string>
EntryPointName("entry", cl::desc("Symbol to call as main entry point"),
cl::init(""));
static cl::list<std::string> JITLinkDylibs(
"jld", cl::desc("Specifies the JITDylib to be used for any subsequent "
"input file arguments"));
static cl::list<std::string>
Dylibs("dlopen", cl::desc("Dynamic libraries to load before linking"),
cl::ZeroOrMore);
static cl::list<std::string> InputArgv("args", cl::Positional,
cl::desc("<program arguments>..."),
cl::ZeroOrMore, cl::PositionalEatsArgs);
static cl::opt<bool>
NoProcessSymbols("no-process-syms",
cl::desc("Do not resolve to llvm-jitlink process symbols"),
cl::init(false));
static cl::list<std::string> AbsoluteDefs(
"define-abs",
cl::desc("Inject absolute symbol definitions (syntax: <name>=<addr>)"),
cl::ZeroOrMore);
static cl::opt<bool> ShowAddrs(
"show-addrs",
cl::desc("Print registered symbol, section, got and stub addresses"),
cl::init(false));
static cl::opt<bool> ShowLinkGraph(
"show-graph",
cl::desc("Print the link graph after fixups have been applied"),
cl::init(false));
static cl::opt<bool> ShowSizes(
"show-sizes",
cl::desc("Show sizes pre- and post-dead stripping, and allocations"),
cl::init(false));
static cl::opt<bool> ShowTimes("show-times",
cl::desc("Show times for llvm-jitlink phases"),
cl::init(false));
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(""));
static cl::opt<bool> ShowRelocatedSectionContents(
"show-relocated-section-contents",
cl::desc("show section contents after fixups have been applied"),
cl::init(false));
ExitOnError ExitOnErr;
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 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 JITTargetAddress DumpWidth = 16;
static_assert(isPowerOf2_64(DumpWidth), "DumpWidth must be a power of two");
// Put sections in address order.
std::vector<Section *> Sections;
for (auto &S : G.sections())
Sections.push_back(&S);
std::sort(Sections.begin(), Sections.end(),
[](const Section *LHS, const Section *RHS) {
if (LHS->symbols_empty() && RHS->symbols_empty())
return false;
if (LHS->symbols_empty())
return false;
if (RHS->symbols_empty())
return true;
SectionRange LHSRange(*LHS);
SectionRange RHSRange(*RHS);
return LHSRange.getStart() < RHSRange.getStart();
});
for (auto *S : Sections) {
OS << S->getName() << " content:";
if (S->symbols_empty()) {
OS << "\n section empty\n";
continue;
}
// Sort symbols into order, then render.
std::vector<Symbol *> Syms(S->symbols().begin(), S->symbols().end());
llvm::sort(Syms, [](const Symbol *LHS, const Symbol *RHS) {
return LHS->getAddress() < RHS->getAddress();
});
JITTargetAddress NextAddr = Syms.front()->getAddress() & ~(DumpWidth - 1);
for (auto *Sym : Syms) {
bool IsZeroFill = Sym->getBlock().isZeroFill();
JITTargetAddress SymStart = Sym->getAddress();
JITTargetAddress SymSize = Sym->getSize();
JITTargetAddress SymEnd = SymStart + SymSize;
const uint8_t *SymData =
IsZeroFill ? nullptr : Sym->getSymbolContent().bytes_begin();
// Pad any space before the symbol starts.
while (NextAddr != SymStart) {
if (NextAddr % DumpWidth == 0)
OS << formatv("\n{0:x16}:", NextAddr);
OS << " ";
++NextAddr;
}
// Render the symbol content.
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 {
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);
}
Expected<std::unique_ptr<JITLinkMemoryManager::Allocation>>
allocate(const SegmentsRequestMap &Request) override {
using AllocationMap = DenseMap<unsigned, sys::MemoryBlock>;
// Local class for allocation.
class IPMMAlloc : public Allocation {
public:
IPMMAlloc(AllocationMap SegBlocks) : SegBlocks(std::move(SegBlocks)) {}
MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
assert(SegBlocks.count(Seg) && "No allocation for segment");
return {static_cast<char *>(SegBlocks[Seg].base()),
SegBlocks[Seg].allocatedSize()};
}
JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
assert(SegBlocks.count(Seg) && "No allocation for segment");
return reinterpret_cast<JITTargetAddress>(SegBlocks[Seg].base());
}
void finalizeAsync(FinalizeContinuation OnFinalize) override {
OnFinalize(applyProtections());
}
Error deallocate() override {
for (auto &KV : SegBlocks)
if (auto EC = sys::Memory::releaseMappedMemory(KV.second))
return errorCodeToError(EC);
return Error::success();
}
private:
Error applyProtections() {
for (auto &KV : SegBlocks) {
auto &Prot = KV.first;
auto &Block = KV.second;
if (auto EC = sys::Memory::protectMappedMemory(Block, Prot))
return errorCodeToError(EC);
if (Prot & sys::Memory::MF_EXEC)
sys::Memory::InvalidateInstructionCache(Block.base(),
Block.allocatedSize());
}
return Error::success();
}
AllocationMap SegBlocks;
};
AllocationMap Blocks;
for (auto &KV : Request) {
auto &Seg = KV.second;
if (Seg.getAlignment() > PageSize)
return make_error<StringError>("Cannot request higher than page "
"alignment",
inconvertibleErrorCode());
if (PageSize % Seg.getAlignment() != 0)
return make_error<StringError>("Page size is not a multiple of "
"alignment",
inconvertibleErrorCode());
uint64_t ZeroFillStart = Seg.getContentSize();
uint64_t SegmentSize = ZeroFillStart + Seg.getZeroFillSize();
// Round segment size up to page boundary.
SegmentSize = (SegmentSize + PageSize - 1) & ~(PageSize - 1);
// Take segment bytes from the front of the slab.
void *SlabBase = SlabRemaining.base();
uint64_t SlabRemainingSize = SlabRemaining.allocatedSize();
if (SegmentSize > SlabRemainingSize)
return make_error<StringError>("Slab allocator out of memory",
inconvertibleErrorCode());
sys::MemoryBlock SegMem(SlabBase, SegmentSize);
SlabRemaining =
sys::MemoryBlock(reinterpret_cast<char *>(SlabBase) + SegmentSize,
SlabRemainingSize - SegmentSize);
// Zero out the zero-fill memory.
memset(static_cast<char *>(SegMem.base()) + ZeroFillStart, 0,
Seg.getZeroFillSize());
// Record the block for this segment.
Blocks[KV.first] = std::move(SegMem);
}
return std::unique_ptr<InProcessMemoryManager::Allocation>(
new IPMMAlloc(std::move(Blocks)));
}
private:
JITLinkSlabAllocator(uint64_t SlabSize, Error &Err) {
ErrorAsOutParameter _(&Err);
PageSize = sys::Process::getPageSizeEstimate();
if (!isPowerOf2_64(PageSize)) {
Err = make_error<StringError>("Page size is not a power of 2",
inconvertibleErrorCode());
return;
}
// Round slab request up to page size.
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;
}
}
sys::MemoryBlock SlabRemaining;
uint64_t PageSize = 0;
};
Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
SizeString = SizeString.trim();
uint64_t Units = 1024;
if (SizeString.endswith_lower("kb"))
SizeString = SizeString.drop_back(2).rtrim();
else if (SizeString.endswith_lower("mb")) {
Units = 1024 * 1024;
SizeString = SizeString.drop_back(2).rtrim();
} else if (SizeString.endswith_lower("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<jitlink::JITLinkMemoryManager> createMemoryManager() {
if (!SlabAllocateSizeString.empty()) {
auto SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString));
return ExitOnErr(JITLinkSlabAllocator::Create(SlabSize));
}
return std::make_unique<jitlink::InProcessMemoryManager>();
}
Session::Session(Triple TT)
: MemMgr(createMemoryManager()), ObjLayer(ES, *MemMgr), TT(std::move(TT)) {
/// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the
/// Session.
class JITLinkSessionPlugin : public ObjectLinkingLayer::Plugin {
public:
JITLinkSessionPlugin(Session &S) : S(S) {}
void modifyPassConfig(MaterializationResponsibility &MR, const Triple &TT,
PassConfiguration &PassConfig) {
S.modifyPassConfig(TT, PassConfig);
}
private:
Session &S;
};
if (!NoExec && !TT.isOSWindows())
ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>(
InProcessEHFrameRegistrar::getInstance()));
ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));
}
void Session::dumpSessionInfo(raw_ostream &OS) {
OS << "Registered addresses:\n" << SymbolInfos << FileInfos;
}
void Session::modifyPassConfig(const Triple &FTT,
PassConfiguration &PassConfig) {
if (!CheckFiles.empty())
PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) {
if (TT.getObjectFormat() == Triple::MachO)
return registerMachOStubsAndGOT(*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 post-fixup:\n";
G.dump(outs());
return Error::success();
});
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();
});
}
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;
}
} // end namespace llvm
Triple getFirstFileTriple() {
assert(!InputFiles.empty() && "InputFiles can not be empty");
auto ObjBuffer =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFiles.front())));
auto Obj = ExitOnErr(
object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef()));
return Obj->makeTriple();
}
Error sanitizeArguments(const Session &S) {
if (EntryPointName.empty()) {
if (S.TT.getObjectFormat() == Triple::MachO)
EntryPointName = "_main";
else
EntryPointName = "main";
}
if (NoExec && !InputArgv.empty())
outs() << "Warning: --args passed to -noexec run will be ignored.\n";
return Error::success();
}
Error loadProcessSymbols(Session &S) {
std::string ErrMsg;
if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr, &ErrMsg))
return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode());
char GlobalPrefix = S.TT.getObjectFormat() == Triple::MachO ? '_' : '\0';
auto InternedEntryPointName = S.ES.intern(EntryPointName);
auto FilterMainEntryPoint = [InternedEntryPointName](SymbolStringPtr Name) {
return Name != InternedEntryPointName;
};
S.ES.getMainJITDylib().addGenerator(
ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
GlobalPrefix, FilterMainEntryPoint)));
return Error::success();
}
Error loadDylibs() {
// FIXME: This should all be handled inside DynamicLibrary.
for (const auto &Dylib : Dylibs) {
if (!sys::fs::is_regular_file(Dylib))
return make_error<StringError>("\"" + Dylib + "\" is not a regular file",
inconvertibleErrorCode());
std::string ErrMsg;
if (sys::DynamicLibrary::LoadLibraryPermanently(Dylib.c_str(), &ErrMsg))
return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
}
return Error::success();
}
Error loadObjects(Session &S) {
std::map<unsigned, JITDylib *> IdxToJLD;
// First, set up JITDylibs.
LLVM_DEBUG(dbgs() << "Creating JITDylibs...\n");
{
// Create a "main" JITLinkDylib.
auto &MainJD = S.ES.getMainJITDylib();
IdxToJLD[0] = &MainJD;
S.JDSearchOrder.push_back(&MainJD);
LLVM_DEBUG(dbgs() << " 0: " << MainJD.getName() << "\n");
// Add any extra JITLinkDylibs from the command line.
std::string JDNamePrefix("lib");
for (auto JLDItr = JITLinkDylibs.begin(), JLDEnd = JITLinkDylibs.end();
JLDItr != JLDEnd; ++JLDItr) {
auto &JD = S.ES.createJITDylib(JDNamePrefix + *JLDItr);
unsigned JDIdx =
JITLinkDylibs.getPosition(JLDItr - JITLinkDylibs.begin());
IdxToJLD[JDIdx] = &JD;
S.JDSearchOrder.push_back(&JD);
LLVM_DEBUG(dbgs() << " " << JDIdx << ": " << JD.getName() << "\n");
}
// Set every dylib to link against every other, in command line order.
for (auto *JD : S.JDSearchOrder) {
JITDylibSearchList O;
for (auto *JD2 : S.JDSearchOrder) {
if (JD2 == JD)
continue;
O.push_back(std::make_pair(JD2, false));
}
JD->setSearchOrder(std::move(O));
}
}
// Load each object into the corresponding JITDylib..
LLVM_DEBUG(dbgs() << "Adding objects...\n");
for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
InputFileItr != InputFileEnd; ++InputFileItr) {
unsigned InputFileArgIdx =
InputFiles.getPosition(InputFileItr - InputFiles.begin());
StringRef InputFile = *InputFileItr;
auto &JD = *std::prev(IdxToJLD.lower_bound(InputFileArgIdx))->second;
LLVM_DEBUG(dbgs() << " " << InputFileArgIdx << ": \"" << InputFile
<< "\" to " << JD.getName() << "\n";);
auto ObjBuffer =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile)));
ExitOnErr(S.ObjLayer.add(JD, std::move(ObjBuffer)));
}
// Define absolute symbols.
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(IdxToJLD.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 define \"" + AbsDefStmt +
"\"",
inconvertibleErrorCode());
JITEvaluatedSymbol AbsDef(Addr, JITSymbolFlags::Exported);
if (auto Err = JD.define(absoluteSymbols({{S.ES.intern(Name), AbsDef}})))
return Err;
// Register the absolute symbol with the session symbol infos.
S.SymbolInfos[Name] = { StringRef(), Addr };
}
LLVM_DEBUG({
dbgs() << "Dylib search order is [ ";
for (auto *JD : S.JDSearchOrder)
dbgs() << JD->getName() << " ";
dbgs() << "]\n";
});
return Error::success();
}
Error runChecks(Session &S) {
auto TripleName = S.TT.str();
std::string ErrorStr;
const Target *TheTarget = TargetRegistry::lookupTarget("", S.TT, 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()));
std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
if (!MAI)
ExitOnErr(make_error<StringError>("Unable to create target asm info " +
TripleName,
inconvertibleErrorCode()));
MCContext Ctx(MAI.get(), MRI.get(), nullptr);
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());
std::unique_ptr<MCInstPrinter> InstPrinter(
TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI));
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,
S.TT.isLittleEndian() ? support::little : support::big,
Disassembler.get(), InstPrinter.get(), dbgs());
for (auto &CheckFile : CheckFiles) {
auto CheckerFileBuf =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(CheckFile)));
if (!Checker.checkAllRulesInBuffer("# jitlink-check:", &*CheckerFileBuf))
ExitOnErr(make_error<StringError>(
"Some checks in " + CheckFile + " failed", inconvertibleErrorCode()));
}
return Error::success();
}
static void dumpSessionStats(Session &S) {
if (ShowSizes)
outs() << "Total size of all blocks before pruning: " << S.SizeBeforePruning
<< "\nTotal size of all blocks after fixups: " << S.SizeAfterFixups
<< "\n";
}
static Expected<JITEvaluatedSymbol> getMainEntryPoint(Session &S) {
return S.ES.lookup(S.JDSearchOrder, EntryPointName);
}
Expected<int> runEntryPoint(Session &S, JITEvaluatedSymbol EntryPoint) {
assert(EntryPoint.getAddress() && "Entry point address should not be null");
constexpr const char *JITProgramName = "<llvm-jitlink jit'd code>";
auto PNStorage = std::make_unique<char[]>(strlen(JITProgramName) + 1);
strcpy(PNStorage.get(), JITProgramName);
std::vector<const char *> EntryPointArgs;
EntryPointArgs.push_back(PNStorage.get());
for (auto &InputArg : InputArgv)
EntryPointArgs.push_back(InputArg.data());
EntryPointArgs.push_back(nullptr);
using MainTy = int (*)(int, const char *[]);
MainTy EntryPointPtr = reinterpret_cast<MainTy>(EntryPoint.getAddress());
return EntryPointPtr(EntryPointArgs.size() - 1, EntryPointArgs.data());
}
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::ParseCommandLineOptions(argc, argv, "llvm jitlink tool");
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
/// If timers are enabled, create a JITLinkTimers instance.
std::unique_ptr<JITLinkTimers> Timers =
ShowTimes ? std::make_unique<JITLinkTimers>() : nullptr;
Session S(getFirstFileTriple());
ExitOnErr(sanitizeArguments(S));
if (!NoProcessSymbols)
ExitOnErr(loadProcessSymbols(S));
ExitOnErr(loadDylibs());
{
TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr);
ExitOnErr(loadObjects(S));
}
JITEvaluatedSymbol EntryPoint = 0;
{
TimeRegion TR(Timers ? &Timers->LinkTimer : nullptr);
EntryPoint = ExitOnErr(getMainEntryPoint(S));
}
if (ShowAddrs)
S.dumpSessionInfo(outs());
ExitOnErr(runChecks(S));
dumpSessionStats(S);
if (NoExec)
return 0;
int Result = 0;
{
TimeRegion TR(Timers ? &Timers->RunTimer : nullptr);
Result = ExitOnErr(runEntryPoint(S, EntryPoint));
}
return Result;
}