Path: blob/main/contrib/llvm-project/lld/MachO/DriverUtils.cpp
34878 views
//===- DriverUtils.cpp ----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Config.h"9#include "Driver.h"10#include "InputFiles.h"11#include "ObjC.h"12#include "Target.h"1314#include "lld/Common/Args.h"15#include "lld/Common/CommonLinkerContext.h"16#include "lld/Common/Reproduce.h"17#include "llvm/ADT/CachedHashString.h"18#include "llvm/ADT/DenseMap.h"19#include "llvm/LTO/LTO.h"20#include "llvm/Option/Arg.h"21#include "llvm/Option/ArgList.h"22#include "llvm/Option/Option.h"23#include "llvm/Support/CommandLine.h"24#include "llvm/Support/FileSystem.h"25#include "llvm/Support/Path.h"26#include "llvm/TextAPI/InterfaceFile.h"27#include "llvm/TextAPI/TextAPIReader.h"2829using namespace llvm;30using namespace llvm::MachO;31using namespace llvm::opt;32using namespace llvm::sys;33using namespace lld;34using namespace lld::macho;3536// Create prefix string literals used in Options.td37#define PREFIX(NAME, VALUE) \38static constexpr StringLiteral NAME##_init[] = VALUE; \39static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \40std::size(NAME##_init) - 1);41#include "Options.inc"42#undef PREFIX4344// Create table mapping all options defined in Options.td45static constexpr OptTable::Info optInfo[] = {46#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \47VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \48VALUES) \49{PREFIX, \50NAME, \51HELPTEXT, \52HELPTEXTSFORVARIANTS, \53METAVAR, \54OPT_##ID, \55opt::Option::KIND##Class, \56PARAM, \57FLAGS, \58VISIBILITY, \59OPT_##GROUP, \60OPT_##ALIAS, \61ALIASARGS, \62VALUES},63#include "Options.inc"64#undef OPTION65};6667MachOOptTable::MachOOptTable() : GenericOptTable(optInfo) {}6869// Set color diagnostics according to --color-diagnostics={auto,always,never}70// or --no-color-diagnostics flags.71static void handleColorDiagnostics(InputArgList &args) {72const Arg *arg =73args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,74OPT_no_color_diagnostics);75if (!arg)76return;77if (arg->getOption().getID() == OPT_color_diagnostics) {78lld::errs().enable_colors(true);79} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {80lld::errs().enable_colors(false);81} else {82StringRef s = arg->getValue();83if (s == "always")84lld::errs().enable_colors(true);85else if (s == "never")86lld::errs().enable_colors(false);87else if (s != "auto")88error("unknown option: --color-diagnostics=" + s);89}90}9192InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) {93// Make InputArgList from string vectors.94unsigned missingIndex;95unsigned missingCount;96SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());9798// Expand response files (arguments in the form of @<filename>)99// and then parse the argument again.100cl::ExpandResponseFiles(saver(), cl::TokenizeGNUCommandLine, vec);101InputArgList args = ParseArgs(vec, missingIndex, missingCount);102103// Handle -fatal_warnings early since it converts missing argument warnings104// to errors.105errorHandler().fatalWarnings = args.hasArg(OPT_fatal_warnings);106errorHandler().suppressWarnings = args.hasArg(OPT_w);107108if (missingCount)109error(Twine(args.getArgString(missingIndex)) + ": missing argument");110111handleColorDiagnostics(args);112113for (const Arg *arg : args.filtered(OPT_UNKNOWN)) {114std::string nearest;115if (findNearest(arg->getAsString(args), nearest) > 1)116error("unknown argument '" + arg->getAsString(args) + "'");117else118error("unknown argument '" + arg->getAsString(args) +119"', did you mean '" + nearest + "'");120}121return args;122}123124void MachOOptTable::printHelp(const char *argv0, bool showHidden) const {125OptTable::printHelp(lld::outs(),126(std::string(argv0) + " [options] file...").c_str(),127"LLVM Linker", showHidden);128lld::outs() << "\n";129}130131static std::string rewritePath(StringRef s) {132if (fs::exists(s))133return relativeToRoot(s);134return std::string(s);135}136137static std::string rewriteInputPath(StringRef s) {138// Don't bother rewriting "absolute" paths that are actually under the139// syslibroot; simply rewriting the syslibroot is sufficient.140if (rerootPath(s) == s && fs::exists(s))141return relativeToRoot(s);142return std::string(s);143}144145// Reconstructs command line arguments so that so that you can re-run146// the same command with the same inputs. This is for --reproduce.147std::string macho::createResponseFile(const InputArgList &args) {148SmallString<0> data;149raw_svector_ostream os(data);150151// Copy the command line to the output while rewriting paths.152for (const Arg *arg : args) {153switch (arg->getOption().getID()) {154case OPT_reproduce:155break;156case OPT_INPUT:157os << quote(rewriteInputPath(arg->getValue())) << "\n";158break;159case OPT_o:160os << "-o " << quote(path::filename(arg->getValue())) << "\n";161break;162case OPT_filelist:163if (std::optional<MemoryBufferRef> buffer = readFile(arg->getValue()))164for (StringRef path : args::getLines(*buffer))165os << quote(rewriteInputPath(path)) << "\n";166break;167case OPT_force_load:168case OPT_weak_library:169case OPT_load_hidden:170os << arg->getSpelling() << " "171<< quote(rewriteInputPath(arg->getValue())) << "\n";172break;173case OPT_F:174case OPT_L:175case OPT_bundle_loader:176case OPT_exported_symbols_list:177case OPT_order_file:178case OPT_syslibroot:179case OPT_unexported_symbols_list:180os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue()))181<< "\n";182break;183case OPT_sectcreate:184os << arg->getSpelling() << " " << quote(arg->getValue(0)) << " "185<< quote(arg->getValue(1)) << " "186<< quote(rewritePath(arg->getValue(2))) << "\n";187break;188default:189os << toString(*arg) << "\n";190}191}192return std::string(data);193}194195static void searchedDylib(const Twine &path, bool found) {196if (config->printDylibSearch)197message("searched " + path + (found ? ", found " : ", not found"));198if (!found)199depTracker->logFileNotFound(path);200}201202std::optional<StringRef> macho::resolveDylibPath(StringRef dylibPath) {203// TODO: if a tbd and dylib are both present, we should check to make sure204// they are consistent.205SmallString<261> tbdPath = dylibPath;206path::replace_extension(tbdPath, ".tbd");207bool tbdExists = fs::exists(tbdPath);208searchedDylib(tbdPath, tbdExists);209if (tbdExists)210return saver().save(tbdPath.str());211212bool dylibExists = fs::exists(dylibPath);213searchedDylib(dylibPath, dylibExists);214if (dylibExists)215return saver().save(dylibPath);216return {};217}218219// It's not uncommon to have multiple attempts to load a single dylib,220// especially if it's a commonly re-exported core library.221static DenseMap<CachedHashStringRef, DylibFile *> loadedDylibs;222223DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella,224bool isBundleLoader, bool explicitlyLinked) {225CachedHashStringRef path(mbref.getBufferIdentifier());226DylibFile *&file = loadedDylibs[path];227if (file) {228if (explicitlyLinked)229file->setExplicitlyLinked();230return file;231}232233DylibFile *newFile;234file_magic magic = identify_magic(mbref.getBuffer());235if (magic == file_magic::tapi_file) {236Expected<std::unique_ptr<InterfaceFile>> result = TextAPIReader::get(mbref);237if (!result) {238error("could not load TAPI file at " + mbref.getBufferIdentifier() +239": " + toString(result.takeError()));240return nullptr;241}242file =243make<DylibFile>(**result, umbrella, isBundleLoader, explicitlyLinked);244245// parseReexports() can recursively call loadDylib(). That's fine since246// we wrote the DylibFile we just loaded to the loadDylib cache via the247// `file` reference. But the recursive load can grow loadDylibs, so the248// `file` reference might become invalid after parseReexports() -- so copy249// the pointer it refers to before continuing.250newFile = file;251if (newFile->exportingFile)252newFile->parseReexports(**result);253} else {254assert(magic == file_magic::macho_dynamically_linked_shared_lib ||255magic == file_magic::macho_dynamically_linked_shared_lib_stub ||256magic == file_magic::macho_executable ||257magic == file_magic::macho_bundle);258file = make<DylibFile>(mbref, umbrella, isBundleLoader, explicitlyLinked);259260// parseLoadCommands() can also recursively call loadDylib(). See comment261// in previous block for why this means we must copy `file` here.262newFile = file;263if (newFile->exportingFile)264newFile->parseLoadCommands(mbref);265}266return newFile;267}268269void macho::resetLoadedDylibs() { loadedDylibs.clear(); }270271std::optional<StringRef>272macho::findPathCombination(const Twine &name,273const std::vector<StringRef> &roots,274ArrayRef<StringRef> extensions) {275SmallString<261> base;276for (StringRef dir : roots) {277base = dir;278path::append(base, name);279for (StringRef ext : extensions) {280Twine location = base + ext;281bool exists = fs::exists(location);282searchedDylib(location, exists);283if (exists)284return saver().save(location.str());285}286}287return {};288}289290StringRef macho::rerootPath(StringRef path) {291if (!path::is_absolute(path, path::Style::posix) || path.ends_with(".o"))292return path;293294if (std::optional<StringRef> rerootedPath =295findPathCombination(path, config->systemLibraryRoots))296return *rerootedPath;297298return path;299}300301uint32_t macho::getModTime(StringRef path) {302if (config->zeroModTime)303return 0;304305fs::file_status stat;306if (!fs::status(path, stat))307if (fs::exists(stat))308return toTimeT(stat.getLastModificationTime());309310warn("failed to get modification time of " + path);311return 0;312}313314void macho::printArchiveMemberLoad(StringRef reason, const InputFile *f) {315if (config->printEachFile)316message(toString(f));317if (config->printWhyLoad)318message(reason + " forced load of " + toString(f));319}320321macho::DependencyTracker::DependencyTracker(StringRef path)322: path(path), active(!path.empty()) {323if (active && fs::exists(path) && !fs::can_write(path)) {324warn("Ignoring dependency_info option since specified path is not "325"writeable.");326active = false;327}328}329330void macho::DependencyTracker::write(StringRef version,331const SetVector<InputFile *> &inputs,332StringRef output) {333if (!active)334return;335336std::error_code ec;337raw_fd_ostream os(path, ec, fs::OF_None);338if (ec) {339warn("Error writing dependency info to file");340return;341}342343auto addDep = [&os](DepOpCode opcode, const StringRef &path) {344// XXX: Even though DepOpCode's underlying type is uint8_t,345// this cast is still needed because Clang older than 10.x has a bug,346// where it doesn't know to cast the enum to its underlying type.347// Hence `<< DepOpCode` is ambiguous to it.348os << static_cast<uint8_t>(opcode);349os << path;350os << '\0';351};352353addDep(DepOpCode::Version, version);354355// Sort the input by its names.356std::vector<StringRef> inputNames;357inputNames.reserve(inputs.size());358for (InputFile *f : inputs)359inputNames.push_back(f->getName());360llvm::sort(inputNames);361362for (const StringRef &in : inputNames)363addDep(DepOpCode::Input, in);364365for (const std::string &f : notFounds)366addDep(DepOpCode::NotFound, f);367368addDep(DepOpCode::Output, output);369}370371372