mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 13:16:06 +00:00

While processing library link options that check search paths (-lx, -hidden-lx, etc.) we shouldn't generate candidate paths with extensions that are invalid for the option being visited (e.g. -hidden-lx only applies to archives, so we shouldn't generate candidates with `.so` extensions). Note: Candidate extensions should probably be further filtered based on the OS of the executing process. This patch is a step in the right direction though.
2756 lines
96 KiB
C++
2756 lines
96 KiB
C++
//===- 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/BinaryFormat/Magic.h"
|
|
#include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX, LLVM_ENABLE_THREADS
|
|
#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
|
|
#include "llvm/ExecutionEngine/Orc/COFFPlatform.h"
|
|
#include "llvm/ExecutionEngine/Orc/COFFVCRuntimeSupport.h"
|
|
#include "llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h"
|
|
#include "llvm/ExecutionEngine/Orc/Debugging/DebugInfoSupport.h"
|
|
#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupportPlugin.h"
|
|
#include "llvm/ExecutionEngine/Orc/Debugging/PerfSupportPlugin.h"
|
|
#include "llvm/ExecutionEngine/Orc/Debugging/VTuneSupportPlugin.h"
|
|
#include "llvm/ExecutionEngine/Orc/EHFrameRegistrationPlugin.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/JITLinkRedirectableSymbolManager.h"
|
|
#include "llvm/ExecutionEngine/Orc/JITLinkReentryTrampolines.h"
|
|
#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
|
|
#include "llvm/ExecutionEngine/Orc/LoadLinkableFile.h"
|
|
#include "llvm/ExecutionEngine/Orc/MachO.h"
|
|
#include "llvm/ExecutionEngine/Orc/MachOPlatform.h"
|
|
#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
|
|
#include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h"
|
|
#include "llvm/ExecutionEngine/Orc/SectCreate.h"
|
|
#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
|
|
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h"
|
|
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderPerf.h"
|
|
#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderVTune.h"
|
|
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
|
|
#include "llvm/ExecutionEngine/Orc/UnwindInfoRegistrationPlugin.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 <deque>
|
|
#include <string>
|
|
|
|
#ifdef LLVM_ON_UNIX
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#endif // LLVM_ON_UNIX
|
|
|
|
#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<bool> LazyLink("lazy",
|
|
cl::desc("Link the following file lazily"),
|
|
cl::cat(JITLinkCategory));
|
|
|
|
enum class SpeculateKind { None, Simple };
|
|
|
|
cl::opt<SpeculateKind> Speculate(
|
|
"speculate", cl::desc("Choose speculation scheme"),
|
|
cl::init(SpeculateKind::None),
|
|
cl::values(clEnumValN(SpeculateKind::None, "none", "No speculation"),
|
|
clEnumValN(SpeculateKind::Simple, "simple",
|
|
"Simple speculation")),
|
|
cl::cat(JITLinkCategory));
|
|
|
|
cl::opt<std::string> SpeculateOrder(
|
|
"speculate-order",
|
|
cl::desc("A CSV file containing (JITDylib, Function) pairs to"
|
|
"speculatively look up"),
|
|
cl::cat(JITLinkCategory));
|
|
|
|
cl::opt<std::string> RecordLazyExecs(
|
|
"record-lazy-execs",
|
|
cl::desc("Write lazy-function executions to a CSV file as (JITDylib, "
|
|
"function) pairs"),
|
|
cl::cat(JITLinkCategory));
|
|
|
|
static cl::opt<size_t> MaterializationThreads(
|
|
"num-threads", cl::desc("Number of materialization threads to use"),
|
|
cl::init(std::numeric_limits<size_t>::max()), 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> SearchSystemLibrary(
|
|
"search-sys-lib",
|
|
cl::desc("Add system library paths to library search paths"),
|
|
cl::init(false), 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> PerfSupport("perf-support",
|
|
cl::desc("Enable perf profiling support"),
|
|
cl::init(false), cl::Hidden,
|
|
cl::cat(JITLinkCategory));
|
|
|
|
static cl::opt<bool> VTuneSupport("vtune-support",
|
|
cl::desc("Enable vtune profiling support"),
|
|
cl::init(false), 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: <alias-name>=<aliasee>)"),
|
|
cl::cat(JITLinkCategory));
|
|
|
|
static cl::list<std::string>
|
|
SectCreate("sectcreate",
|
|
cl::desc("given <sectname>,<filename>[@<sym>=<offset>,...] "
|
|
"add the content of <filename> to <sectname>"),
|
|
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>
|
|
ShowLinkedFiles("show-linked-files",
|
|
cl::desc("List each file/graph name if/when it is linked"),
|
|
cl::init(false), 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<std::string> ShowLinkGraphs(
|
|
"show-graphs",
|
|
cl::desc("Takes a posix regex and prints the link graphs of all files "
|
|
"matching that regex after fixups have been applied"),
|
|
cl::Optional, 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 cl::opt<bool> UseSharedMemory(
|
|
"use-shared-memory",
|
|
cl::desc("Use shared memory to transfer generated code and data"),
|
|
cl::init(false), cl::cat(JITLinkCategory));
|
|
|
|
static cl::opt<std::string>
|
|
OverrideTriple("triple", cl::desc("Override target triple detection"),
|
|
cl::init(""), cl::cat(JITLinkCategory));
|
|
|
|
static cl::opt<bool> AllLoad("all_load",
|
|
cl::desc("Load all members of static archives"),
|
|
cl::init(false), cl::cat(JITLinkCategory));
|
|
|
|
static cl::opt<bool> ForceLoadObjC(
|
|
"ObjC",
|
|
cl::desc("Load all members of static archives that implement "
|
|
"Objective-C classes or categories, or Swift structs, "
|
|
"classes or extensions"),
|
|
cl::init(false), cl::cat(JITLinkCategory));
|
|
|
|
static ExitOnError ExitOnErr;
|
|
|
|
static LLVM_ATTRIBUTE_USED void linkComponents() {
|
|
errs() << "Linking in runtime functions\n"
|
|
<< (void *)&llvm_orc_registerEHFrameSectionWrapper << '\n'
|
|
<< (void *)&llvm_orc_deregisterEHFrameSectionWrapper << '\n'
|
|
<< (void *)&llvm_orc_registerJITLoaderGDBWrapper << '\n'
|
|
<< (void *)&llvm_orc_registerJITLoaderGDBAllocAction << '\n'
|
|
<< (void *)&llvm_orc_registerJITLoaderPerfStart << '\n'
|
|
<< (void *)&llvm_orc_registerJITLoaderPerfEnd << '\n'
|
|
<< (void *)&llvm_orc_registerJITLoaderPerfImpl << '\n'
|
|
<< (void *)&llvm_orc_registerVTuneImpl << '\n'
|
|
<< (void *)&llvm_orc_unregisterVTuneImpl << '\n'
|
|
<< (void *)&llvm_orc_test_registerVTuneImpl << '\n';
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
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 &StubKVs : FI.StubInfos) {
|
|
OS << " Stubs \"" << StubKVs.first() << "\":";
|
|
for (auto MemRegion : StubKVs.second)
|
|
OS << " " << MemRegion;
|
|
OS << "\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;
|
|
}
|
|
|
|
bool lazyLinkingRequested() {
|
|
for (auto LL : LazyLink)
|
|
if (LL)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static Error applyLibraryLinkModifiers(Session &S, LinkGraph &G) {
|
|
// If there are hidden archives and this graph is an archive
|
|
// member then apply hidden modifier.
|
|
if (!S.HiddenArchives.empty()) {
|
|
StringRef ObjName(G.getName());
|
|
if (ObjName.ends_with(')')) {
|
|
auto LibName = ObjName.split('(').first;
|
|
if (S.HiddenArchives.count(LibName)) {
|
|
for (auto *Sym : G.defined_symbols())
|
|
Sym->setScope(std::max(Sym->getScope(), Scope::Hidden));
|
|
}
|
|
}
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error applyHarnessPromotions(Session &S, LinkGraph &G) {
|
|
std::lock_guard<std::mutex> Lock(S.M);
|
|
|
|
// If this graph is part of the test harness there's nothing to do.
|
|
if (S.HarnessFiles.empty() || S.HarnessFiles.count(G.getName()))
|
|
return Error::success();
|
|
|
|
LLVM_DEBUG(dbgs() << "Applying promotions to graph " << G.getName() << "\n");
|
|
|
|
// If this graph is part of the test then promote any symbols referenced by
|
|
// the harness to default scope, remove all symbols that clash with harness
|
|
// definitions.
|
|
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 void dumpSectionContents(raw_ostream &OS, Session &S, LinkGraph &G) {
|
|
std::lock_guard<std::mutex> Lock(S.M);
|
|
|
|
outs() << "Relocated section contents for " << G.getName() << ":\n";
|
|
|
|
constexpr orc::ExecutorAddrDiff 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);
|
|
|
|
llvm::sort(Sections, [](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();
|
|
});
|
|
|
|
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());
|
|
|
|
// 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";
|
|
}
|
|
}
|
|
|
|
// A memory mapper with a fake offset applied only used for -noexec testing
|
|
class InProcessDeltaMapper final : public InProcessMemoryMapper {
|
|
public:
|
|
InProcessDeltaMapper(size_t PageSize, uint64_t TargetAddr)
|
|
: InProcessMemoryMapper(PageSize), TargetMapAddr(TargetAddr),
|
|
DeltaAddr(0) {}
|
|
|
|
static Expected<std::unique_ptr<InProcessDeltaMapper>> Create() {
|
|
size_t PageSize = SlabPageSize;
|
|
if (!PageSize) {
|
|
if (auto PageSizeOrErr = sys::Process::getPageSize())
|
|
PageSize = *PageSizeOrErr;
|
|
else
|
|
return PageSizeOrErr.takeError();
|
|
}
|
|
|
|
if (PageSize == 0)
|
|
return make_error<StringError>("Page size is zero",
|
|
inconvertibleErrorCode());
|
|
|
|
return std::make_unique<InProcessDeltaMapper>(PageSize, SlabAddress);
|
|
}
|
|
|
|
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override {
|
|
InProcessMemoryMapper::reserve(
|
|
NumBytes, [this, OnReserved = std::move(OnReserved)](
|
|
Expected<ExecutorAddrRange> Result) mutable {
|
|
if (!Result)
|
|
return OnReserved(Result.takeError());
|
|
|
|
assert(DeltaAddr == 0 && "Overwriting previous offset");
|
|
if (TargetMapAddr != ~0ULL)
|
|
DeltaAddr = TargetMapAddr - Result->Start.getValue();
|
|
auto OffsetRange = ExecutorAddrRange(Result->Start + DeltaAddr,
|
|
Result->End + DeltaAddr);
|
|
|
|
OnReserved(OffsetRange);
|
|
});
|
|
}
|
|
|
|
char *prepare(ExecutorAddr Addr, size_t ContentSize) override {
|
|
return InProcessMemoryMapper::prepare(Addr - DeltaAddr, ContentSize);
|
|
}
|
|
|
|
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override {
|
|
// Slide mapping based on delta, make all segments read-writable, and
|
|
// discard allocation actions.
|
|
auto FixedAI = std::move(AI);
|
|
FixedAI.MappingBase -= DeltaAddr;
|
|
for (auto &Seg : FixedAI.Segments)
|
|
Seg.AG = {MemProt::Read | MemProt::Write, Seg.AG.getMemLifetime()};
|
|
FixedAI.Actions.clear();
|
|
InProcessMemoryMapper::initialize(
|
|
FixedAI, [this, OnInitialized = std::move(OnInitialized)](
|
|
Expected<ExecutorAddr> Result) mutable {
|
|
if (!Result)
|
|
return OnInitialized(Result.takeError());
|
|
|
|
OnInitialized(ExecutorAddr(Result->getValue() + DeltaAddr));
|
|
});
|
|
}
|
|
|
|
void deinitialize(ArrayRef<ExecutorAddr> Allocations,
|
|
OnDeinitializedFunction OnDeInitialized) override {
|
|
std::vector<ExecutorAddr> Addrs(Allocations.size());
|
|
for (const auto Base : Allocations) {
|
|
Addrs.push_back(Base - DeltaAddr);
|
|
}
|
|
|
|
InProcessMemoryMapper::deinitialize(Addrs, std::move(OnDeInitialized));
|
|
}
|
|
|
|
void release(ArrayRef<ExecutorAddr> Reservations,
|
|
OnReleasedFunction OnRelease) override {
|
|
std::vector<ExecutorAddr> Addrs(Reservations.size());
|
|
for (const auto Base : Reservations) {
|
|
Addrs.push_back(Base - DeltaAddr);
|
|
}
|
|
InProcessMemoryMapper::release(Addrs, std::move(OnRelease));
|
|
}
|
|
|
|
private:
|
|
uint64_t TargetMapAddr;
|
|
uint64_t DeltaAddr;
|
|
};
|
|
|
|
Expected<uint64_t> getSlabAllocSize(StringRef SizeString) {
|
|
SizeString = SizeString.trim();
|
|
|
|
uint64_t Units = 1024;
|
|
|
|
if (SizeString.ends_with_insensitive("kb"))
|
|
SizeString = SizeString.drop_back(2).rtrim();
|
|
else if (SizeString.ends_with_insensitive("mb")) {
|
|
Units = 1024 * 1024;
|
|
SizeString = SizeString.drop_back(2).rtrim();
|
|
} else if (SizeString.ends_with_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> createInProcessMemoryManager() {
|
|
uint64_t SlabSize;
|
|
#ifdef _WIN32
|
|
SlabSize = 1024 * 1024;
|
|
#else
|
|
SlabSize = 1024 * 1024 * 1024;
|
|
#endif
|
|
|
|
if (!SlabAllocateSizeString.empty())
|
|
SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString));
|
|
|
|
// If this is a -no-exec case and we're tweaking the slab address or size then
|
|
// use the delta mapper.
|
|
if (NoExec && (SlabAddress || SlabPageSize))
|
|
return ExitOnErr(
|
|
MapperJITLinkMemoryManager::CreateWithMapper<InProcessDeltaMapper>(
|
|
SlabSize));
|
|
|
|
// Otherwise use the standard in-process mapper.
|
|
return ExitOnErr(
|
|
MapperJITLinkMemoryManager::CreateWithMapper<InProcessMemoryMapper>(
|
|
SlabSize));
|
|
}
|
|
|
|
Expected<std::unique_ptr<jitlink::JITLinkMemoryManager>>
|
|
createSharedMemoryManager(SimpleRemoteEPC &SREPC) {
|
|
SharedMemoryMapper::SymbolAddrs SAs;
|
|
if (auto Err = SREPC.getBootstrapSymbols(
|
|
{{SAs.Instance, rt::ExecutorSharedMemoryMapperServiceInstanceName},
|
|
{SAs.Reserve,
|
|
rt::ExecutorSharedMemoryMapperServiceReserveWrapperName},
|
|
{SAs.Initialize,
|
|
rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName},
|
|
{SAs.Deinitialize,
|
|
rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName},
|
|
{SAs.Release,
|
|
rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName}}))
|
|
return std::move(Err);
|
|
|
|
#ifdef _WIN32
|
|
size_t SlabSize = 1024 * 1024;
|
|
#else
|
|
size_t SlabSize = 1024 * 1024 * 1024;
|
|
#endif
|
|
|
|
if (!SlabAllocateSizeString.empty())
|
|
SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString));
|
|
|
|
return MapperJITLinkMemoryManager::CreateWithMapper<SharedMemoryMapper>(
|
|
SlabSize, SREPC, SAs);
|
|
}
|
|
|
|
|
|
static Expected<MaterializationUnit::Interface>
|
|
getTestObjectFileInterface(Session &S, MemoryBufferRef O) {
|
|
|
|
// Get the standard interface for this object, but ignore the symbols field.
|
|
// We'll handle that manually to include promotion.
|
|
auto I = getObjectFileInterface(S.ES, O);
|
|
if (!I)
|
|
return I.takeError();
|
|
I->SymbolFlags.clear();
|
|
|
|
// If creating an object file was going to fail it would have happened above,
|
|
// so we can 'cantFail' this.
|
|
auto Obj = cantFail(object::ObjectFile::createObjectFile(O));
|
|
|
|
// The init symbol must be included in the SymbolFlags map if present.
|
|
if (I->InitSymbol)
|
|
I->SymbolFlags[I->InitSymbol] =
|
|
JITSymbolFlags::MaterializationSideEffectsOnly;
|
|
|
|
for (auto &Sym : Obj->symbols()) {
|
|
Expected<uint32_t> SymFlagsOrErr = Sym.getFlags();
|
|
if (!SymFlagsOrErr)
|
|
// TODO: Test this error.
|
|
return SymFlagsOrErr.takeError();
|
|
|
|
// Skip symbols not defined in this object file.
|
|
if ((*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined))
|
|
continue;
|
|
|
|
auto Name = Sym.getName();
|
|
if (!Name)
|
|
return Name.takeError();
|
|
|
|
// Skip symbols that have type SF_File.
|
|
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 this is a weak symbol that's not defined in the harness then we
|
|
// need to either mark it as strong (if this is the first definition
|
|
// that we've seen) or discard it.
|
|
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;
|
|
|
|
I->SymbolFlags[S.ES.intern(*Name)] = std::move(*SymFlags);
|
|
}
|
|
|
|
return I;
|
|
}
|
|
|
|
static Error loadProcessSymbols(Session &S) {
|
|
S.ProcessSymsJD = &S.ES.createBareJITDylib("Process");
|
|
auto FilterMainEntryPoint =
|
|
[EPName = S.ES.intern(EntryPointName)](SymbolStringPtr Name) {
|
|
return Name != EPName;
|
|
};
|
|
S.ProcessSymsJD->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 DL = S.getOrLoadDynamicLibrary(Dylib);
|
|
if (!DL)
|
|
return DL.takeError();
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Expected<std::unique_ptr<ExecutorProcessControl>> launchExecutor() {
|
|
#ifndef LLVM_ON_UNIX
|
|
// FIXME: Add support for Windows.
|
|
return make_error<StringError>("-" + OutOfProcessExecutor.ArgStr +
|
|
" not supported on non-unix platforms",
|
|
inconvertibleErrorCode());
|
|
#elif !LLVM_ENABLE_THREADS
|
|
// Out of process mode using SimpleRemoteEPC depends on 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;
|
|
|
|
// Pipe FDs.
|
|
int ToExecutor[2];
|
|
int FromExecutor[2];
|
|
|
|
pid_t ChildPID;
|
|
|
|
// Create pipes to/from the executor..
|
|
if (pipe(ToExecutor) != 0 || pipe(FromExecutor) != 0)
|
|
return make_error<StringError>("Unable to create pipe for executor",
|
|
inconvertibleErrorCode());
|
|
|
|
ChildPID = fork();
|
|
|
|
if (ChildPID == 0) {
|
|
// In the child...
|
|
|
|
// Close the parent ends of the pipes
|
|
close(ToExecutor[WriteEnd]);
|
|
close(FromExecutor[ReadEnd]);
|
|
|
|
// Execute the child process.
|
|
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);
|
|
}
|
|
}
|
|
// else we're the parent...
|
|
|
|
// Close the child ends of the pipes
|
|
close(ToExecutor[ReadEnd]);
|
|
close(FromExecutor[WriteEnd]);
|
|
|
|
auto S = SimpleRemoteEPC::Setup();
|
|
if (UseSharedMemory)
|
|
S.CreateMemoryManager = createSharedMemoryManager;
|
|
|
|
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
|
|
std::make_unique<DynamicThreadPoolTaskDispatcher>(MaterializationThreads),
|
|
std::move(S), 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)) + ")");
|
|
|
|
// Cycle through the returned addrinfo structures and connect to the first
|
|
// reachable endpoint.
|
|
int SockFD;
|
|
addrinfo *Server;
|
|
for (Server = AI; Server != nullptr; Server = Server->ai_next) {
|
|
// socket might fail, e.g. if the address family is not supported. Skip to
|
|
// the next addrinfo structure in such a case.
|
|
if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
|
|
continue;
|
|
|
|
// If connect returns null, we exit the loop with a working socket.
|
|
if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
|
|
break;
|
|
|
|
close(SockFD);
|
|
}
|
|
freeaddrinfo(AI);
|
|
|
|
// If we reached the end of the loop without connecting to a valid endpoint,
|
|
// dump the last error that was logged in socket() or connect().
|
|
if (Server == nullptr)
|
|
return createTCPSocketError(std::strerror(errno));
|
|
|
|
return SockFD;
|
|
}
|
|
#endif
|
|
|
|
static Expected<std::unique_ptr<ExecutorProcessControl>> connectToExecutor() {
|
|
#ifndef LLVM_ON_UNIX
|
|
// FIXME: Add TCP support for Windows.
|
|
return make_error<StringError>("-" + OutOfProcessExecutorConnect.ArgStr +
|
|
" not supported on non-unix platforms",
|
|
inconvertibleErrorCode());
|
|
#elif !LLVM_ENABLE_THREADS
|
|
// Out of process mode using SimpleRemoteEPC depends on 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();
|
|
|
|
auto S = SimpleRemoteEPC::Setup();
|
|
if (UseSharedMemory)
|
|
S.CreateMemoryManager = createSharedMemoryManager;
|
|
|
|
return SimpleRemoteEPC::Create<FDSimpleRemoteEPCTransport>(
|
|
std::make_unique<DynamicThreadPoolTaskDispatcher>(std::nullopt),
|
|
std::move(S), *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] = {ExecutorAddr(), JITSymbolFlags::Exported};
|
|
return JD.define(absoluteSymbols(std::move(PhonySymbols)));
|
|
}
|
|
};
|
|
|
|
Expected<std::unique_ptr<Session::LazyLinkingSupport>>
|
|
createLazyLinkingSupport(Session &S) {
|
|
auto RSMgr = JITLinkRedirectableSymbolManager::Create(S.ObjLayer);
|
|
if (!RSMgr)
|
|
return RSMgr.takeError();
|
|
|
|
std::shared_ptr<SimpleLazyReexportsSpeculator> Speculator;
|
|
switch (Speculate) {
|
|
case SpeculateKind::None:
|
|
break;
|
|
case SpeculateKind::Simple:
|
|
SimpleLazyReexportsSpeculator::RecordExecutionFunction RecordExecs;
|
|
|
|
if (!RecordLazyExecs.empty())
|
|
RecordExecs = [&S](const LazyReexportsManager::CallThroughInfo &CTI) {
|
|
S.LazyFnExecOrder.push_back({CTI.JD->getName(), CTI.BodyName});
|
|
};
|
|
|
|
Speculator =
|
|
SimpleLazyReexportsSpeculator::Create(S.ES, std::move(RecordExecs));
|
|
break;
|
|
}
|
|
|
|
auto LRMgr = createJITLinkLazyReexportsManager(
|
|
S.ObjLayer, **RSMgr, *S.PlatformJD, Speculator.get());
|
|
if (!LRMgr)
|
|
return LRMgr.takeError();
|
|
|
|
return std::make_unique<Session::LazyLinkingSupport>(
|
|
std::move(*RSMgr), std::move(Speculator), std::move(*LRMgr), S.ObjLayer);
|
|
}
|
|
|
|
static Error writeLazyExecOrder(Session &S) {
|
|
if (RecordLazyExecs.empty())
|
|
return Error::success();
|
|
|
|
std::error_code EC;
|
|
raw_fd_ostream ExecOrderOut(RecordLazyExecs, EC);
|
|
if (EC)
|
|
return createFileError(RecordLazyExecs, EC);
|
|
|
|
for (auto &[JDName, FunctionName] : S.LazyFnExecOrder)
|
|
ExecOrderOut << JDName << ", " << FunctionName << "\n";
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
Expected<std::unique_ptr<Session>> Session::Create(Triple TT,
|
|
SubtargetFeatures Features) {
|
|
|
|
std::unique_ptr<ExecutorProcessControl> EPC;
|
|
if (OutOfProcessExecutor.getNumOccurrences()) {
|
|
/// If -oop-executor is passed then launch the executor.
|
|
if (auto REPC = launchExecutor())
|
|
EPC = std::move(*REPC);
|
|
else
|
|
return REPC.takeError();
|
|
} else if (OutOfProcessExecutorConnect.getNumOccurrences()) {
|
|
/// If -oop-executor-connect is passed then connect to the executor.
|
|
if (auto REPC = connectToExecutor())
|
|
EPC = std::move(*REPC);
|
|
else
|
|
return REPC.takeError();
|
|
} else {
|
|
/// Otherwise use SelfExecutorProcessControl to target the current process.
|
|
auto PageSize = sys::Process::getPageSize();
|
|
if (!PageSize)
|
|
return PageSize.takeError();
|
|
std::unique_ptr<TaskDispatcher> Dispatcher;
|
|
if (MaterializationThreads == 0)
|
|
Dispatcher = std::make_unique<InPlaceTaskDispatcher>();
|
|
else {
|
|
#if LLVM_ENABLE_THREADS
|
|
Dispatcher = std::make_unique<DynamicThreadPoolTaskDispatcher>(
|
|
MaterializationThreads);
|
|
#else
|
|
llvm_unreachable("MaterializationThreads should be 0");
|
|
#endif
|
|
}
|
|
|
|
EPC = std::make_unique<SelfExecutorProcessControl>(
|
|
std::make_shared<SymbolStringPool>(), std::move(Dispatcher),
|
|
std::move(TT), *PageSize, createInProcessMemoryManager());
|
|
}
|
|
|
|
Error Err = Error::success();
|
|
std::unique_ptr<Session> S(new Session(std::move(EPC), Err));
|
|
if (Err)
|
|
return std::move(Err);
|
|
S->Features = std::move(Features);
|
|
|
|
if (lazyLinkingRequested()) {
|
|
if (auto LazyLinking = createLazyLinkingSupport(*S))
|
|
S->LazyLinking = std::move(*LazyLinking);
|
|
else
|
|
return LazyLinking.takeError();
|
|
}
|
|
|
|
return std::move(S);
|
|
}
|
|
|
|
Session::~Session() {
|
|
if (auto Err = writeLazyExecOrder(*this))
|
|
ES.reportError(std::move(Err));
|
|
|
|
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()) {
|
|
|
|
/// 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, LinkGraph &G,
|
|
PassConfiguration &PassConfig) override {
|
|
S.modifyPassConfig(G, PassConfig);
|
|
}
|
|
|
|
Error notifyFailed(MaterializationResponsibility &MR) override {
|
|
return Error::success();
|
|
}
|
|
Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
|
|
return Error::success();
|
|
}
|
|
void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
|
|
ResourceKey SrcKey) override {}
|
|
|
|
private:
|
|
Session &S;
|
|
};
|
|
|
|
ErrorAsOutParameter _(&Err);
|
|
|
|
ES.setErrorReporter(reportLLVMJITLinkError);
|
|
|
|
if (!NoProcessSymbols)
|
|
ExitOnErr(loadProcessSymbols(*this));
|
|
|
|
ExitOnErr(loadDylibs(*this));
|
|
|
|
auto &TT = ES.getTargetTriple();
|
|
|
|
if (DebuggerSupport && TT.isOSBinFormatMachO()) {
|
|
if (!ProcessSymsJD) {
|
|
Err = make_error<StringError>("MachO debugging requires process symbols",
|
|
inconvertibleErrorCode());
|
|
return;
|
|
}
|
|
ObjLayer.addPlugin(ExitOnErr(GDBJITDebugInfoRegistrationPlugin::Create(
|
|
this->ES, *ProcessSymsJD, TT)));
|
|
}
|
|
|
|
if (PerfSupport && TT.isOSBinFormatELF()) {
|
|
if (!ProcessSymsJD) {
|
|
Err = make_error<StringError>("MachO debugging requires process symbols",
|
|
inconvertibleErrorCode());
|
|
return;
|
|
}
|
|
ObjLayer.addPlugin(ExitOnErr(DebugInfoPreservationPlugin::Create()));
|
|
ObjLayer.addPlugin(ExitOnErr(PerfSupportPlugin::Create(
|
|
this->ES.getExecutorProcessControl(), *ProcessSymsJD, true, true)));
|
|
}
|
|
|
|
if (VTuneSupport && TT.isOSBinFormatELF()) {
|
|
ObjLayer.addPlugin(ExitOnErr(DebugInfoPreservationPlugin::Create()));
|
|
ObjLayer.addPlugin(ExitOnErr(
|
|
VTuneSupportPlugin::Create(this->ES.getExecutorProcessControl(),
|
|
*ProcessSymsJD, /*EmitDebugInfo=*/true,
|
|
/*TestMode=*/true)));
|
|
}
|
|
|
|
// Set up the platform.
|
|
if (!OrcRuntime.empty()) {
|
|
assert(ProcessSymsJD && "ProcessSymsJD should have been set");
|
|
PlatformJD = &ES.createBareJITDylib("Platform");
|
|
PlatformJD->addToLinkOrder(*ProcessSymsJD);
|
|
|
|
if (TT.isOSBinFormatMachO()) {
|
|
if (auto P =
|
|
MachOPlatform::Create(ObjLayer, *PlatformJD, OrcRuntime.c_str()))
|
|
ES.setPlatform(std::move(*P));
|
|
else {
|
|
Err = P.takeError();
|
|
return;
|
|
}
|
|
} else if (TT.isOSBinFormatELF()) {
|
|
if (auto P =
|
|
ELFNixPlatform::Create(ObjLayer, *PlatformJD, OrcRuntime.c_str()))
|
|
ES.setPlatform(std::move(*P));
|
|
else {
|
|
Err = P.takeError();
|
|
return;
|
|
}
|
|
} else if (TT.isOSBinFormatCOFF()) {
|
|
auto LoadDynLibrary = [&, this](JITDylib &JD,
|
|
StringRef DLLName) -> Error {
|
|
if (!DLLName.ends_with_insensitive(".dll"))
|
|
return make_error<StringError>("DLLName not ending with .dll",
|
|
inconvertibleErrorCode());
|
|
return loadAndLinkDynamicLibrary(JD, DLLName);
|
|
};
|
|
|
|
if (auto P =
|
|
COFFPlatform::Create(ObjLayer, *PlatformJD, OrcRuntime.c_str(),
|
|
std::move(LoadDynLibrary)))
|
|
ES.setPlatform(std::move(*P));
|
|
else {
|
|
Err = P.takeError();
|
|
return;
|
|
}
|
|
} else {
|
|
Err = make_error<StringError>(
|
|
"-" + OrcRuntime.ArgStr + " specified, but format " +
|
|
Triple::getObjectFormatTypeName(TT.getObjectFormat()) +
|
|
" not supported",
|
|
inconvertibleErrorCode());
|
|
return;
|
|
}
|
|
} else if (TT.isOSBinFormatMachO()) {
|
|
if (!NoExec) {
|
|
std::optional<bool> ForceEHFrames;
|
|
if ((Err = ES.getBootstrapMapValue<bool, bool>("darwin-use-ehframes-only",
|
|
ForceEHFrames)))
|
|
return;
|
|
bool UseEHFrames = ForceEHFrames ? *ForceEHFrames : false;
|
|
if (!UseEHFrames)
|
|
ObjLayer.addPlugin(ExitOnErr(UnwindInfoRegistrationPlugin::Create(ES)));
|
|
else
|
|
ObjLayer.addPlugin(std::make_unique<EHFrameRegistrationPlugin>(
|
|
ES, ExitOnErr(EPCEHFrameRegistrar::Create(ES))));
|
|
}
|
|
} 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)), true, true));
|
|
}
|
|
|
|
if (auto MainJDOrErr = ES.createJITDylib("main"))
|
|
MainJD = &*MainJDOrErr;
|
|
else {
|
|
Err = MainJDOrErr.takeError();
|
|
return;
|
|
}
|
|
|
|
if (NoProcessSymbols) {
|
|
// This symbol is used in testcases, but we're not reflecting process
|
|
// symbols so we'll need to make it available some other way.
|
|
auto &TestResultJD = ES.createBareJITDylib("<TestResultJD>");
|
|
ExitOnErr(TestResultJD.define(absoluteSymbols(
|
|
{{ES.intern("llvm_jitlink_setTestResultOverride"),
|
|
{ExecutorAddr::fromPtr(llvm_jitlink_setTestResultOverride),
|
|
JITSymbolFlags::Exported}}})));
|
|
MainJD->addToLinkOrder(TestResultJD);
|
|
}
|
|
|
|
ObjLayer.addPlugin(std::make_unique<JITLinkSessionPlugin>(*this));
|
|
|
|
// Process any harness files.
|
|
for (auto &HarnessFile : TestHarnesses) {
|
|
HarnessFiles.insert(HarnessFile);
|
|
|
|
auto ObjBuffer =
|
|
ExitOnErr(loadLinkableFile(HarnessFile, ES.getTargetTriple(),
|
|
LoadArchives::Never))
|
|
.first;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// If a name is defined by some harness file then it's a definition, not an
|
|
// external.
|
|
for (auto &DefName : HarnessDefinitions)
|
|
HarnessExternals.erase(DefName.getKey());
|
|
|
|
if (!ShowLinkGraphs.empty())
|
|
ShowGraphsRegex = Regex(ShowLinkGraphs);
|
|
}
|
|
|
|
void Session::dumpSessionInfo(raw_ostream &OS) {
|
|
OS << "Registered addresses:\n" << SymbolInfos << FileInfos;
|
|
}
|
|
|
|
void Session::modifyPassConfig(LinkGraph &G, PassConfiguration &PassConfig) {
|
|
|
|
if (ShowLinkedFiles)
|
|
outs() << "Linking " << G.getName() << "\n";
|
|
|
|
if (!CheckFiles.empty())
|
|
PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) {
|
|
if (ES.getTargetTriple().getObjectFormat() == Triple::ELF)
|
|
return registerELFGraphInfo(*this, G);
|
|
|
|
if (ES.getTargetTriple().getObjectFormat() == Triple::MachO)
|
|
return registerMachOGraphInfo(*this, G);
|
|
|
|
if (ES.getTargetTriple().getObjectFormat() == Triple::COFF)
|
|
return registerCOFFGraphInfo(*this, G);
|
|
|
|
return make_error<StringError>("Unsupported object format for GOT/stub "
|
|
"registration",
|
|
inconvertibleErrorCode());
|
|
});
|
|
|
|
if (ShowGraphsRegex)
|
|
PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) -> Error {
|
|
std::lock_guard<std::mutex> Lock(M);
|
|
// Print graph if ShowLinkGraphs is specified-but-empty, or if
|
|
// it contains the given graph.
|
|
if (ShowGraphsRegex->match(G.getName())) {
|
|
outs() << "Link graph \"" << G.getName() << "\" post-fixup:\n";
|
|
G.dump(outs());
|
|
}
|
|
return Error::success();
|
|
});
|
|
|
|
PassConfig.PrePrunePasses.push_back([this](LinkGraph &G) {
|
|
std::lock_guard<std::mutex> Lock(M);
|
|
++ActiveLinks;
|
|
return Error::success();
|
|
});
|
|
PassConfig.PrePrunePasses.push_back(
|
|
[this](LinkGraph &G) { return applyLibraryLinkModifiers(*this, G); });
|
|
PassConfig.PrePrunePasses.push_back(
|
|
[this](LinkGraph &G) { return applyHarnessPromotions(*this, G); });
|
|
|
|
if (ShowRelocatedSectionContents)
|
|
PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) -> Error {
|
|
dumpSectionContents(outs(), *this, G);
|
|
return Error::success();
|
|
});
|
|
|
|
if (AddSelfRelocations)
|
|
PassConfig.PostPrunePasses.push_back(addSelfRelocations);
|
|
|
|
PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) {
|
|
std::lock_guard<std::mutex> Lock(M);
|
|
if (--ActiveLinks == 0)
|
|
ActiveLinksCV.notify_all();
|
|
return Error::success();
|
|
});
|
|
}
|
|
|
|
Expected<JITDylib *> Session::getOrLoadDynamicLibrary(StringRef LibPath) {
|
|
auto It = DynLibJDs.find(LibPath);
|
|
if (It != DynLibJDs.end()) {
|
|
return It->second;
|
|
}
|
|
auto G = EPCDynamicLibrarySearchGenerator::Load(ES, LibPath.data());
|
|
if (!G)
|
|
return G.takeError();
|
|
auto JD = &ES.createBareJITDylib(LibPath.str());
|
|
|
|
JD->addGenerator(std::move(*G));
|
|
DynLibJDs.emplace(LibPath.str(), JD);
|
|
LLVM_DEBUG({
|
|
dbgs() << "Loaded dynamic library " << LibPath.data() << " for " << LibPath
|
|
<< "\n";
|
|
});
|
|
return JD;
|
|
}
|
|
|
|
Error Session::loadAndLinkDynamicLibrary(JITDylib &JD, StringRef LibPath) {
|
|
auto DL = getOrLoadDynamicLibrary(LibPath);
|
|
if (!DL)
|
|
return DL.takeError();
|
|
JD.addToLinkOrder(**DL);
|
|
LLVM_DEBUG({
|
|
dbgs() << "Linking dynamic library " << LibPath << " to " << JD.getName()
|
|
<< "\n";
|
|
});
|
|
return Error::success();
|
|
}
|
|
|
|
Error Session::FileInfo::registerGOTEntry(
|
|
LinkGraph &G, Symbol &Sym, GetSymbolTargetFunction GetSymbolTarget) {
|
|
if (Sym.isSymbolZeroFill())
|
|
return make_error<StringError>("Unexpected zero-fill symbol in section " +
|
|
Sym.getBlock().getSection().getName(),
|
|
inconvertibleErrorCode());
|
|
auto TS = GetSymbolTarget(G, Sym.getBlock());
|
|
if (!TS)
|
|
return TS.takeError();
|
|
GOTEntryInfos[*TS->getName()] = {Sym.getSymbolContent(),
|
|
Sym.getAddress().getValue(),
|
|
Sym.getTargetFlags()};
|
|
return Error::success();
|
|
}
|
|
|
|
Error Session::FileInfo::registerStubEntry(
|
|
LinkGraph &G, Symbol &Sym, GetSymbolTargetFunction GetSymbolTarget) {
|
|
if (Sym.isSymbolZeroFill())
|
|
return make_error<StringError>("Unexpected zero-fill symbol in section " +
|
|
Sym.getBlock().getSection().getName(),
|
|
inconvertibleErrorCode());
|
|
auto TS = GetSymbolTarget(G, Sym.getBlock());
|
|
if (!TS)
|
|
return TS.takeError();
|
|
|
|
SmallVectorImpl<MemoryRegionInfo> &Entry = StubInfos[*TS->getName()];
|
|
Entry.insert(Entry.begin(),
|
|
{Sym.getSymbolContent(), Sym.getAddress().getValue(),
|
|
Sym.getTargetFlags()});
|
|
return Error::success();
|
|
}
|
|
|
|
Error Session::FileInfo::registerMultiStubEntry(
|
|
LinkGraph &G, Symbol &Sym, GetSymbolTargetFunction GetSymbolTarget) {
|
|
if (Sym.isSymbolZeroFill())
|
|
return make_error<StringError>("Unexpected zero-fill symbol in section " +
|
|
Sym.getBlock().getSection().getName(),
|
|
inconvertibleErrorCode());
|
|
|
|
auto Target = GetSymbolTarget(G, Sym.getBlock());
|
|
if (!Target)
|
|
return Target.takeError();
|
|
|
|
SmallVectorImpl<MemoryRegionInfo> &Entry = StubInfos[*Target->getName()];
|
|
Entry.emplace_back(Sym.getSymbolContent(), Sym.getAddress().getValue(),
|
|
Sym.getTargetFlags());
|
|
|
|
// Let's keep stubs ordered by ascending address.
|
|
std::sort(Entry.begin(), Entry.end(),
|
|
[](const MemoryRegionInfo &L, const MemoryRegionInfo &R) {
|
|
return L.getTargetAddress() < R.getTargetAddress();
|
|
});
|
|
|
|
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;
|
|
}
|
|
|
|
class MemoryMatcher {
|
|
public:
|
|
MemoryMatcher(ArrayRef<char> Content)
|
|
: Pos(Content.data()), End(Pos + Content.size()) {}
|
|
|
|
template <typename MaskType> bool matchMask(MaskType Mask) {
|
|
if (Mask == (Mask & *reinterpret_cast<const MaskType *>(Pos))) {
|
|
Pos += sizeof(MaskType);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename ValueType> bool matchEqual(ValueType Value) {
|
|
if (Value == *reinterpret_cast<const ValueType *>(Pos)) {
|
|
Pos += sizeof(ValueType);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool done() const { return Pos == End; }
|
|
|
|
private:
|
|
const char *Pos;
|
|
const char *End;
|
|
};
|
|
|
|
static StringRef detectStubKind(const Session::MemoryRegionInfo &Stub) {
|
|
using namespace support::endian;
|
|
auto Armv7MovWTle = byte_swap<uint32_t, endianness::little>(0xe300c000);
|
|
auto Armv7BxR12le = byte_swap<uint32_t, endianness::little>(0xe12fff1c);
|
|
auto Thumbv7MovWTle = byte_swap<uint32_t, endianness::little>(0x0c00f240);
|
|
auto Thumbv7BxR12le = byte_swap<uint16_t, endianness::little>(0x4760);
|
|
|
|
MemoryMatcher M(Stub.getContent());
|
|
if (M.matchMask(Thumbv7MovWTle)) {
|
|
if (M.matchMask(Thumbv7MovWTle))
|
|
if (M.matchEqual(Thumbv7BxR12le))
|
|
if (M.done())
|
|
return "thumbv7_abs_le";
|
|
} else if (M.matchMask(Armv7MovWTle)) {
|
|
if (M.matchMask(Armv7MovWTle))
|
|
if (M.matchEqual(Armv7BxR12le))
|
|
if (M.done())
|
|
return "armv7_abs_le";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
Expected<Session::MemoryRegionInfo &>
|
|
Session::findStubInfo(StringRef FileName, StringRef TargetName,
|
|
StringRef KindNameFilter) {
|
|
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());
|
|
auto &StubsForTarget = StubInfoItr->second;
|
|
assert(!StubsForTarget.empty() && "At least 1 stub in each entry");
|
|
if (KindNameFilter.empty() && StubsForTarget.size() == 1)
|
|
return StubsForTarget[0]; // Regular single-stub match
|
|
|
|
std::string KindsStr;
|
|
SmallVector<MemoryRegionInfo *, 1> Matches;
|
|
Regex KindNameMatcher(KindNameFilter.empty() ? ".*" : KindNameFilter);
|
|
for (MemoryRegionInfo &Stub : StubsForTarget) {
|
|
StringRef Kind = detectStubKind(Stub);
|
|
if (KindNameMatcher.match(Kind))
|
|
Matches.push_back(&Stub);
|
|
KindsStr += "\"" + (Kind.empty() ? "<unknown>" : Kind.str()) + "\", ";
|
|
}
|
|
if (Matches.empty())
|
|
return make_error<StringError>(
|
|
"\"" + TargetName + "\" has " + Twine(StubsForTarget.size()) +
|
|
" stubs in file \"" + FileName +
|
|
"\", but none of them matches the stub-kind filter \"" +
|
|
KindNameFilter + "\" (all encountered kinds are " +
|
|
StringRef(KindsStr.data(), KindsStr.size() - 2) + ").",
|
|
inconvertibleErrorCode());
|
|
if (Matches.size() > 1)
|
|
return make_error<StringError>(
|
|
"\"" + TargetName + "\" has " + Twine(Matches.size()) +
|
|
" candidate stubs in file \"" + FileName +
|
|
"\". Please refine stub-kind filter \"" + KindNameFilter +
|
|
"\" for disambiguation (encountered kinds are " +
|
|
StringRef(KindsStr.data(), KindsStr.size() - 2) + ").",
|
|
inconvertibleErrorCode());
|
|
|
|
return *Matches[0];
|
|
}
|
|
|
|
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(const orc::SymbolStringPtr &SymbolName) {
|
|
return SymbolInfos.count(SymbolName);
|
|
}
|
|
|
|
Expected<Session::MemoryRegionInfo &>
|
|
Session::findSymbolInfo(const orc::SymbolStringPtr &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
|
|
|
|
static std::pair<Triple, SubtargetFeatures> getFirstFileTripleAndFeatures() {
|
|
static std::pair<Triple, SubtargetFeatures> FirstTTAndFeatures = []() {
|
|
assert(!InputFiles.empty() && "InputFiles can not be empty");
|
|
|
|
if (!OverrideTriple.empty()) {
|
|
LLVM_DEBUG({
|
|
dbgs() << "Triple from -triple override: " << OverrideTriple << "\n";
|
|
});
|
|
return std::make_pair(Triple(OverrideTriple), SubtargetFeatures());
|
|
}
|
|
|
|
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) {
|
|
// TODO: Move this to makeTriple() if possible.
|
|
TT.setObjectFormat(Triple::COFF);
|
|
TT.setOS(Triple::OSType::Win32);
|
|
}
|
|
SubtargetFeatures Features;
|
|
if (auto ObjFeatures = Obj->getFeatures())
|
|
Features = std::move(*ObjFeatures);
|
|
|
|
LLVM_DEBUG({
|
|
dbgs() << "Triple from " << InputFile << ": " << TT.str() << "\n";
|
|
});
|
|
return std::make_pair(TT, Features);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If no plain object file inputs exist to pin down the triple then detect
|
|
// the host triple and default to that.
|
|
auto JTMB = ExitOnErr(JITTargetMachineBuilder::detectHost());
|
|
LLVM_DEBUG({
|
|
dbgs() << "Triple from host-detection: " << JTMB.getTargetTriple().str()
|
|
<< "\n";
|
|
});
|
|
return std::make_pair(JTMB.getTargetTriple(), JTMB.getFeatures());
|
|
}();
|
|
|
|
return FirstTTAndFeatures;
|
|
}
|
|
|
|
static Error sanitizeArguments(const Triple &TT, const char *ArgV0) {
|
|
|
|
// -noexec and --args should not be used together.
|
|
if (NoExec && !InputArgv.empty())
|
|
errs() << "Warning: --args passed to -noexec run will be ignored.\n";
|
|
|
|
// Set the entry point name if not specified.
|
|
if (EntryPointName.empty())
|
|
EntryPointName = TT.getObjectFormat() == Triple::MachO ? "_main" : "main";
|
|
|
|
// Disable debugger support by default in noexec tests.
|
|
if (DebuggerSupport.getNumOccurrences() == 0 && NoExec)
|
|
DebuggerSupport = false;
|
|
|
|
if (!OrcRuntime.empty() && NoProcessSymbols)
|
|
return make_error<StringError>("-orc-runtime requires process symbols",
|
|
inconvertibleErrorCode());
|
|
|
|
// If -slab-allocate is passed, check that we're not trying to use it in
|
|
// -oop-executor or -oop-executor-connect mode.
|
|
//
|
|
// FIXME: Remove once we enable remote slab allocation.
|
|
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 -slab-address is passed, require -slab-allocate and -noexec
|
|
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) {
|
|
// -slab-page-size requires slab alloc.
|
|
if (SlabAllocateSizeString == "")
|
|
return make_error<StringError>("-slab-page-size requires -slab-allocate",
|
|
inconvertibleErrorCode());
|
|
|
|
// Check -slab-page-size / -noexec interactions.
|
|
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 LLVM_ENABLE_THREADS
|
|
if (MaterializationThreads == std::numeric_limits<size_t>::max()) {
|
|
if (auto HC = std::thread::hardware_concurrency())
|
|
MaterializationThreads = HC;
|
|
else {
|
|
errs() << "Warning: std::thread::hardware_concurrency() returned 0, "
|
|
"defaulting to -num-threads=1.\n";
|
|
MaterializationThreads = 1;
|
|
}
|
|
}
|
|
#else
|
|
if (MaterializationThreads.getNumOccurrences() &&
|
|
MaterializationThreads != 0) {
|
|
errs() << "Warning: -num-threads was set, but LLVM was built with threads "
|
|
"disabled. Resetting to -num-threads=0\n";
|
|
}
|
|
MaterializationThreads = 0;
|
|
#endif
|
|
|
|
if (!!OutOfProcessExecutor.getNumOccurrences() ||
|
|
!!OutOfProcessExecutorConnect.getNumOccurrences()) {
|
|
if (NoExec)
|
|
return make_error<StringError>("-noexec cannot be used with " +
|
|
OutOfProcessExecutor.ArgStr + " or " +
|
|
OutOfProcessExecutorConnect.ArgStr,
|
|
inconvertibleErrorCode());
|
|
|
|
if (MaterializationThreads == 0)
|
|
return make_error<StringError>("-threads=0 cannot be used with " +
|
|
OutOfProcessExecutor.ArgStr + " or " +
|
|
OutOfProcessExecutorConnect.ArgStr,
|
|
inconvertibleErrorCode());
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
if (DebugFlag && MaterializationThreads != 0)
|
|
errs() << "Warning: debugging output is not thread safe. "
|
|
"Use -num-threads=0 to stabilize output.\n";
|
|
#endif // NDEBUG
|
|
|
|
// Only one of -oop-executor and -oop-executor-connect can be used.
|
|
if (!!OutOfProcessExecutor.getNumOccurrences() &&
|
|
!!OutOfProcessExecutorConnect.getNumOccurrences())
|
|
return make_error<StringError>(
|
|
"Only one of -" + OutOfProcessExecutor.ArgStr + " and -" +
|
|
OutOfProcessExecutorConnect.ArgStr + " can be specified",
|
|
inconvertibleErrorCode());
|
|
|
|
// If -oop-executor was used but no value was specified then use a sensible
|
|
// default.
|
|
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();
|
|
}
|
|
|
|
// If lazy linking is requested then check compatibility with other options.
|
|
if (lazyLinkingRequested()) {
|
|
if (OrcRuntime.empty())
|
|
return make_error<StringError>("Lazy linking requries the ORC runtime",
|
|
inconvertibleErrorCode());
|
|
|
|
if (!TestHarnesses.empty())
|
|
return make_error<StringError>(
|
|
"Lazy linking cannot be used with -harness mode",
|
|
inconvertibleErrorCode());
|
|
} else if (Speculate != SpeculateKind::None) {
|
|
errs() << "Warning: -speculate ignored as there are no -lazy inputs\n";
|
|
Speculate = SpeculateKind::None;
|
|
}
|
|
|
|
if (Speculate == SpeculateKind::None) {
|
|
if (!SpeculateOrder.empty()) {
|
|
errs() << "Warning: -speculate-order ignored because speculation is "
|
|
"disabled\n";
|
|
SpeculateOrder = "";
|
|
}
|
|
|
|
if (!RecordLazyExecs.empty()) {
|
|
errs() << "Warning: -record-lazy-execs ignored because speculation is "
|
|
"disabled\n";
|
|
RecordLazyExecs = "";
|
|
}
|
|
}
|
|
|
|
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) {
|
|
// First, set up JITDylibs.
|
|
LLVM_DEBUG(dbgs() << "Creating JITDylibs...\n");
|
|
{
|
|
// Create a "main" JITLinkDylib.
|
|
IdxToJD[0] = S.MainJD;
|
|
S.JDSearchOrder.push_back({S.MainJD, JITDylibLookupFlags::MatchAllSymbols});
|
|
LLVM_DEBUG(dbgs() << " 0: " << S.MainJD->getName() << "\n");
|
|
|
|
// Add any extra JITDylibs from the command line.
|
|
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");
|
|
}
|
|
}
|
|
|
|
if (S.PlatformJD)
|
|
S.JDSearchOrder.push_back(
|
|
{S.PlatformJD, JITDylibLookupFlags::MatchExportedSymbolsOnly});
|
|
if (S.ProcessSymsJD)
|
|
S.JDSearchOrder.push_back(
|
|
{S.ProcessSymsJD, JITDylibLookupFlags::MatchExportedSymbolsOnly});
|
|
|
|
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) {
|
|
// 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(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());
|
|
ExecutorSymbolDef AbsDef(ExecutorAddr(Addr), JITSymbolFlags::Exported);
|
|
auto InternedName = S.ES.intern(Name);
|
|
if (auto Err = JD.define(absoluteSymbols({{InternedName, AbsDef}})))
|
|
return Err;
|
|
|
|
// Register the absolute symbol with the session symbol infos.
|
|
S.SymbolInfos[std::move(InternedName)] =
|
|
{ArrayRef<char>(), Addr, AbsDef.getFlags().getTargetFlags()};
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error addAliases(Session &S,
|
|
const std::map<unsigned, JITDylib *> &IdxToJD) {
|
|
// Define absolute symbols.
|
|
LLVM_DEBUG(dbgs() << "Defining aliases...\n");
|
|
|
|
DenseMap<std::pair<JITDylib *, JITDylib *>, SymbolAliasMap> Reexports;
|
|
for (auto AliasItr = Aliases.begin(), AliasEnd = Aliases.end();
|
|
AliasItr != AliasEnd; ++AliasItr) {
|
|
|
|
auto BadExpr = [&]() {
|
|
return make_error<StringError>(
|
|
"Invalid alias definition \"" + *AliasItr +
|
|
"\". Syntax: [<dst-jd>:]<alias>=[<src-jd>:]<aliasee>",
|
|
inconvertibleErrorCode());
|
|
};
|
|
|
|
auto GetJD = [&](StringRef JDName) -> Expected<JITDylib *> {
|
|
if (JDName.empty()) {
|
|
unsigned AliasArgIdx = Aliases.getPosition(AliasItr - Aliases.begin());
|
|
return std::prev(IdxToJD.lower_bound(AliasArgIdx))->second;
|
|
}
|
|
|
|
auto *JD = S.ES.getJITDylibByName(JDName);
|
|
if (!JD)
|
|
return make_error<StringError>(StringRef("In alias definition \"") +
|
|
*AliasItr + "\" no dylib named " +
|
|
JDName,
|
|
inconvertibleErrorCode());
|
|
|
|
return JD;
|
|
};
|
|
|
|
{
|
|
// First split on '=' to get alias and aliasee.
|
|
StringRef AliasStmt = *AliasItr;
|
|
auto [AliasExpr, AliaseeExpr] = AliasStmt.split('=');
|
|
if (AliaseeExpr.empty())
|
|
return BadExpr();
|
|
|
|
auto [AliasJDName, Alias] = AliasExpr.split(':');
|
|
if (Alias.empty())
|
|
std::swap(AliasJDName, Alias);
|
|
|
|
auto AliasJD = GetJD(AliasJDName);
|
|
if (!AliasJD)
|
|
return AliasJD.takeError();
|
|
|
|
auto [AliaseeJDName, Aliasee] = AliaseeExpr.split(':');
|
|
if (Aliasee.empty())
|
|
std::swap(AliaseeJDName, Aliasee);
|
|
|
|
if (AliaseeJDName.empty() && !AliasJDName.empty())
|
|
AliaseeJDName = AliasJDName;
|
|
auto AliaseeJD = GetJD(AliaseeJDName);
|
|
if (!AliaseeJD)
|
|
return AliaseeJD.takeError();
|
|
|
|
Reexports[{*AliasJD, *AliaseeJD}][S.ES.intern(Alias)] = {
|
|
S.ES.intern(Aliasee), JITSymbolFlags::Exported};
|
|
}
|
|
}
|
|
|
|
for (auto &[JDs, AliasMap] : Reexports) {
|
|
auto [DstJD, SrcJD] = JDs;
|
|
if (auto Err = DstJD->define(reexports(*SrcJD, std::move(AliasMap))))
|
|
return Err;
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error addSectCreates(Session &S,
|
|
const std::map<unsigned, JITDylib *> &IdxToJD) {
|
|
for (auto SCItr = SectCreate.begin(), SCEnd = SectCreate.end();
|
|
SCItr != SCEnd; ++SCItr) {
|
|
|
|
unsigned SCArgIdx = SectCreate.getPosition(SCItr - SectCreate.begin());
|
|
auto &JD = *std::prev(IdxToJD.lower_bound(SCArgIdx))->second;
|
|
|
|
StringRef SCArg(*SCItr);
|
|
|
|
auto [SectAndFileName, ExtraSymbolsString] = SCArg.rsplit('@');
|
|
auto [SectName, FileName] = SectAndFileName.rsplit(',');
|
|
if (SectName.empty())
|
|
return make_error<StringError>("In -sectcreate=" + SCArg +
|
|
", filename component cannot be empty",
|
|
inconvertibleErrorCode());
|
|
if (FileName.empty())
|
|
return make_error<StringError>("In -sectcreate=" + SCArg +
|
|
", filename component cannot be empty",
|
|
inconvertibleErrorCode());
|
|
|
|
auto Content = getFile(FileName);
|
|
if (!Content)
|
|
return Content.takeError();
|
|
|
|
SectCreateMaterializationUnit::ExtraSymbolsMap ExtraSymbols;
|
|
while (!ExtraSymbolsString.empty()) {
|
|
StringRef NextSymPair;
|
|
std::tie(NextSymPair, ExtraSymbolsString) = ExtraSymbolsString.split(',');
|
|
|
|
auto [Sym, OffsetString] = NextSymPair.split('=');
|
|
size_t Offset;
|
|
|
|
if (OffsetString.getAsInteger(0, Offset))
|
|
return make_error<StringError>("In -sectcreate=" + SCArg + ", " +
|
|
OffsetString +
|
|
" is not a valid integer",
|
|
inconvertibleErrorCode());
|
|
|
|
ExtraSymbols[S.ES.intern(Sym)] = {JITSymbolFlags::Exported, Offset};
|
|
}
|
|
|
|
if (auto Err = JD.define(std::make_unique<SectCreateMaterializationUnit>(
|
|
S.ObjLayer, SectName.str(), MemProt::Read, 16, std::move(*Content),
|
|
std::move(ExtraSymbols))))
|
|
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 Linkable = loadLinkableFile(HarnessFile, S.ES.getTargetTriple(),
|
|
LoadArchives::Never);
|
|
if (!Linkable)
|
|
return Linkable.takeError();
|
|
if (auto Err = S.ObjLayer.add(*S.MainJD, std::move(Linkable->first)))
|
|
return Err;
|
|
}
|
|
return Error::success();
|
|
}
|
|
|
|
static Error addObjects(Session &S,
|
|
const std::map<unsigned, JITDylib *> &IdxToJD,
|
|
const DenseSet<unsigned> &LazyLinkIdxs) {
|
|
|
|
// 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());
|
|
const std::string &InputFile = *InputFileItr;
|
|
if (StringRef(InputFile).ends_with(".a") ||
|
|
StringRef(InputFile).ends_with(".lib"))
|
|
continue;
|
|
auto &JD = *std::prev(IdxToJD.lower_bound(InputFileArgIdx))->second;
|
|
bool AddLazy = LazyLinkIdxs.count(InputFileArgIdx);
|
|
LLVM_DEBUG(dbgs() << " " << InputFileArgIdx << ": \"" << InputFile << "\" "
|
|
<< (AddLazy ? " (lazy-linked)" : "") << " to "
|
|
<< JD.getName() << "\n";);
|
|
auto ObjBuffer = loadLinkableFile(InputFile, S.ES.getTargetTriple(),
|
|
LoadArchives::Never);
|
|
if (!ObjBuffer)
|
|
return ObjBuffer.takeError();
|
|
|
|
if (S.HarnessFiles.empty()) {
|
|
if (auto Err =
|
|
S.getLinkLayer(AddLazy).add(JD, std::move(ObjBuffer->first)))
|
|
return Err;
|
|
} else {
|
|
// We're in -harness mode. Use a custom interface for this
|
|
// test object.
|
|
auto ObjInterface =
|
|
getTestObjectFileInterface(S, ObjBuffer->first->getMemBufferRef());
|
|
if (!ObjInterface)
|
|
return ObjInterface.takeError();
|
|
|
|
if (auto Err = S.ObjLayer.add(JD, std::move(ObjBuffer->first),
|
|
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 SmallVector<StringRef, 5> getSearchPathsFromEnvVar(Session &S) {
|
|
// FIXME: Handle EPC environment.
|
|
SmallVector<StringRef, 5> PathVec;
|
|
auto TT = S.ES.getTargetTriple();
|
|
if (TT.isOSBinFormatCOFF())
|
|
StringRef(getenv("PATH")).split(PathVec, ";");
|
|
else if (TT.isOSBinFormatELF())
|
|
StringRef(getenv("LD_LIBRARY_PATH")).split(PathVec, ":");
|
|
|
|
return PathVec;
|
|
}
|
|
|
|
static Error addLibraries(Session &S,
|
|
const std::map<unsigned, JITDylib *> &IdxToJD,
|
|
const DenseSet<unsigned> &LazyLinkIdxs) {
|
|
|
|
// 1. Collect search paths for each JITDylib.
|
|
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";
|
|
}
|
|
});
|
|
|
|
// 2. Collect library loads
|
|
struct LibraryLoad {
|
|
std::string LibName;
|
|
bool IsPath = false;
|
|
unsigned Position;
|
|
ArrayRef<StringRef> CandidateExtensions;
|
|
enum { Standard, Hidden } Modifier;
|
|
};
|
|
|
|
// Queue to load library as in the order as it appears in the argument list.
|
|
std::deque<LibraryLoad> LibraryLoadQueue;
|
|
// Add archive files from the inputs to LibraryLoads.
|
|
for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
|
|
InputFileItr != InputFileEnd; ++InputFileItr) {
|
|
StringRef InputFile = *InputFileItr;
|
|
if (!InputFile.ends_with(".a") && !InputFile.ends_with(".lib"))
|
|
continue;
|
|
LibraryLoad LL;
|
|
LL.LibName = InputFile.str();
|
|
LL.IsPath = true;
|
|
LL.Position = InputFiles.getPosition(InputFileItr - InputFiles.begin());
|
|
LL.CandidateExtensions = {};
|
|
LL.Modifier = LibraryLoad::Standard;
|
|
LibraryLoadQueue.push_back(std::move(LL));
|
|
}
|
|
|
|
// Add -load_hidden arguments to LibraryLoads.
|
|
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 = {};
|
|
LL.Modifier = LibraryLoad::Hidden;
|
|
LibraryLoadQueue.push_back(std::move(LL));
|
|
}
|
|
StringRef StandardExtensions[] = {".so", ".dylib", ".dll", ".a", ".lib"};
|
|
StringRef DynLibExtensionsOnly[] = {".so", ".dylib", ".dll"};
|
|
StringRef ArchiveExtensionsOnly[] = {".a", ".lib"};
|
|
|
|
// Add -lx arguments to LibraryLoads.
|
|
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;
|
|
LibraryLoadQueue.push_back(std::move(LL));
|
|
}
|
|
|
|
// Add -hidden-lx arguments to LibraryLoads.
|
|
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;
|
|
LibraryLoadQueue.push_back(std::move(LL));
|
|
}
|
|
|
|
// Sort library loads by position in the argument list.
|
|
llvm::sort(LibraryLoadQueue,
|
|
[](const LibraryLoad &LHS, const LibraryLoad &RHS) {
|
|
return LHS.Position < RHS.Position;
|
|
});
|
|
|
|
// 3. Process library loads.
|
|
auto AddArchive = [&](JITDylib &JD, const char *Path, const LibraryLoad &LL)
|
|
-> Expected<std::unique_ptr<StaticLibraryDefinitionGenerator>> {
|
|
StaticLibraryDefinitionGenerator::GetObjectFileInterface
|
|
GetObjFileInterface;
|
|
switch (LL.Modifier) {
|
|
case LibraryLoad::Standard:
|
|
GetObjFileInterface = getObjectFileInterface;
|
|
break;
|
|
case LibraryLoad::Hidden:
|
|
GetObjFileInterface = getObjectFileInterfaceHidden;
|
|
S.HiddenArchives.insert(Path);
|
|
break;
|
|
}
|
|
|
|
auto &LinkLayer = S.getLinkLayer(LazyLinkIdxs.count(LL.Position));
|
|
|
|
StaticLibraryDefinitionGenerator::VisitMembersFunction VisitMembers;
|
|
if (AllLoad)
|
|
VisitMembers = StaticLibraryDefinitionGenerator::loadAllObjectFileMembers(
|
|
LinkLayer, JD);
|
|
else if (S.ES.getTargetTriple().isOSBinFormatMachO() && ForceLoadObjC)
|
|
VisitMembers = ForceLoadMachOArchiveMembers(LinkLayer, JD, true);
|
|
|
|
auto G = StaticLibraryDefinitionGenerator::Load(
|
|
LinkLayer, Path, std::move(VisitMembers),
|
|
std::move(GetObjFileInterface));
|
|
if (!G)
|
|
return G.takeError();
|
|
|
|
// Push additional dynamic libraries to search.
|
|
// Note that this mechanism only happens in COFF.
|
|
for (auto FileName : (*G)->getImportedDynamicLibraries()) {
|
|
LibraryLoad NewLL;
|
|
auto FileNameRef = StringRef(FileName);
|
|
if (!FileNameRef.ends_with_insensitive(".dll"))
|
|
return make_error<StringError>(
|
|
"COFF Imported library not ending with dll extension?",
|
|
inconvertibleErrorCode());
|
|
NewLL.LibName = FileNameRef.drop_back(strlen(".dll")).str();
|
|
NewLL.Position = LL.Position;
|
|
NewLL.CandidateExtensions = DynLibExtensionsOnly;
|
|
NewLL.Modifier = LibraryLoad::Standard;
|
|
LibraryLoadQueue.push_front(std::move(NewLL));
|
|
}
|
|
return G;
|
|
};
|
|
|
|
SmallVector<StringRef, 5> SystemSearchPaths;
|
|
if (SearchSystemLibrary.getValue())
|
|
SystemSearchPaths = getSearchPathsFromEnvVar(S);
|
|
while (!LibraryLoadQueue.empty()) {
|
|
bool LibFound = false;
|
|
auto LL = LibraryLoadQueue.front();
|
|
LibraryLoadQueue.pop_front();
|
|
auto &JD = *std::prev(IdxToJD.lower_bound(LL.Position))->second;
|
|
|
|
// If this is the name of a JITDylib then link against that.
|
|
if (auto *LJD = S.ES.getJITDylibByName(LL.LibName)) {
|
|
JD.addToLinkOrder(*LJD);
|
|
continue;
|
|
}
|
|
|
|
if (LL.IsPath) {
|
|
auto G = AddArchive(JD, LL.LibName.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;
|
|
}
|
|
|
|
// Otherwise look through the search paths.
|
|
auto CurJDSearchPaths = JDSearchPaths[&JD];
|
|
for (StringRef SearchPath :
|
|
concat<StringRef>(CurJDSearchPaths, SystemSearchPaths)) {
|
|
for (auto LibExt : LL.CandidateExtensions) {
|
|
SmallVector<char, 256> LibPath;
|
|
LibPath.reserve(SearchPath.size() + strlen("lib") + LL.LibName.size() +
|
|
LibExt.size() + 2); // +2 for pathsep, null term.
|
|
llvm::copy(SearchPath, std::back_inserter(LibPath));
|
|
if (LibExt != ".lib" && LibExt != ".dll")
|
|
sys::path::append(LibPath, "lib" + LL.LibName + LibExt);
|
|
else
|
|
sys::path::append(LibPath, LL.LibName + LibExt);
|
|
LibPath.push_back('\0');
|
|
|
|
// Skip missing or non-regular paths.
|
|
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)) {
|
|
// If there was an error loading the file then skip it.
|
|
LLVM_DEBUG({
|
|
dbgs() << "Library search found \"" << LibPath
|
|
<< "\", but could not identify file type (" << EC.message()
|
|
<< "). Skipping.\n";
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// We identified the magic. Assume that we can load it -- we'll reset
|
|
// in the default case.
|
|
LibFound = true;
|
|
switch (Magic) {
|
|
case file_magic::pecoff_executable:
|
|
case file_magic::elf_shared_object:
|
|
case file_magic::macho_dynamically_linked_shared_lib: {
|
|
if (auto Err = S.loadAndLinkDynamicLibrary(JD, LibPath.data()))
|
|
return Err;
|
|
break;
|
|
}
|
|
case file_magic::archive:
|
|
case file_magic::macho_universal_binary: {
|
|
auto G = AddArchive(JD, 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:
|
|
// This file isn't a recognized library kind.
|
|
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());
|
|
}
|
|
|
|
// Add platform and process symbols if available.
|
|
for (auto &[Idx, JD] : IdxToJD) {
|
|
if (S.PlatformJD)
|
|
JD->addToLinkOrder(*S.PlatformJD);
|
|
if (S.ProcessSymsJD)
|
|
JD->addToLinkOrder(*S.ProcessSymsJD);
|
|
}
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error addSpeculationOrder(Session &S) {
|
|
|
|
if (SpeculateOrder.empty())
|
|
return Error::success();
|
|
|
|
assert(S.LazyLinking && "SpeculateOrder set, but lazy linking not enabled");
|
|
assert(S.LazyLinking->Speculator && "SpeculatoOrder set, but no speculator");
|
|
|
|
auto SpecOrderBuffer = getFile(SpeculateOrder);
|
|
if (!SpecOrderBuffer)
|
|
return SpecOrderBuffer.takeError();
|
|
|
|
StringRef LineStream((*SpecOrderBuffer)->getBuffer());
|
|
std::vector<std::pair<std::string, SymbolStringPtr>> SpecOrder;
|
|
|
|
size_t LineNumber = 0;
|
|
while (!LineStream.empty()) {
|
|
++LineNumber;
|
|
|
|
auto MakeSpecOrderErr = [&](StringRef Reason) {
|
|
return make_error<StringError>("Error in speculation order file \"" +
|
|
SpeculateOrder + "\" on line " +
|
|
Twine(LineNumber) + ": " + Reason,
|
|
inconvertibleErrorCode());
|
|
};
|
|
|
|
StringRef CurLine;
|
|
std::tie(CurLine, LineStream) = LineStream.split('\n');
|
|
CurLine = CurLine.trim();
|
|
if (CurLine.empty())
|
|
continue;
|
|
|
|
auto [JDName, FuncName] = CurLine.split(',');
|
|
|
|
if (FuncName.empty())
|
|
return MakeSpecOrderErr("missing ',' separator");
|
|
|
|
JDName = JDName.trim();
|
|
if (JDName.empty())
|
|
return MakeSpecOrderErr("no value for column 1 (JIT Dylib name)");
|
|
|
|
FuncName = FuncName.trim();
|
|
if (FuncName.empty())
|
|
return MakeSpecOrderErr("no value for column 2 (function name)");
|
|
|
|
SpecOrder.push_back({JDName.str(), S.ES.intern(FuncName)});
|
|
}
|
|
|
|
S.LazyLinking->Speculator->addSpeculationSuggestions(std::move(SpecOrder));
|
|
|
|
return Error::success();
|
|
}
|
|
|
|
static Error addSessionInputs(Session &S) {
|
|
std::map<unsigned, JITDylib *> IdxToJD;
|
|
DenseSet<unsigned> LazyLinkIdxs;
|
|
|
|
for (auto LLItr = LazyLink.begin(), LLEnd = LazyLink.end(); LLItr != LLEnd;
|
|
++LLItr) {
|
|
if (*LLItr)
|
|
LazyLinkIdxs.insert(LazyLink.getPosition(LLItr - LazyLink.begin()) + 1);
|
|
}
|
|
|
|
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 (auto Err = addSectCreates(S, IdxToJD))
|
|
return Err;
|
|
|
|
if (!TestHarnesses.empty())
|
|
if (auto Err = addTestHarnesses(S))
|
|
return Err;
|
|
|
|
if (auto Err = addObjects(S, IdxToJD, LazyLinkIdxs))
|
|
return Err;
|
|
|
|
if (auto Err = addLibraries(S, IdxToJD, LazyLinkIdxs))
|
|
return Err;
|
|
|
|
if (auto Err = addSpeculationOrder(S))
|
|
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;
|
|
};
|
|
} // anonymous namespace
|
|
|
|
static TargetInfo
|
|
getTargetInfo(const Triple &TT,
|
|
const SubtargetFeatures &TF = SubtargetFeatures()) {
|
|
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, "", TF.getString()));
|
|
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, Triple TT, SubtargetFeatures Features) {
|
|
if (CheckFiles.empty())
|
|
return Error::success();
|
|
|
|
S.waitForFilesLinkedFromEntryPointFile();
|
|
|
|
LLVM_DEBUG(dbgs() << "Running checks...\n");
|
|
|
|
auto IsSymbolValid = [&S](StringRef Symbol) {
|
|
auto InternedSymbol = S.ES.getSymbolStringPool()->intern(Symbol);
|
|
return S.isSymbolRegistered(InternedSymbol);
|
|
};
|
|
|
|
auto GetSymbolInfo = [&S](StringRef Symbol) {
|
|
auto InternedSymbol = S.ES.getSymbolStringPool()->intern(Symbol);
|
|
return S.findSymbolInfo(InternedSymbol, "Can not get symbol info");
|
|
};
|
|
|
|
auto GetSectionInfo = [&S](StringRef FileName, StringRef SectionName) {
|
|
return S.findSectionInfo(FileName, SectionName);
|
|
};
|
|
|
|
auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName,
|
|
StringRef KindNameFilter) {
|
|
return S.findStubInfo(FileName, SectionName, KindNameFilter);
|
|
};
|
|
|
|
auto GetGOTInfo = [&S](StringRef FileName, StringRef SectionName) {
|
|
return S.findGOTEntryInfo(FileName, SectionName);
|
|
};
|
|
|
|
RuntimeDyldChecker Checker(
|
|
IsSymbolValid, GetSymbolInfo, GetSectionInfo, GetStubInfo, GetGOTInfo,
|
|
S.ES.getTargetTriple().isLittleEndian() ? llvm::endianness::little
|
|
: llvm::endianness::big,
|
|
TT, StringRef(), Features, 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 Expected<ExecutorSymbolDef> getMainEntryPoint(Session &S) {
|
|
return S.ES.lookup(S.JDSearchOrder, S.ES.intern(EntryPointName));
|
|
}
|
|
|
|
static Expected<ExecutorSymbolDef> getOrcRuntimeEntryPoint(Session &S) {
|
|
std::string RuntimeEntryPoint = "__orc_rt_run_program_wrapper";
|
|
if (S.ES.getTargetTriple().getObjectFormat() == Triple::MachO)
|
|
RuntimeEntryPoint = '_' + RuntimeEntryPoint;
|
|
return S.ES.lookup(S.JDSearchOrder, S.ES.intern(RuntimeEntryPoint));
|
|
}
|
|
|
|
static Expected<ExecutorSymbolDef> getEntryPoint(Session &S) {
|
|
ExecutorSymbolDef EntryPoint;
|
|
|
|
// Find the entry-point function unconditionally, since we want to force
|
|
// it to be materialized to collect stats.
|
|
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 we're running with the ORC runtime then replace the entry-point
|
|
// with the __orc_rt_run_program symbol.
|
|
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;
|
|
if (S.ES.getTargetTriple().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};
|
|
};
|
|
} // namespace
|
|
|
|
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]) + ": ");
|
|
|
|
/// If timers are enabled, create a JITLinkTimers instance.
|
|
std::unique_ptr<JITLinkTimers> Timers =
|
|
ShowTimes ? std::make_unique<JITLinkTimers>() : nullptr;
|
|
|
|
auto [TT, Features] = getFirstFileTripleAndFeatures();
|
|
ExitOnErr(sanitizeArguments(TT, argv[0]));
|
|
|
|
auto S = ExitOnErr(Session::Create(TT, Features));
|
|
|
|
enableStatistics(*S, !OrcRuntime.empty());
|
|
|
|
{
|
|
TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr);
|
|
ExitOnErr(addSessionInputs(*S));
|
|
}
|
|
|
|
if (PhonyExternals)
|
|
addPhonyExternalsGenerator(*S);
|
|
|
|
if (ShowInitialExecutionSessionState)
|
|
S->ES.dump(outs());
|
|
|
|
Expected<ExecutorSymbolDef> EntryPoint((ExecutorSymbolDef()));
|
|
{
|
|
ExpectedAsOutParameter<ExecutorSymbolDef> _(&EntryPoint);
|
|
TimeRegion TR(Timers ? &Timers->LinkTimer : nullptr);
|
|
EntryPoint = getEntryPoint(*S);
|
|
}
|
|
|
|
// Print any reports regardless of whether we succeeded or failed.
|
|
if (ShowEntryExecutionSessionState)
|
|
S->ES.dump(outs());
|
|
|
|
if (ShowAddrs)
|
|
S->dumpSessionInfo(outs());
|
|
|
|
if (!EntryPoint) {
|
|
if (Timers)
|
|
Timers->JITLinkTG.printAll(errs());
|
|
reportLLVMJITLinkError(EntryPoint.takeError());
|
|
ExitOnErr(S->ES.endSession());
|
|
exit(1);
|
|
}
|
|
|
|
ExitOnErr(runChecks(*S, std::move(TT), std::move(Features)));
|
|
|
|
int Result = 0;
|
|
if (!NoExec) {
|
|
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())));
|
|
}
|
|
|
|
// Destroy the session.
|
|
ExitOnErr(S->ES.endSession());
|
|
S.reset();
|
|
|
|
if (Timers)
|
|
Timers->JITLinkTG.printAll(errs());
|
|
|
|
// If the executing code set a test result override then use that.
|
|
if (UseTestResultOverride)
|
|
Result = TestResultOverride;
|
|
|
|
return Result;
|
|
}
|