//===- 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/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/ELFNixPlatform.h" #include "llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h" #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" #include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/IndirectionUtils.h" #include "llvm/ExecutionEngine/Orc/MachOPlatform.h" #include "llvm/ExecutionEngine/Orc/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/RegisterEHFrames.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrAnalysis.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Object/COFF.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Timer.h" #include #include #include #ifdef LLVM_ON_UNIX #include #include #include #include #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 InputFiles(cl::Positional, cl::OneOrMore, cl::desc("input files"), cl::cat(JITLinkCategory)); static cl::list LibrarySearchPaths("L", cl::desc("Add dir to the list of library search paths"), cl::Prefix, cl::cat(JITLinkCategory)); static cl::list Libraries("l", cl::desc("Link against library X in the library search paths"), cl::Prefix, cl::cat(JITLinkCategory)); static cl::list 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 LoadHidden("load_hidden", cl::desc("Link against library X with hidden visibility"), cl::cat(JITLinkCategory)); static cl::opt SearchSystemLibrary( "search-sys-lib", cl::desc("Add system library paths to library search paths"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt NoExec("noexec", cl::desc("Do not execute loaded code"), cl::init(false), cl::cat(JITLinkCategory)); static cl::list CheckFiles("check", cl::desc("File containing verifier checks"), cl::cat(JITLinkCategory)); static cl::opt CheckName("check-name", cl::desc("Name of checks to match against"), cl::init("jitlink-check"), cl::cat(JITLinkCategory)); static cl::opt EntryPointName("entry", cl::desc("Symbol to call as main entry point"), cl::init(""), cl::cat(JITLinkCategory)); static cl::list JITDylibs( "jd", cl::desc("Specifies the JITDylib to be used for any subsequent " "input file, -L, and -l arguments"), cl::cat(JITLinkCategory)); static cl::list Dylibs("preload", cl::desc("Pre-load dynamic libraries (e.g. language runtimes " "required by the ORC runtime)"), cl::cat(JITLinkCategory)); static cl::list InputArgv("args", cl::Positional, cl::desc("..."), cl::PositionalEatsArgs, cl::cat(JITLinkCategory)); static cl::opt DebuggerSupport("debugger-support", cl::desc("Enable debugger suppport (default = !-noexec)"), cl::init(true), cl::Hidden, cl::cat(JITLinkCategory)); static cl::opt PerfSupport("perf-support", cl::desc("Enable perf profiling support"), cl::init(false), cl::Hidden, cl::cat(JITLinkCategory)); static cl::opt NoProcessSymbols("no-process-syms", cl::desc("Do not resolve to llvm-jitlink process symbols"), cl::init(false), cl::cat(JITLinkCategory)); static cl::list AbsoluteDefs( "abs", cl::desc("Inject absolute symbol definitions (syntax: =)"), cl::cat(JITLinkCategory)); static cl::list Aliases("alias", cl::desc("Inject symbol aliases (syntax: =)"), cl::cat(JITLinkCategory)); static cl::list SectCreate("sectcreate", cl::desc("given ,[@=,...] " "add the content of to "), cl::cat(JITLinkCategory)); static cl::list TestHarnesses("harness", cl::Positional, cl::desc("Test harness files"), cl::PositionalEatsArgs, cl::cat(JITLinkCategory)); static cl::opt ShowInitialExecutionSessionState( "show-init-es", cl::desc("Print ExecutionSession state before resolving entry point"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt ShowEntryExecutionSessionState( "show-entry-es", cl::desc("Print ExecutionSession state after resolving entry point"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt ShowAddrs( "show-addrs", cl::desc("Print registered symbol, section, got and stub addresses"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt 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 ShowTimes("show-times", cl::desc("Show times for llvm-jitlink phases"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt 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 SlabAddress( "slab-address", cl::desc("Set slab target address (requires -slab-allocate and -noexec)"), cl::init(~0ULL), cl::cat(JITLinkCategory)); static cl::opt 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 ShowRelocatedSectionContents( "show-relocated-section-contents", cl::desc("show section contents after fixups have been applied"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt PhonyExternals( "phony-externals", cl::desc("resolve all otherwise unresolved externals to null"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt OutOfProcessExecutor( "oop-executor", cl::desc("Launch an out-of-process executor to run code"), cl::ValueOptional, cl::cat(JITLinkCategory)); static cl::opt OutOfProcessExecutorConnect( "oop-executor-connect", cl::desc("Connect to an out-of-process executor via TCP"), cl::cat(JITLinkCategory)); static cl::opt OrcRuntime("orc-runtime", cl::desc("Use ORC runtime from given path"), cl::init(""), cl::cat(JITLinkCategory)); static cl::opt AddSelfRelocations( "add-self-relocations", cl::desc("Add relocations to function pointers to the current function"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt ShowErrFailedToMaterialize("show-err-failed-to-materialize", cl::desc("Show FailedToMaterialize errors"), cl::init(false), cl::cat(JITLinkCategory)); static cl::opt UseSharedMemory( "use-shared-memory", cl::desc("Use shared memory to transfer generated code and data"), 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'; } 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 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> 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(ShowErrFailedToMaterialize), ConditionalPrintErr(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; } static Error applyHarnessPromotions(Session &S, LinkGraph &G) { // 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 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, LinkGraph &G) { constexpr orc::ExecutorAddrDiff DumpWidth = 16; static_assert(isPowerOf2_64(DumpWidth), "DumpWidth must be a power of two"); // Put sections in address order. std::vector
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 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( 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> 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("Page size is zero", inconvertibleErrorCode()); return std::make_unique(PageSize, SlabAddress); } void reserve(size_t NumBytes, OnReservedFunction OnReserved) override { InProcessMemoryMapper::reserve( NumBytes, [this, OnReserved = std::move(OnReserved)]( Expected 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 Result) mutable { if (!Result) return OnInitialized(Result.takeError()); OnInitialized(ExecutorAddr(Result->getValue() + DeltaAddr)); }); } void deinitialize(ArrayRef Allocations, OnDeinitializedFunction OnDeInitialized) override { std::vector Addrs(Allocations.size()); for (const auto Base : Allocations) { Addrs.push_back(Base - DeltaAddr); } InProcessMemoryMapper::deinitialize(Addrs, std::move(OnDeInitialized)); } void release(ArrayRef Reservations, OnReleasedFunction OnRelease) override { std::vector 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 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("Invalid numeric format for slab size", inconvertibleErrorCode()); return SlabSize * Units; } static std::unique_ptr 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( SlabSize)); // Otherwise use the standard in-process mapper. return ExitOnErr( MapperJITLinkMemoryManager::CreateWithMapper( SlabSize)); } Expected> 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( SlabSize, SREPC, SAs); } static Expected 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 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; auto InternedName = S.ES.intern(*Name); I->SymbolFlags[InternedName] = 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> launchExecutor() { #ifndef LLVM_ON_UNIX // FIXME: Add support for Windows. return make_error("-" + 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( "-" + 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("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 ExecutorPath, FDSpecifier; { ExecutorPath = std::make_unique(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(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( std::make_unique(), std::move(S), FromExecutor[ReadEnd], ToExecutor[WriteEnd]); #endif } #if LLVM_ON_UNIX && LLVM_ENABLE_THREADS static Error createTCPSocketError(Twine Details) { return make_error( formatv("Failed to connect TCP socket '{0}': {1}", OutOfProcessExecutorConnect, Details), inconvertibleErrorCode()); } static Expected 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> connectToExecutor() { #ifndef LLVM_ON_UNIX // FIXME: Add TCP support for Windows. return make_error("-" + 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( "-" + 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 SockFD = connectTCPSocket(Host.str(), PortStr.str()); if (!SockFD) return SockFD.takeError(); auto S = SimpleRemoteEPC::Setup(); if (UseSharedMemory) S.CreateMemoryManager = createSharedMemoryManager; return SimpleRemoteEPC::Create( std::make_unique(), 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> Session::Create(Triple TT, SubtargetFeatures Features) { std::unique_ptr 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(); EPC = std::make_unique( std::make_shared(), std::make_unique(), std::move(TT), *PageSize, createInProcessMemoryManager()); } Error Err = Error::success(); std::unique_ptr S(new Session(std::move(EPC), Err)); if (Err) return std::move(Err); S->Features = std::move(Features); return std::move(S); } Session::~Session() { if (auto Err = ES.endSession()) ES.reportError(std::move(Err)); } Session::Session(std::unique_ptr 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.getTargetTriple(), 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("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("MachO debugging requires process symbols", inconvertibleErrorCode()); return; } ObjLayer.addPlugin(ExitOnErr(DebugInfoPreservationPlugin::Create())); ObjLayer.addPlugin(ExitOnErr(PerfSupportPlugin::Create( this->ES.getExecutorProcessControl(), *ProcessSymsJD, true, 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(ES, ObjLayer, *PlatformJD, OrcRuntime.c_str())) ES.setPlatform(std::move(*P)); else { Err = P.takeError(); return; } } else if (TT.isOSBinFormatELF()) { if (auto P = ELFNixPlatform::Create(ES, 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("DLLName not ending with .dll", inconvertibleErrorCode()); return loadAndLinkDynamicLibrary(JD, DLLName); }; if (auto P = COFFPlatform::Create(ES, ObjLayer, *PlatformJD, OrcRuntime.c_str(), std::move(LoadDynLibrary))) ES.setPlatform(std::move(*P)); else { Err = P.takeError(); return; } } else { Err = make_error( "-" + OrcRuntime.ArgStr + " specified, but format " + Triple::getObjectFormatTypeName(TT.getObjectFormat()) + " not supported", inconvertibleErrorCode()); return; } } else if (TT.isOSBinFormatELF()) { if (!NoExec) ObjLayer.addPlugin(std::make_unique( ES, ExitOnErr(EPCEHFrameRegistrar::Create(this->ES)))); if (DebuggerSupport) ObjLayer.addPlugin(std::make_unique( 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(""); ExitOnErr(TestResultJD.define(absoluteSymbols( {{ES.intern("llvm_jitlink_setTestResultOverride"), {ExecutorAddr::fromPtr(llvm_jitlink_setTestResultOverride), JITSymbolFlags::Exported}}}))); MainJD->addToLinkOrder(TestResultJD); } ObjLayer.addPlugin(std::make_unique(*this)); // Process any harness files. for (auto &HarnessFile : TestHarnesses) { HarnessFiles.insert(HarnessFile); auto ObjBuffer = ExitOnErr(getFile(HarnessFile)); auto ObjInterface = ExitOnErr(getObjectFileInterface(ES, ObjBuffer->getMemBufferRef())); for (auto &KV : ObjInterface.SymbolFlags) HarnessDefinitions.insert(*KV.first); auto Obj = ExitOnErr( object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); for (auto &Sym : Obj->symbols()) { uint32_t SymFlags = ExitOnErr(Sym.getFlags()); auto Name = ExitOnErr(Sym.getName()); if (Name.empty()) continue; if (SymFlags & object::BasicSymbolRef::SF_Undefined) HarnessExternals.insert(Name); } } // 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(const Triple &TT, PassConfiguration &PassConfig) { 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("Unsupported object format for GOT/stub " "registration", inconvertibleErrorCode()); }); if (ShowGraphsRegex) PassConfig.PostFixupPasses.push_back([this](LinkGraph &G) -> Error { // 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) { return applyHarnessPromotions(*this, G); }); if (ShowRelocatedSectionContents) PassConfig.PostFixupPasses.push_back([](LinkGraph &G) -> Error { outs() << "Relocated section contents for " << G.getName() << ":\n"; dumpSectionContents(outs(), G); return Error::success(); }); if (AddSelfRelocations) PassConfig.PostPrunePasses.push_back(addSelfRelocations); } Expected Session::getOrLoadDynamicLibrary(StringRef LibPath) { auto It = DynLibJDs.find(LibPath.str()); 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("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("Unexpected zero-fill symbol in section " + Sym.getBlock().getSection().getName(), inconvertibleErrorCode()); auto TS = GetSymbolTarget(G, Sym.getBlock()); if (!TS) return TS.takeError(); SmallVectorImpl &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("Unexpected zero-fill symbol in section " + Sym.getBlock().getSection().getName(), inconvertibleErrorCode()); auto Target = GetSymbolTarget(G, Sym.getBlock()); if (!Target) return Target.takeError(); SmallVectorImpl &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::findFileInfo(StringRef FileName) { auto FileInfoItr = FileInfos.find(FileName); if (FileInfoItr == FileInfos.end()) return make_error("file \"" + FileName + "\" not recognized", inconvertibleErrorCode()); return FileInfoItr->second; } Expected 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("no section \"" + SectionName + "\" registered for file \"" + FileName + "\"", inconvertibleErrorCode()); return SecInfoItr->second; } class MemoryMatcher { public: MemoryMatcher(ArrayRef Content) : Pos(Content.data()), End(Pos + Content.size()) {} template bool matchMask(MaskType Mask) { if (Mask == (Mask & *reinterpret_cast(Pos))) { Pos += sizeof(MaskType); return true; } return false; } template bool matchEqual(ValueType Value) { if (Value == *reinterpret_cast(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(0xe300c000); auto Armv7BxR12le = byte_swap(0xe12fff1c); auto Thumbv7MovWTle = byte_swap(0x0c00f240); auto Thumbv7BxR12le = byte_swap(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::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("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 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() ? "" : Kind.str()) + "\", "; } if (Matches.empty()) return make_error( "\"" + 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( "\"" + 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::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("no GOT entry for \"" + TargetName + "\" registered for file \"" + FileName + "\"", inconvertibleErrorCode()); return GOTInfoItr->second; } bool Session::isSymbolRegistered(StringRef SymbolName) { return SymbolInfos.count(SymbolName); } Expected Session::findSymbolInfo(StringRef SymbolName, Twine ErrorMsgStem) { auto SymInfoItr = SymbolInfos.find(SymbolName); if (SymInfoItr == SymbolInfos.end()) return make_error(ErrorMsgStem + ": symbol " + SymbolName + " not found", inconvertibleErrorCode()); return SymInfoItr->second; } } // end namespace llvm static std::pair getFirstFileTripleAndFeatures() { static std::pair FirstTTAndFeatures = []() { assert(!InputFiles.empty() && "InputFiles can not be empty"); for (auto InputFile : InputFiles) { auto ObjBuffer = ExitOnErr(getFile(InputFile)); file_magic Magic = identify_magic(ObjBuffer->getBuffer()); switch (Magic) { case file_magic::coff_object: case file_magic::elf_relocatable: case file_magic::macho_object: { auto Obj = ExitOnErr( object::ObjectFile::createObjectFile(ObjBuffer->getMemBufferRef())); Triple TT = Obj->makeTriple(); if (Magic == file_magic::coff_object) { // 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); return std::make_pair(TT, Features); } default: break; } } return std::make_pair(Triple(), SubtargetFeatures()); }(); 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("-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( "-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( "-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("-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( "-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 ?)"; } } } // Only one of -oop-executor and -oop-executor-connect can be used. if (!!OutOfProcessExecutor.getNumOccurrences() && !!OutOfProcessExecutorConnect.getNumOccurrences()) return make_error( "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(&sanitizeArguments))); sys::path::remove_filename(OOPExecutorPath); sys::path::append(OOPExecutorPath, "llvm-jitlink-executor"); OutOfProcessExecutor = OOPExecutorPath.str().str(); } return Error::success(); } static void addPhonyExternalsGenerator(Session &S) { S.MainJD->addGenerator(std::make_unique()); } static Error createJITDylibs(Session &S, std::map &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 &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("Invalid absolute define \"" + AbsDefStmt + "\". Syntax: =", 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("Invalid address expression \"" + AddrStr + "\" in absolute symbol definition \"" + AbsDefStmt + "\"", inconvertibleErrorCode()); ExecutorSymbolDef AbsDef(ExecutorAddr(Addr), JITSymbolFlags::Exported); if (auto Err = JD.define(absoluteSymbols({{S.ES.intern(Name), AbsDef}}))) return Err; // Register the absolute symbol with the session symbol infos. S.SymbolInfos[Name] = {ArrayRef(), Addr, AbsDef.getFlags().getTargetFlags()}; } return Error::success(); } static Error addAliases(Session &S, const std::map &IdxToJD) { // Define absolute symbols. LLVM_DEBUG(dbgs() << "Defining aliases...\n"); DenseMap, SymbolAliasMap> Reexports; for (auto AliasItr = Aliases.begin(), AliasEnd = Aliases.end(); AliasItr != AliasEnd; ++AliasItr) { auto BadExpr = [&]() { return make_error( "Invalid alias definition \"" + *AliasItr + "\". Syntax: [:]=[:]", inconvertibleErrorCode()); }; auto GetJD = [&](StringRef JDName) -> Expected { 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(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 &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.split('@'); auto [SectName, FileName] = SectAndFileName.rsplit(','); if (SectName.empty()) return make_error("In -sectcreate=" + SCArg + ", filename component cannot be empty", inconvertibleErrorCode()); if (FileName.empty()) return make_error("In -sectcreate=" + SCArg + ", filename component cannot be empty", inconvertibleErrorCode()); auto Content = MemoryBuffer::getFile(FileName); if (!Content) return createFileError(FileName, errorCodeToError(Content.getError())); 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("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( 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 ObjBuffer = getFile(HarnessFile); if (!ObjBuffer) return ObjBuffer.takeError(); if (auto Err = S.ObjLayer.add(*S.MainJD, std::move(*ObjBuffer))) return Err; } return Error::success(); } static Error addObjects(Session &S, const std::map &IdxToJD) { // 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; LLVM_DEBUG(dbgs() << " " << InputFileArgIdx << ": \"" << InputFile << "\" to " << JD.getName() << "\n";); auto ObjBuffer = getFile(InputFile); if (!ObjBuffer) return ObjBuffer.takeError(); if (S.HarnessFiles.empty()) { if (auto Err = S.ObjLayer.add(JD, std::move(*ObjBuffer))) return Err; } else { // We're in -harness mode. Use a custom interface for this // test object. auto ObjInterface = getTestObjectFileInterface(S, (*ObjBuffer)->getMemBufferRef()); if (!ObjInterface) return ObjInterface.takeError(); if (auto Err = S.ObjLayer.add(JD, std::move(*ObjBuffer), std::move(*ObjInterface))) return Err; } } return Error::success(); } static Expected 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 getSearchPathsFromEnvVar(Session &S) { // FIXME: Handle EPC environment. SmallVector 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 &IdxToJD) { // 1. Collect search paths for each JITDylib. DenseMap> 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("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; StringRef *CandidateExtensions; enum { Standard, Hidden } Modifier; }; // Queue to load library as in the order as it appears in the argument list. std::deque 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 = nullptr; 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 = nullptr; 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)); } // If there are any load- options then turn on flag overrides // to avoid flag mismatch errors. if (!LibrariesHidden.empty() || !LoadHidden.empty()) S.ObjLayer.setOverrideObjectFlagsWithResponsibilityFlags(true); // 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 = [&](const char *Path, const LibraryLoad &LL) -> Expected> { unique_function( ExecutionSession & ES, MemoryBufferRef ObjBuffer)> GetObjFileInterface; switch (LL.Modifier) { case LibraryLoad::Standard: GetObjFileInterface = getObjectFileInterface; break; case LibraryLoad::Hidden: GetObjFileInterface = getObjectFileInterfaceHidden; break; } auto G = StaticLibraryDefinitionGenerator::Load( S.ObjLayer, Path, 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( "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 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(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(CurJDSearchPaths, SystemSearchPaths)) { for (const char *LibExt : {".dylib", ".so", ".dll", ".a", ".lib"}) { SmallVector LibPath; LibPath.reserve(SearchPath.size() + strlen("lib") + LL.LibName.size() + strlen(LibExt) + 2); // +2 for pathsep, null term. llvm::copy(SearchPath, std::back_inserter(LibPath)); if (StringRef(LibExt) != ".lib" && StringRef(LibExt) != ".dll") sys::path::append(LibPath, "lib" + LL.LibName + LibExt); else sys::path::append(LibPath, LL.LibName + LibExt); LibPath.push_back('\0'); // 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(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("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 addSessionInputs(Session &S) { std::map IdxToJD; if (auto Err = createJITDylibs(S, IdxToJD)) return Err; if (auto Err = addAbsoluteSymbols(S, IdxToJD)) return Err; if (auto Err = addAliases(S, IdxToJD)) return Err; if (auto Err = addSectCreates(S, IdxToJD)) return Err; if (!TestHarnesses.empty()) if (auto Err = addTestHarnesses(S)) return Err; if (auto Err = addObjects(S, IdxToJD)) return Err; if (auto Err = addLibraries(S, IdxToJD)) return Err; return Error::success(); } namespace { struct TargetInfo { const Target *TheTarget; std::unique_ptr STI; std::unique_ptr MRI; std::unique_ptr MAI; std::unique_ptr Ctx; std::unique_ptr Disassembler; std::unique_ptr MII; std::unique_ptr MIA; std::unique_ptr 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("Error accessing target '" + TripleName + "': " + ErrorStr, inconvertibleErrorCode())); std::unique_ptr STI( TheTarget->createMCSubtargetInfo(TripleName, "", TF.getString())); if (!STI) ExitOnErr( make_error("Unable to create subtarget for " + TripleName, inconvertibleErrorCode())); std::unique_ptr MRI(TheTarget->createMCRegInfo(TripleName)); if (!MRI) ExitOnErr(make_error("Unable to create target register info " "for " + TripleName, inconvertibleErrorCode())); MCTargetOptions MCOptions; std::unique_ptr MAI( TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); if (!MAI) ExitOnErr(make_error("Unable to create target asm info " + TripleName, inconvertibleErrorCode())); auto Ctx = std::make_unique(Triple(TripleName), MAI.get(), MRI.get(), STI.get()); std::unique_ptr Disassembler( TheTarget->createMCDisassembler(*STI, *Ctx)); if (!Disassembler) ExitOnErr(make_error("Unable to create disassembler for " + TripleName, inconvertibleErrorCode())); std::unique_ptr MII(TheTarget->createMCInstrInfo()); if (!MII) ExitOnErr(make_error("Unable to create instruction info for" + TripleName, inconvertibleErrorCode())); std::unique_ptr MIA( TheTarget->createMCInstrAnalysis(MII.get())); if (!MIA) ExitOnErr(make_error( "Unable to create instruction analysis for" + TripleName, inconvertibleErrorCode())); std::unique_ptr InstPrinter( TheTarget->createMCInstPrinter(Triple(TripleName), 0, *MAI, *MII, *MRI)); if (!InstPrinter) ExitOnErr(make_error( "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(); LLVM_DEBUG(dbgs() << "Running checks...\n"); auto IsSymbolValid = [&S](StringRef Symbol) { return S.isSymbolRegistered(Symbol); }; auto GetSymbolInfo = [&S](StringRef Symbol) { return S.findSymbolInfo(Symbol, "Can not get symbol info"); }; auto GetSectionInfo = [&S](StringRef FileName, StringRef SectionName) { return S.findSectionInfo(FileName, SectionName); }; auto GetStubInfo = [&S](StringRef FileName, StringRef SectionName, 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( "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 getMainEntryPoint(Session &S) { return S.ES.lookup(S.JDSearchOrder, S.ES.intern(EntryPointName)); } static Expected 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 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 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); int64_t Result; if (auto Err = S.ES.callSPSWrapper( EntryPointAddr, Result, S.MainJD->getName(), DemangledEntryPoint, static_cast &>(InputArgv))) return std::move(Err); return Result; } static Expected 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 Timers = ShowTimes ? std::make_unique() : 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 EntryPoint((ExecutorSymbolDef())); { ExpectedAsOutParameter _(&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()); 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; }