//===- Driver.cpp ---------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "lld/Common/Driver.h" #include "Config.h" #include "InputChunks.h" #include "InputElement.h" #include "MarkLive.h" #include "SymbolTable.h" #include "Writer.h" #include "lld/Common/Args.h" #include "lld/Common/CommonLinkerContext.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Filesystem.h" #include "lld/Common/Memory.h" #include "lld/Common/Reproduce.h" #include "lld/Common/Strings.h" #include "lld/Common/Version.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/llvm-config.h" #include "llvm/Object/Wasm.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" #include "llvm/TargetParser/Host.h" #include #define DEBUG_TYPE "lld" using namespace llvm; using namespace llvm::object; using namespace llvm::sys; using namespace llvm::wasm; namespace lld::wasm { Configuration *config; namespace { // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, #include "Options.inc" #undef OPTION }; // This function is called on startup. We need this for LTO since // LTO calls LLVM functions to compile bitcode files to native code. // Technically this can be delayed until we read bitcode files, but // we don't bother to do lazily because the initialization is fast. static void initLLVM() { InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); } class LinkerDriver { public: void linkerMain(ArrayRef argsArr); private: void createFiles(opt::InputArgList &args); void addFile(StringRef path); void addLibrary(StringRef name); // True if we are in --whole-archive and --no-whole-archive. bool inWholeArchive = false; std::vector files; }; } // anonymous namespace bool link(ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { // This driver-specific context will be freed later by lldMain(). auto *ctx = new CommonLinkerContext; ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput); ctx->e.logName = args::getFilenameWithoutExe(args[0]); ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; config = make(); symtab = make(); initLLVM(); LinkerDriver().linkerMain(args); return errorCount() == 0; } // Create prefix string literals used in Options.td #define PREFIX(NAME, VALUE) \ static constexpr StringLiteral NAME##_init[] = VALUE; \ static constexpr ArrayRef NAME(NAME##_init, \ std::size(NAME##_init) - 1); #include "Options.inc" #undef PREFIX // Create table mapping all options defined in Options.td static constexpr opt::OptTable::Info optInfo[] = { #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, #include "Options.inc" #undef OPTION }; namespace { class WasmOptTable : public opt::GenericOptTable { public: WasmOptTable() : opt::GenericOptTable(optInfo) {} opt::InputArgList parse(ArrayRef argv); }; } // namespace // Set color diagnostics according to -color-diagnostics={auto,always,never} // or -no-color-diagnostics flags. static void handleColorDiagnostics(opt::InputArgList &args) { auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, OPT_no_color_diagnostics); if (!arg) return; if (arg->getOption().getID() == OPT_color_diagnostics) { lld::errs().enable_colors(true); } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { lld::errs().enable_colors(false); } else { StringRef s = arg->getValue(); if (s == "always") lld::errs().enable_colors(true); else if (s == "never") lld::errs().enable_colors(false); else if (s != "auto") error("unknown option: --color-diagnostics=" + s); } } static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { StringRef s = arg->getValue(); if (s != "windows" && s != "posix") error("invalid response file quoting: " + s); if (s == "windows") return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } if (Triple(sys::getProcessTriple()).isOSWindows()) return cl::TokenizeWindowsCommandLine; return cl::TokenizeGNUCommandLine; } // Find a file by concatenating given paths. static std::optional findFile(StringRef path1, const Twine &path2) { SmallString<128> s; path::append(s, path1, path2); if (fs::exists(s)) return std::string(s); return std::nullopt; } opt::InputArgList WasmOptTable::parse(ArrayRef argv) { SmallVector vec(argv.data(), argv.data() + argv.size()); unsigned missingIndex; unsigned missingCount; // We need to get the quoting style for response files before parsing all // options so we parse here before and ignore all the options but // --rsp-quoting. opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount); // Expand response files (arguments in the form of @) // and then parse the argument again. cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec); args = this->ParseArgs(vec, missingIndex, missingCount); handleColorDiagnostics(args); if (missingCount) error(Twine(args.getArgString(missingIndex)) + ": missing argument"); for (auto *arg : args.filtered(OPT_UNKNOWN)) error("unknown argument: " + arg->getAsString(args)); return args; } // Currently we allow a ".imports" to live alongside a library. This can // be used to specify a list of symbols which can be undefined at link // time (imported from the environment. For example libc.a include an // import file that lists the syscall functions it relies on at runtime. // In the long run this information would be better stored as a symbol // attribute/flag in the object file itself. // See: https://github.com/WebAssembly/tool-conventions/issues/35 static void readImportFile(StringRef filename) { if (std::optional buf = readFile(filename)) for (StringRef sym : args::getLines(*buf)) config->allowUndefinedSymbols.insert(sym); } // Returns slices of MB by parsing MB as an archive file. // Each slice consists of a member file in the archive. std::vector static getArchiveMembers(MemoryBufferRef mb) { std::unique_ptr file = CHECK(Archive::create(mb), mb.getBufferIdentifier() + ": failed to parse archive"); std::vector v; Error err = Error::success(); for (const Archive::Child &c : file->children(err)) { MemoryBufferRef mbref = CHECK(c.getMemoryBufferRef(), mb.getBufferIdentifier() + ": could not get the buffer for a child of the archive"); v.push_back(mbref); } if (err) fatal(mb.getBufferIdentifier() + ": Archive::children failed: " + toString(std::move(err))); // Take ownership of memory buffers created for members of thin archives. for (std::unique_ptr &mb : file->takeThinBuffers()) make>(std::move(mb)); return v; } void LinkerDriver::addFile(StringRef path) { std::optional buffer = readFile(path); if (!buffer) return; MemoryBufferRef mbref = *buffer; switch (identify_magic(mbref.getBuffer())) { case file_magic::archive: { SmallString<128> importFile = path; path::replace_extension(importFile, ".imports"); if (fs::exists(importFile)) readImportFile(importFile.str()); // Handle -whole-archive. if (inWholeArchive) { for (MemoryBufferRef &m : getArchiveMembers(mbref)) { auto *object = createObjectFile(m, path); // Mark object as live; object members are normally not // live by default but -whole-archive is designed to treat // them as such. object->markLive(); files.push_back(object); } return; } std::unique_ptr file = CHECK(Archive::create(mbref), path + ": failed to parse archive"); if (!file->isEmpty() && !file->hasSymbolTable()) { error(mbref.getBufferIdentifier() + ": archive has no index; run ranlib to add one"); } files.push_back(make(mbref)); return; } case file_magic::bitcode: case file_magic::wasm_object: files.push_back(createObjectFile(mbref)); break; case file_magic::unknown: if (mbref.getBuffer().starts_with("#STUB")) { files.push_back(make(mbref)); break; } [[fallthrough]]; default: error("unknown file type: " + mbref.getBufferIdentifier()); } } static std::optional findFromSearchPaths(StringRef path) { for (StringRef dir : config->searchPaths) if (std::optional s = findFile(dir, path)) return s; return std::nullopt; } // This is for -l. We'll look for lib.a from // search paths. static std::optional searchLibraryBaseName(StringRef name) { for (StringRef dir : config->searchPaths) { // Currently we don't enable dynamic linking at all unless -shared or -pie // are used, so don't even look for .so files in that case.. if (config->isPic && !config->isStatic) if (std::optional s = findFile(dir, "lib" + name + ".so")) return s; if (std::optional s = findFile(dir, "lib" + name + ".a")) return s; } return std::nullopt; } // This is for -l. static std::optional searchLibrary(StringRef name) { if (name.startswith(":")) return findFromSearchPaths(name.substr(1)); return searchLibraryBaseName(name); } // Add a given library by searching it from input search paths. void LinkerDriver::addLibrary(StringRef name) { if (std::optional path = searchLibrary(name)) addFile(saver().save(*path)); else error("unable to find library -l" + name, ErrorTag::LibNotFound, {name}); } void LinkerDriver::createFiles(opt::InputArgList &args) { for (auto *arg : args) { switch (arg->getOption().getID()) { case OPT_library: addLibrary(arg->getValue()); break; case OPT_INPUT: addFile(arg->getValue()); break; case OPT_Bstatic: config->isStatic = true; break; case OPT_Bdynamic: config->isStatic = false; break; case OPT_whole_archive: inWholeArchive = true; break; case OPT_no_whole_archive: inWholeArchive = false; break; } } if (files.empty() && errorCount() == 0) error("no input files"); } static StringRef getEntry(opt::InputArgList &args) { auto *arg = args.getLastArg(OPT_entry, OPT_no_entry); if (!arg) { if (args.hasArg(OPT_relocatable)) return ""; if (args.hasArg(OPT_shared)) return "__wasm_call_ctors"; return "_start"; } if (arg->getOption().getID() == OPT_no_entry) return ""; return arg->getValue(); } // Determines what we should do if there are remaining unresolved // symbols after the name resolution. static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) { UnresolvedPolicy errorOrWarn = args.hasFlag(OPT_error_unresolved_symbols, OPT_warn_unresolved_symbols, true) ? UnresolvedPolicy::ReportError : UnresolvedPolicy::Warn; if (auto *arg = args.getLastArg(OPT_unresolved_symbols)) { StringRef s = arg->getValue(); if (s == "ignore-all") return UnresolvedPolicy::Ignore; if (s == "import-dynamic") return UnresolvedPolicy::ImportDynamic; if (s == "report-all") return errorOrWarn; error("unknown --unresolved-symbols value: " + s); } return errorOrWarn; } // Parse --build-id or --build-id=