//===- llvm-link.cpp - Low-level LLVM linker ------------------------------===// // // 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 may be invoked in the following manner: // llvm-link a.bc b.bc c.bc -o x.bc // //===----------------------------------------------------------------------===// #include "llvm/Object/Archive.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Linker/Linker.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/SystemUtils.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/WithColor.h" #include "llvm/Transforms/IPO/FunctionImport.h" #include "llvm/Transforms/IPO/Internalize.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" #include #include using namespace llvm; static cl::list InputFilenames(cl::Positional, cl::OneOrMore, cl::desc("")); static cl::list OverridingInputs( "override", cl::ZeroOrMore, cl::value_desc("filename"), cl::desc( "input bitcode file which can override previously defined symbol(s)")); // Option to simulate function importing for testing. This enables using // llvm-link to simulate ThinLTO backend processes. static cl::list Imports( "import", cl::ZeroOrMore, cl::value_desc("function:filename"), cl::desc("Pair of function name and filename, where function should be " "imported from bitcode in filename")); // Option to support testing of function importing. The module summary // must be specified in the case were we request imports via the -import // option, as well as when compiling any module with functions that may be // exported (imported by a different llvm-link -import invocation), to ensure // consistent promotion and renaming of locals. static cl::opt SummaryIndex("summary-index", cl::desc("Module summary index filename"), cl::init(""), cl::value_desc("filename")); static cl::opt OutputFilename("o", cl::desc("Override output filename"), cl::init("-"), cl::value_desc("filename")); static cl::opt Internalize("internalize", cl::desc("Internalize linked symbols")); static cl::opt DisableDITypeMap("disable-debug-info-type-map", cl::desc("Don't use a uniquing type map for debug info")); static cl::opt OnlyNeeded("only-needed", cl::desc("Link only needed symbols")); static cl::opt Force("f", cl::desc("Enable binary output on terminals")); static cl::opt DisableLazyLoad("disable-lazy-loading", cl::desc("Disable lazy module loading")); static cl::opt OutputAssembly("S", cl::desc("Write output as LLVM assembly"), cl::Hidden); static cl::opt Verbose("v", cl::desc("Print information about actions taken")); static cl::opt DumpAsm("d", cl::desc("Print assembly as linked"), cl::Hidden); static cl::opt SuppressWarnings("suppress-warnings", cl::desc("Suppress all linking warnings"), cl::init(false)); static cl::opt PreserveBitcodeUseListOrder( "preserve-bc-uselistorder", cl::desc("Preserve use-list order when writing LLVM bitcode."), cl::init(true), cl::Hidden); static cl::opt PreserveAssemblyUseListOrder( "preserve-ll-uselistorder", cl::desc("Preserve use-list order when writing LLVM assembly."), cl::init(false), cl::Hidden); static ExitOnError ExitOnErr; // Read the specified bitcode file in and return it. This routine searches the // link path for the specified file to try to find it... // static std::unique_ptr loadFile(const char *argv0, const std::string &FN, LLVMContext &Context, bool MaterializeMetadata = true) { SMDiagnostic Err; if (Verbose) errs() << "Loading '" << FN << "'\n"; std::unique_ptr Result; if (DisableLazyLoad) Result = parseIRFile(FN, Err, Context); else Result = getLazyIRFileModule(FN, Err, Context, !MaterializeMetadata); if (!Result) { Err.print(argv0, errs()); return nullptr; } if (MaterializeMetadata) { ExitOnErr(Result->materializeMetadata()); UpgradeDebugInfo(*Result); } return Result; } static std::unique_ptr loadArFile(const char *Argv0, const std::string &ArchiveName, LLVMContext &Context, Linker &L, unsigned OrigFlags, unsigned ApplicableFlags) { std::unique_ptr Result(new Module("ArchiveModule", Context)); if (Verbose) errs() << "Reading library archive file '" << ArchiveName << "' to memory\n"; ErrorOr> Buf = MemoryBuffer::getFile(ArchiveName, -1, false); ExitOnErr(errorCodeToError(Buf.getError())); Error Err = Error::success(); object::Archive Archive(Buf.get()->getMemBufferRef(), Err); ExitOnErr(std::move(Err)); for (const object::Archive::Child &C : Archive.children(Err)) { Expected Ename = C.getName(); if (Error E = Ename.takeError()) { errs() << Argv0 << ": "; WithColor::error() << " failed to read name of archive member" << ArchiveName << "'\n"; return nullptr; }; std::string ChildName = Ename.get().str(); if (Verbose) errs() << "Parsing member '" << ChildName << "' of archive library to module.\n"; SMDiagnostic ParseErr; Expected MemBuf = C.getMemoryBufferRef(); if (Error E = MemBuf.takeError()) { errs() << Argv0 << ": "; WithColor::error() << " loading memory for member '" << ChildName << "' of archive library failed'" << ArchiveName << "'\n"; return nullptr; }; if (!isBitcode(reinterpret_cast (MemBuf.get().getBufferStart()), reinterpret_cast (MemBuf.get().getBufferEnd()))) { errs() << Argv0 << ": "; WithColor::error() << " member of archive is not a bitcode file: '" << ChildName << "'\n"; return nullptr; } std::unique_ptr M = parseIR(MemBuf.get(), ParseErr, Context); if (!M.get()) { errs() << Argv0 << ": "; WithColor::error() << " parsing member '" << ChildName << "' of archive library failed'" << ArchiveName << "'\n"; return nullptr; } if (Verbose) errs() << "Linking member '" << ChildName << "' of archive library.\n"; if (L.linkModules(*Result, std::move(M), ApplicableFlags)) return nullptr; ApplicableFlags = OrigFlags; } // end for each child ExitOnErr(std::move(Err)); return Result; } namespace { /// Helper to load on demand a Module from file and cache it for subsequent /// queries during function importing. class ModuleLazyLoaderCache { /// Cache of lazily loaded module for import. StringMap> ModuleMap; /// Retrieve a Module from the cache or lazily load it on demand. std::function(const char *argv0, const std::string &FileName)> createLazyModule; public: /// Create the loader, Module will be initialized in \p Context. ModuleLazyLoaderCache(std::function( const char *argv0, const std::string &FileName)> createLazyModule) : createLazyModule(std::move(createLazyModule)) {} /// Retrieve a Module from the cache or lazily load it on demand. Module &operator()(const char *argv0, const std::string &FileName); std::unique_ptr takeModule(const std::string &FileName) { auto I = ModuleMap.find(FileName); assert(I != ModuleMap.end()); std::unique_ptr Ret = std::move(I->second); ModuleMap.erase(I); return Ret; } }; // Get a Module for \p FileName from the cache, or load it lazily. Module &ModuleLazyLoaderCache::operator()(const char *argv0, const std::string &Identifier) { auto &Module = ModuleMap[Identifier]; if (!Module) Module = createLazyModule(argv0, Identifier); return *Module; } } // anonymous namespace namespace { struct LLVMLinkDiagnosticHandler : public DiagnosticHandler { bool handleDiagnostics(const DiagnosticInfo &DI) override { unsigned Severity = DI.getSeverity(); switch (Severity) { case DS_Error: WithColor::error(); break; case DS_Warning: if (SuppressWarnings) return true; WithColor::warning(); break; case DS_Remark: case DS_Note: llvm_unreachable("Only expecting warnings and errors"); } DiagnosticPrinterRawOStream DP(errs()); DI.print(DP); errs() << '\n'; return true; } }; } /// Import any functions requested via the -import option. static bool importFunctions(const char *argv0, Module &DestModule) { if (SummaryIndex.empty()) return true; std::unique_ptr Index = ExitOnErr(llvm::getModuleSummaryIndexForFile(SummaryIndex)); // Map of Module -> List of globals to import from the Module FunctionImporter::ImportMapTy ImportList; auto ModuleLoader = [&DestModule](const char *argv0, const std::string &Identifier) { return loadFile(argv0, Identifier, DestModule.getContext(), false); }; ModuleLazyLoaderCache ModuleLoaderCache(ModuleLoader); for (const auto &Import : Imports) { // Identify the requested function and its bitcode source file. size_t Idx = Import.find(':'); if (Idx == std::string::npos) { errs() << "Import parameter bad format: " << Import << "\n"; return false; } std::string FunctionName = Import.substr(0, Idx); std::string FileName = Import.substr(Idx + 1, std::string::npos); // Load the specified source module. auto &SrcModule = ModuleLoaderCache(argv0, FileName); if (verifyModule(SrcModule, &errs())) { errs() << argv0 << ": " << FileName; WithColor::error() << "input module is broken!\n"; return false; } Function *F = SrcModule.getFunction(FunctionName); if (!F) { errs() << "Ignoring import request for non-existent function " << FunctionName << " from " << FileName << "\n"; continue; } // We cannot import weak_any functions without possibly affecting the // order they are seen and selected by the linker, changing program // semantics. if (F->hasWeakAnyLinkage()) { errs() << "Ignoring import request for weak-any function " << FunctionName << " from " << FileName << "\n"; continue; } if (Verbose) errs() << "Importing " << FunctionName << " from " << FileName << "\n"; auto &Entry = ImportList[FileName]; Entry.insert(F->getGUID()); } auto CachedModuleLoader = [&](StringRef Identifier) { return ModuleLoaderCache.takeModule(std::string(Identifier)); }; FunctionImporter Importer(*Index, CachedModuleLoader, /*ClearDSOLocalOnDeclarations=*/false); ExitOnErr(Importer.importFunctions(DestModule, ImportList)); return true; } static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L, const cl::list &Files, unsigned Flags) { // Filter out flags that don't apply to the first file we load. unsigned ApplicableFlags = Flags & Linker::Flags::OverrideFromSrc; // Similar to some flags, internalization doesn't apply to the first file. bool InternalizeLinkedSymbols = false; for (const auto &File : Files) { std::unique_ptr M = (llvm::sys::path::extension(File) == ".a") ? loadArFile(argv0, File, Context, L, Flags, ApplicableFlags) : loadFile(argv0, File, Context); if (!M.get()) { errs() << argv0 << ": "; WithColor::error() << " loading file '" << File << "'\n"; return false; } // Note that when ODR merging types cannot verify input files in here When // doing that debug metadata in the src module might already be pointing to // the destination. if (DisableDITypeMap && verifyModule(*M, &errs())) { errs() << argv0 << ": " << File << ": "; WithColor::error() << "input module is broken!\n"; return false; } // If a module summary index is supplied, load it so linkInModule can treat // local functions/variables as exported and promote if necessary. if (!SummaryIndex.empty()) { std::unique_ptr Index = ExitOnErr(llvm::getModuleSummaryIndexForFile(SummaryIndex)); // Conservatively mark all internal values as promoted, since this tool // does not do the ThinLink that would normally determine what values to // promote. for (auto &I : *Index) { for (auto &S : I.second.SummaryList) { if (GlobalValue::isLocalLinkage(S->linkage())) S->setLinkage(GlobalValue::ExternalLinkage); } } // Promotion if (renameModuleForThinLTO(*M, *Index, /*ClearDSOLocalOnDeclarations=*/false)) return true; } if (Verbose) errs() << "Linking in '" << File << "'\n"; bool Err = false; if (InternalizeLinkedSymbols) { Err = L.linkInModule( std::move(M), ApplicableFlags, [](Module &M, const StringSet<> &GVS) { internalizeModule(M, [&GVS](const GlobalValue &GV) { return !GV.hasName() || (GVS.count(GV.getName()) == 0); }); }); } else { Err = L.linkInModule(std::move(M), ApplicableFlags); } if (Err) return false; // Internalization applies to linking of subsequent files. InternalizeLinkedSymbols = Internalize; // All linker flags apply to linking of subsequent files. ApplicableFlags = Flags; } return true; } int main(int argc, char **argv) { InitLLVM X(argc, argv); ExitOnErr.setBanner(std::string(argv[0]) + ": "); LLVMContext Context; Context.setDiagnosticHandler( std::make_unique(), true); cl::ParseCommandLineOptions(argc, argv, "llvm linker\n"); if (!DisableDITypeMap) Context.enableDebugTypeODRUniquing(); auto Composite = std::make_unique("llvm-link", Context); Linker L(*Composite); unsigned Flags = Linker::Flags::None; if (OnlyNeeded) Flags |= Linker::Flags::LinkOnlyNeeded; // First add all the regular input files if (!linkFiles(argv[0], Context, L, InputFilenames, Flags)) return 1; // Next the -override ones. if (!linkFiles(argv[0], Context, L, OverridingInputs, Flags | Linker::Flags::OverrideFromSrc)) return 1; // Import any functions requested via -import if (!importFunctions(argv[0], *Composite)) return 1; if (DumpAsm) errs() << "Here's the assembly:\n" << *Composite; std::error_code EC; ToolOutputFile Out(OutputFilename, EC, sys::fs::OF_None); if (EC) { WithColor::error() << EC.message() << '\n'; return 1; } if (verifyModule(*Composite, &errs())) { errs() << argv[0] << ": "; WithColor::error() << "linked module is broken!\n"; return 1; } if (Verbose) errs() << "Writing bitcode...\n"; if (OutputAssembly) { Composite->print(Out.os(), nullptr, PreserveAssemblyUseListOrder); } else if (Force || !CheckBitcodeOutputToConsole(Out.os())) WriteBitcodeToFile(*Composite, Out.os(), PreserveBitcodeUseListOrder); // Declare success. Out.keep(); return 0; }