brintos

brintos / llvm-project-archived public Read only

0
0
Text · 30.6 KiB · 688f6aa Raw
890 lines · cpp
1//===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This program is a utility that aims to be a dropin replacement for Darwin's10// dsymutil.11//===----------------------------------------------------------------------===//12 13#include "dsymutil.h"14#include "BinaryHolder.h"15#include "CFBundle.h"16#include "DebugMap.h"17#include "DwarfLinkerForBinary.h"18#include "LinkUtils.h"19#include "MachOUtils.h"20#include "Reproducer.h"21#include "llvm/ADT/STLExtras.h"22#include "llvm/ADT/SmallString.h"23#include "llvm/ADT/SmallVector.h"24#include "llvm/ADT/StringExtras.h"25#include "llvm/ADT/StringRef.h"26#include "llvm/DebugInfo/DIContext.h"27#include "llvm/DebugInfo/DWARF/DWARFContext.h"28#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"29#include "llvm/MC/MCSubtargetInfo.h"30#include "llvm/Object/Binary.h"31#include "llvm/Object/MachO.h"32#include "llvm/Option/Arg.h"33#include "llvm/Option/ArgList.h"34#include "llvm/Option/Option.h"35#include "llvm/Support/CommandLine.h"36#include "llvm/Support/CrashRecoveryContext.h"37#include "llvm/Support/FileCollector.h"38#include "llvm/Support/FileSystem.h"39#include "llvm/Support/FormatVariadic.h"40#include "llvm/Support/LLVMDriver.h"41#include "llvm/Support/Path.h"42#include "llvm/Support/TargetSelect.h"43#include "llvm/Support/ThreadPool.h"44#include "llvm/Support/WithColor.h"45#include "llvm/Support/raw_ostream.h"46#include "llvm/Support/thread.h"47#include "llvm/TargetParser/Triple.h"48#include <algorithm>49#include <cstdint>50#include <cstdlib>51#include <string>52#include <system_error>53 54using namespace llvm;55using namespace llvm::dsymutil;56using namespace object;57using namespace llvm::dwarf_linker;58 59namespace {60enum ID {61  OPT_INVALID = 0, // This is not an option ID.62#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),63#include "Options.inc"64#undef OPTION65};66 67#define OPTTABLE_STR_TABLE_CODE68#include "Options.inc"69#undef OPTTABLE_STR_TABLE_CODE70 71#define OPTTABLE_PREFIXES_TABLE_CODE72#include "Options.inc"73#undef OPTTABLE_PREFIXES_TABLE_CODE74 75using namespace llvm::opt;76static constexpr opt::OptTable::Info InfoTable[] = {77#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),78#include "Options.inc"79#undef OPTION80};81 82class DsymutilOptTable : public opt::GenericOptTable {83public:84  DsymutilOptTable()85      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}86};87} // namespace88 89enum class DWARFVerify : uint8_t {90  None = 0,91  Input = 1 << 0,92  Output = 1 << 1,93  OutputOnValidInput = 1 << 2,94  All = Input | Output,95  Auto = Input | OutputOnValidInput,96#if !defined(NDEBUG) || defined(EXPENSIVE_CHECKS)97  Default = Auto98#else99  Default = None100#endif101};102 103inline bool flagIsSet(DWARFVerify Flags, DWARFVerify SingleFlag) {104  return static_cast<uint8_t>(Flags) & static_cast<uint8_t>(SingleFlag);105}106 107struct DsymutilOptions {108  bool DumpDebugMap = false;109  bool DumpStab = false;110  bool Flat = false;111  bool InputIsYAMLDebugMap = false;112  bool ForceKeepFunctionForStatic = false;113  bool NoObjectTimestamp = false;114  std::string OutputFile;115  std::string Toolchain;116  std::string ReproducerPath;117  std::vector<std::string> Archs;118  std::vector<std::string> InputFiles;119  unsigned NumThreads;120  DWARFVerify Verify = DWARFVerify::Default;121  ReproducerMode ReproMode = ReproducerMode::GenerateOnCrash;122  dsymutil::LinkOptions LinkOpts;123};124 125/// Return a list of input files. This function has logic for dealing with the126/// special case where we might have dSYM bundles as input. The function127/// returns an error when the directory structure doesn't match that of a dSYM128/// bundle.129static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args,130                                                    bool DsymAsInput) {131  std::vector<std::string> InputFiles;132  for (auto *File : Args.filtered(OPT_INPUT))133    InputFiles.push_back(File->getValue());134 135  if (!DsymAsInput)136    return InputFiles;137 138  // If we are updating, we might get dSYM bundles as input.139  std::vector<std::string> Inputs;140  for (const auto &Input : InputFiles) {141    if (!sys::fs::is_directory(Input)) {142      Inputs.push_back(Input);143      continue;144    }145 146    // Make sure that we're dealing with a dSYM bundle.147    SmallString<256> BundlePath(Input);148    sys::path::append(BundlePath, "Contents", "Resources", "DWARF");149    if (!sys::fs::is_directory(BundlePath))150      return make_error<StringError>(151          Input + " is a directory, but doesn't look like a dSYM bundle.",152          inconvertibleErrorCode());153 154    // Create a directory iterator to iterate over all the entries in the155    // bundle.156    std::error_code EC;157    sys::fs::directory_iterator DirIt(BundlePath, EC);158    sys::fs::directory_iterator DirEnd;159    if (EC)160      return errorCodeToError(EC);161 162    // Add each entry to the list of inputs.163    while (DirIt != DirEnd) {164      Inputs.push_back(DirIt->path());165      DirIt.increment(EC);166      if (EC)167        return errorCodeToError(EC);168    }169  }170  return Inputs;171}172 173// Verify that the given combination of options makes sense.174static Error verifyOptions(const DsymutilOptions &Options) {175  if (Options.LinkOpts.Verbose && Options.LinkOpts.Quiet) {176    return make_error<StringError>(177        "--quiet and --verbose cannot be specified together",178        errc::invalid_argument);179  }180 181  if (Options.InputFiles.empty()) {182    return make_error<StringError>("no input files specified",183                                   errc::invalid_argument);184  }185 186  if (!Options.Flat && Options.OutputFile == "-")187    return make_error<StringError>(188        "cannot emit to standard output without --flat.",189        errc::invalid_argument);190 191  if (Options.InputFiles.size() > 1 && Options.Flat &&192      !Options.OutputFile.empty())193    return make_error<StringError>(194        "cannot use -o with multiple inputs in flat mode.",195        errc::invalid_argument);196 197  if (!Options.ReproducerPath.empty() &&198      Options.ReproMode != ReproducerMode::Use)199    return make_error<StringError>(200        "cannot combine --gen-reproducer and --use-reproducer.",201        errc::invalid_argument);202 203  return Error::success();204}205 206static Expected<DsymutilAccelTableKind>207getAccelTableKind(opt::InputArgList &Args) {208  if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) {209    StringRef S = Accelerator->getValue();210    if (S == "Apple")211      return DsymutilAccelTableKind::Apple;212    if (S == "Dwarf")213      return DsymutilAccelTableKind::Dwarf;214    if (S == "Pub")215      return DsymutilAccelTableKind::Pub;216    if (S == "Default")217      return DsymutilAccelTableKind::Default;218    if (S == "None")219      return DsymutilAccelTableKind::None;220    return make_error<StringError>("invalid accelerator type specified: '" + S +221                                       "'. Supported values are 'Apple', "222                                       "'Dwarf', 'Pub', 'Default' and 'None'.",223                                   inconvertibleErrorCode());224  }225  return DsymutilAccelTableKind::Default;226}227 228static Expected<DsymutilDWARFLinkerType>229getDWARFLinkerType(opt::InputArgList &Args) {230  if (opt::Arg *LinkerType = Args.getLastArg(OPT_linker)) {231    StringRef S = LinkerType->getValue();232    if (S == "classic")233      return DsymutilDWARFLinkerType::Classic;234    if (S == "parallel")235      return DsymutilDWARFLinkerType::Parallel;236    return make_error<StringError>("invalid DWARF linker type specified: '" +237                                       S +238                                       "'. Supported values are 'classic', "239                                       "'parallel'.",240                                   inconvertibleErrorCode());241  }242 243  return DsymutilDWARFLinkerType::Classic;244}245 246static Expected<ReproducerMode> getReproducerMode(opt::InputArgList &Args) {247  if (Args.hasArg(OPT_gen_reproducer))248    return ReproducerMode::GenerateOnExit;249  if (opt::Arg *Reproducer = Args.getLastArg(OPT_reproducer)) {250    StringRef S = Reproducer->getValue();251    if (S == "GenerateOnExit")252      return ReproducerMode::GenerateOnExit;253    if (S == "GenerateOnCrash")254      return ReproducerMode::GenerateOnCrash;255    if (S == "Off")256      return ReproducerMode::Off;257    return make_error<StringError>(258        "invalid reproducer mode: '" + S +259            "'. Supported values are 'GenerateOnExit', 'GenerateOnCrash', "260            "'Off'.",261        inconvertibleErrorCode());262  }263  return ReproducerMode::GenerateOnCrash;264}265 266static Expected<DWARFVerify> getVerifyKind(opt::InputArgList &Args) {267  if (Args.hasArg(OPT_verify))268    return DWARFVerify::Output;269  if (opt::Arg *Verify = Args.getLastArg(OPT_verify_dwarf)) {270    StringRef S = Verify->getValue();271    if (S == "input")272      return DWARFVerify::Input;273    if (S == "output")274      return DWARFVerify::Output;275    if (S == "all")276      return DWARFVerify::All;277    if (S == "auto")278      return DWARFVerify::Auto;279    if (S == "none")280      return DWARFVerify::None;281    return make_error<StringError>("invalid verify type specified: '" + S +282                                       "'. Supported values are 'none', "283                                       "'input', 'output', 'all' and 'auto'.",284                                   inconvertibleErrorCode());285  }286  return DWARFVerify::Default;287}288 289/// Parses the command line options into the LinkOptions struct and performs290/// some sanity checking. Returns an error in case the latter fails.291static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {292  DsymutilOptions Options;293 294  Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);295  Options.DumpStab = Args.hasArg(OPT_symtab);296  Options.Flat = Args.hasArg(OPT_flat);297  Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);298  Options.NoObjectTimestamp = Args.hasArg(OPT_no_object_timestamp);299 300  if (Expected<DWARFVerify> Verify = getVerifyKind(Args)) {301    Options.Verify = *Verify;302  } else {303    return Verify.takeError();304  }305 306  Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr);307  Options.LinkOpts.VerifyInputDWARF =308      flagIsSet(Options.Verify, DWARFVerify::Input);309  Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output);310  Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);311  Options.LinkOpts.Update = Args.hasArg(OPT_update);312  Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);313  Options.LinkOpts.Quiet = Args.hasArg(OPT_quiet);314  Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);315  Options.LinkOpts.Fat64 = Args.hasArg(OPT_fat64);316  Options.LinkOpts.KeepFunctionForStatic =317      Args.hasArg(OPT_keep_func_for_static);318 319  if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) {320    Options.ReproMode = ReproducerMode::Use;321    Options.ReproducerPath = ReproducerPath->getValue();322  } else {323    if (Expected<ReproducerMode> ReproMode = getReproducerMode(Args)) {324      Options.ReproMode = *ReproMode;325    } else {326      return ReproMode.takeError();327    }328  }329 330  if (Expected<DsymutilAccelTableKind> AccelKind = getAccelTableKind(Args)) {331    Options.LinkOpts.TheAccelTableKind = *AccelKind;332  } else {333    return AccelKind.takeError();334  }335 336  if (Expected<DsymutilDWARFLinkerType> DWARFLinkerType =337          getDWARFLinkerType(Args)) {338    Options.LinkOpts.DWARFLinkerType = *DWARFLinkerType;339  } else {340    return DWARFLinkerType.takeError();341  }342 343  if (Expected<std::vector<std::string>> InputFiles =344          getInputs(Args, Options.LinkOpts.Update)) {345    Options.InputFiles = std::move(*InputFiles);346  } else {347    return InputFiles.takeError();348  }349 350  for (auto *Arch : Args.filtered(OPT_arch))351    Options.Archs.push_back(Arch->getValue());352 353  if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))354    Options.LinkOpts.PrependPath = OsoPrependPath->getValue();355 356  for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) {357    auto Split = StringRef(Arg).split('=');358    Options.LinkOpts.ObjectPrefixMap.insert(359        {std::string(Split.first), std::string(Split.second)});360  }361 362  if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))363    Options.OutputFile = OutputFile->getValue();364 365  if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))366    Options.Toolchain = Toolchain->getValue();367 368  if (Args.hasArg(OPT_assembly))369    Options.LinkOpts.FileType = DWARFLinkerBase::OutputFileType::Assembly;370 371  if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))372    Options.LinkOpts.Threads = atoi(NumThreads->getValue());373  else374    Options.LinkOpts.Threads = 0; // Use all available hardware threads375 376  if (Options.DumpDebugMap || Options.LinkOpts.Verbose)377    Options.LinkOpts.Threads = 1;378 379  if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path))380    Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue();381 382  if (opt::Arg *RemarksOutputFormat =383          Args.getLastArg(OPT_remarks_output_format)) {384    if (Expected<remarks::Format> FormatOrErr =385            remarks::parseFormat(RemarksOutputFormat->getValue()))386      Options.LinkOpts.RemarksFormat = *FormatOrErr;387    else388      return FormatOrErr.takeError();389  }390 391  Options.LinkOpts.RemarksKeepAll =392      !Args.hasArg(OPT_remarks_drop_without_debug);393 394  Options.LinkOpts.IncludeSwiftModulesFromInterface =395      Args.hasArg(OPT_include_swiftmodules_from_interface);396 397  if (opt::Arg *BuildVariantSuffix = Args.getLastArg(OPT_build_variant_suffix))398    Options.LinkOpts.BuildVariantSuffix = BuildVariantSuffix->getValue();399 400  for (auto *SearchPath : Args.filtered(OPT_dsym_search_path))401    Options.LinkOpts.DSYMSearchPaths.push_back(SearchPath->getValue());402 403  if (Error E = verifyOptions(Options))404    return std::move(E);405  return Options;406}407 408static Error createPlistFile(StringRef Bin, StringRef BundleRoot,409                             StringRef Toolchain) {410  // Create plist file to write to.411  SmallString<128> InfoPlist(BundleRoot);412  sys::path::append(InfoPlist, "Contents/Info.plist");413  std::error_code EC;414  raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF);415  if (EC)416    return make_error<StringError>(417        "cannot create Plist: " + toString(errorCodeToError(EC)), EC);418 419  CFBundleInfo BI = getBundleInfo(Bin);420 421  if (BI.IDStr.empty()) {422    StringRef BundleID = *sys::path::rbegin(BundleRoot);423    if (sys::path::extension(BundleRoot) == ".dSYM")424      BI.IDStr = std::string(sys::path::stem(BundleID));425    else426      BI.IDStr = std::string(BundleID);427  }428 429  // Print out information to the plist file.430  PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"431     << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "432     << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"433     << "<plist version=\"1.0\">\n"434     << "\t<dict>\n"435     << "\t\t<key>CFBundleDevelopmentRegion</key>\n"436     << "\t\t<string>English</string>\n"437     << "\t\t<key>CFBundleIdentifier</key>\n"438     << "\t\t<string>com.apple.xcode.dsym.";439  printHTMLEscaped(BI.IDStr, PL);440  PL << "</string>\n"441     << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"442     << "\t\t<string>6.0</string>\n"443     << "\t\t<key>CFBundlePackageType</key>\n"444     << "\t\t<string>dSYM</string>\n"445     << "\t\t<key>CFBundleSignature</key>\n"446     << "\t\t<string>\?\?\?\?</string>\n";447 448  if (!BI.OmitShortVersion()) {449    PL << "\t\t<key>CFBundleShortVersionString</key>\n";450    PL << "\t\t<string>";451    printHTMLEscaped(BI.ShortVersionStr, PL);452    PL << "</string>\n";453  }454 455  PL << "\t\t<key>CFBundleVersion</key>\n";456  PL << "\t\t<string>";457  printHTMLEscaped(BI.VersionStr, PL);458  PL << "</string>\n";459 460  if (!Toolchain.empty()) {461    PL << "\t\t<key>Toolchain</key>\n";462    PL << "\t\t<string>";463    printHTMLEscaped(Toolchain, PL);464    PL << "</string>\n";465  }466 467  PL << "\t</dict>\n"468     << "</plist>\n";469 470  PL.close();471  return Error::success();472}473 474static Error createBundleDir(StringRef BundleBase) {475  SmallString<128> Bundle(BundleBase);476  sys::path::append(Bundle, "Contents", "Resources", "DWARF");477  if (std::error_code EC =478          create_directories(Bundle.str(), true, sys::fs::perms::all_all))479    return make_error<StringError>(480        "cannot create bundle: " + toString(errorCodeToError(EC)), EC);481 482  return Error::success();483}484 485static bool verifyOutput(StringRef OutputFile, StringRef Arch,486                         DsymutilOptions Options, std::mutex &Mutex) {487 488  if (OutputFile == "-") {489    if (!Options.LinkOpts.Quiet) {490      std::lock_guard<std::mutex> Guard(Mutex);491      WithColor::warning() << "verification skipped for " << Arch492                           << " because writing to stdout.\n";493    }494    return true;495  }496 497  if (Options.LinkOpts.NoOutput) {498    if (!Options.LinkOpts.Quiet) {499      std::lock_guard<std::mutex> Guard(Mutex);500      WithColor::warning() << "verification skipped for " << Arch501                           << " because --no-output was passed.\n";502    }503    return true;504  }505 506  Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);507  if (!BinOrErr) {508    std::lock_guard<std::mutex> Guard(Mutex);509    WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());510    return false;511  }512 513  Binary &Binary = *BinOrErr.get().getBinary();514  if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {515    std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);516    if (DICtx->getMaxVersion() > 5) {517      if (!Options.LinkOpts.Quiet) {518        std::lock_guard<std::mutex> Guard(Mutex);519        WithColor::warning() << "verification skipped for " << Arch520                             << " because DWARF standard greater than v5 is "521                                "not supported yet.\n";522      }523      return true;524    }525 526    if (Options.LinkOpts.Verbose) {527      std::lock_guard<std::mutex> Guard(Mutex);528      errs() << "Verifying DWARF for architecture: " << Arch << "\n";529    }530 531    std::string Buffer;532    raw_string_ostream OS(Buffer);533 534    DIDumpOptions DumpOpts;535    bool success = DICtx->verify(OS, DumpOpts.noImplicitRecursion());536    if (!success) {537      std::lock_guard<std::mutex> Guard(Mutex);538      errs() << OS.str();539      WithColor::error() << "output verification failed for " << Arch << '\n';540    }541    return success;542  }543 544  return false;545}546 547namespace {548struct OutputLocation {549  OutputLocation(std::string DWARFFile,550                 std::optional<std::string> ResourceDir = {})551      : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}552  /// This method is a workaround for older compilers.553  std::optional<std::string> getResourceDir() const { return ResourceDir; }554  std::string DWARFFile;555  std::optional<std::string> ResourceDir;556};557} // namespace558 559static Expected<OutputLocation>560getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {561  if (Options.OutputFile == "-")562    return OutputLocation(Options.OutputFile);563 564  // When updating, do in place replacement.565  if (Options.OutputFile.empty() && Options.LinkOpts.Update)566    return OutputLocation(std::string(InputFile));567 568  // When dumping the debug map, just return an empty output location. This569  // allows us to compute the output location once.570  if (Options.DumpDebugMap)571    return OutputLocation("");572 573  // If a flat dSYM has been requested, things are pretty simple.574  if (Options.Flat) {575    if (Options.OutputFile.empty()) {576      if (InputFile == "-")577        return OutputLocation{"a.out.dwarf", {}};578      return OutputLocation((InputFile + ".dwarf").str());579    }580 581    return OutputLocation(Options.OutputFile);582  }583 584  // We need to create/update a dSYM bundle.585  // A bundle hierarchy looks like this:586  //   <bundle name>.dSYM/587  //       Contents/588  //          Info.plist589  //          Resources/590  //             DWARF/591  //                <DWARF file(s)>592  std::string DwarfFile =593      std::string(InputFile == "-" ? StringRef("a.out") : InputFile);594  SmallString<128> Path(Options.OutputFile);595  if (Path.empty())596    Path = DwarfFile + ".dSYM";597  if (!Options.LinkOpts.NoOutput) {598    if (auto E = createBundleDir(Path))599      return std::move(E);600    if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))601      return std::move(E);602  }603 604  sys::path::append(Path, "Contents", "Resources");605  std::string ResourceDir = std::string(Path);606  sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));607  return OutputLocation(std::string(Path), ResourceDir);608}609 610int dsymutil_main(int argc, char **argv, const llvm::ToolContext &) {611  // Parse arguments.612  DsymutilOptTable T;613  unsigned MAI;614  unsigned MAC;615  ArrayRef<const char *> ArgsArr = ArrayRef(argv + 1, argc - 1);616  opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);617 618  void *P = (void *)(intptr_t)getOutputFileName;619  std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);620  SDKPath = std::string(sys::path::parent_path(SDKPath));621 622  for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {623    WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()624                         << '\n';625  }626 627  if (Args.hasArg(OPT_help)) {628    T.printHelp(629        outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),630        "manipulate archived DWARF debug symbol files.\n\n"631        "dsymutil links the DWARF debug information found in the object files\n"632        "for the executable <input file> by using debug symbols information\n"633        "contained in its symbol table.\n",634        false);635    return EXIT_SUCCESS;636  }637 638  if (Args.hasArg(OPT_version)) {639    cl::PrintVersionMessage();640    return EXIT_SUCCESS;641  }642 643  auto OptionsOrErr = getOptions(Args);644  if (!OptionsOrErr) {645    WithColor::error() << toString(OptionsOrErr.takeError()) << '\n';646    return EXIT_FAILURE;647  }648 649  auto &Options = *OptionsOrErr;650 651  InitializeAllTargetInfos();652  InitializeAllTargetMCs();653  InitializeAllTargets();654  InitializeAllAsmPrinters();655 656  auto Repro = Reproducer::createReproducer(Options.ReproMode,657                                            Options.ReproducerPath, argc, argv);658  if (!Repro) {659    WithColor::error() << toString(Repro.takeError()) << '\n';660    return EXIT_FAILURE;661  }662 663  Options.LinkOpts.VFS = (*Repro)->getVFS();664 665  for (const auto &Arch : Options.Archs)666    if (Arch != "*" && Arch != "all" &&667        !object::MachOObjectFile::isValidArch(Arch)) {668      WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";669      return EXIT_FAILURE;670    }671 672  for (auto &InputFile : Options.InputFiles) {673    // Shared a single binary holder for all the link steps.674    BinaryHolder::Options BinOpts;675    BinOpts.Verbose = Options.LinkOpts.Verbose;676    BinOpts.Warn = !Options.NoObjectTimestamp;677    BinaryHolder BinHolder(Options.LinkOpts.VFS, BinOpts);678 679    // Dump the symbol table for each input file and requested arch680    if (Options.DumpStab) {681      if (!dumpStab(BinHolder, InputFile, Options.Archs,682                    Options.LinkOpts.DSYMSearchPaths,683                    Options.LinkOpts.PrependPath,684                    Options.LinkOpts.BuildVariantSuffix))685        return EXIT_FAILURE;686      continue;687    }688 689    auto DebugMapPtrsOrErr = parseDebugMap(690        BinHolder, InputFile, Options.Archs, Options.LinkOpts.DSYMSearchPaths,691        Options.LinkOpts.PrependPath, Options.LinkOpts.BuildVariantSuffix,692        Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap);693 694    if (auto EC = DebugMapPtrsOrErr.getError()) {695      WithColor::error() << "cannot parse the debug map for '" << InputFile696                         << "': " << EC.message() << '\n';697      return EXIT_FAILURE;698    }699 700    // Remember the number of debug maps that are being processed to decide how701    // to name the remark files.702    Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size();703 704    if (Options.LinkOpts.Update) {705      // The debug map should be empty. Add one object file corresponding to706      // the input file.707      for (auto &Map : *DebugMapPtrsOrErr)708        Map->addDebugMapObject(InputFile,709                               sys::TimePoint<std::chrono::seconds>());710    }711 712    // Ensure that the debug map is not empty (anymore).713    if (DebugMapPtrsOrErr->empty()) {714      WithColor::error() << "no architecture to link\n";715      return EXIT_FAILURE;716    }717 718    // Compute the output location and update the resource directory.719    Expected<OutputLocation> OutputLocationOrErr =720        getOutputFileName(InputFile, Options);721    if (!OutputLocationOrErr) {722      WithColor::error() << toString(OutputLocationOrErr.takeError()) << "\n";723      return EXIT_FAILURE;724    }725    Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();726 727    // Statistics only require different architectures to be processed728    // sequentially, the link itself can still happen in parallel. Change the729    // thread pool strategy here instead of modifying LinkOpts.Threads.730    ThreadPoolStrategy S = hardware_concurrency(731        Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);732    if (Options.LinkOpts.Threads == 0) {733      // If NumThreads is not specified, create one thread for each input, up to734      // the number of hardware threads.735      S.ThreadsRequested = DebugMapPtrsOrErr->size();736      S.Limit = true;737    }738    DefaultThreadPool Threads(S);739 740    // If there is more than one link to execute, we need to generate741    // temporary files.742    const bool NeedsTempFiles =743        !Options.DumpDebugMap && (Options.OutputFile != "-") &&744        (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);745 746    std::atomic_char AllOK(1);747    SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;748 749    std::mutex ErrorHandlerMutex;750 751    // Set up a crash recovery context.752    CrashRecoveryContext::Enable();753    CrashRecoveryContext CRC;754    CRC.DumpStackAndCleanupOnFailure = true;755 756    const bool Crashed = !CRC.RunSafely([&]() {757      for (auto &Map : *DebugMapPtrsOrErr) {758        if (Options.LinkOpts.Verbose || Options.DumpDebugMap)759          Map->print(outs());760 761        if (Options.DumpDebugMap)762          continue;763 764        if (Map->begin() == Map->end()) {765          if (!Options.LinkOpts.Quiet) {766            std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);767            WithColor::warning()768                << "no debug symbols in executable (-arch "769                << MachOUtils::getArchName(Map->getTriple().getArchName())770                << ")\n";771          }772        }773 774        // Using a std::shared_ptr rather than std::unique_ptr because move-only775        // types don't work with std::bind in the ThreadPool implementation.776        std::shared_ptr<raw_fd_ostream> OS;777 778        std::string OutputFile = OutputLocationOrErr->DWARFFile;779        if (NeedsTempFiles) {780          TempFiles.emplace_back(Map->getTriple().getArchName().str());781 782          auto E = TempFiles.back().createTempFile();783          if (E) {784            std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);785            WithColor::error() << toString(std::move(E));786            AllOK.fetch_and(false);787            return;788          }789 790          MachOUtils::ArchAndFile &AF = TempFiles.back();791          OS = std::make_shared<raw_fd_ostream>(AF.getFD(),792                                                /*shouldClose*/ false);793          OutputFile = AF.getPath();794        } else {795          std::error_code EC;796          OS = std::make_shared<raw_fd_ostream>(797              Options.LinkOpts.NoOutput ? "-" : OutputFile, EC,798              sys::fs::OF_None);799          if (EC) {800            WithColor::error() << OutputFile << ": " << EC.message() << "\n";801            AllOK.fetch_and(false);802            return;803          }804        }805 806        auto LinkLambda = [&,807                           OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {808          DwarfLinkerForBinary Linker(*Stream, BinHolder, Options.LinkOpts,809                                      ErrorHandlerMutex);810          AllOK.fetch_and(Linker.link(*Map));811          Stream->flush();812          if (flagIsSet(Options.Verify, DWARFVerify::Output) ||813              (flagIsSet(Options.Verify, DWARFVerify::OutputOnValidInput) &&814               !Linker.InputVerificationFailed())) {815            AllOK.fetch_and(verifyOutput(OutputFile,816                                         Map->getTriple().getArchName(),817                                         Options, ErrorHandlerMutex));818          }819        };820 821        // FIXME: The DwarfLinker can have some very deep recursion that can max822        // out the (significantly smaller) stack when using threads. We don't823        // want this limitation when we only have a single thread.824        if (S.ThreadsRequested == 1)825          LinkLambda(OS);826        else827          Threads.async(LinkLambda, OS);828      }829 830      Threads.wait();831    });832 833    if (Crashed)834      (*Repro)->generate();835 836    if (!AllOK || Crashed)837      return EXIT_FAILURE;838 839    if (NeedsTempFiles) {840      bool Fat64 = Options.LinkOpts.Fat64;841      if (!Fat64) {842        // Universal Mach-O files can't have an archicture slice that starts843        // beyond the 4GB boundary. "lipo" can create a 64 bit universal844        // header, but older tools may not support these files so we want to845        // emit a warning if the file can't be encoded as a file with a 32 bit846        // universal header. To detect this, we check the size of each847        // architecture's skinny Mach-O file and add up the offsets. If they848        // exceed 4GB, we emit a warning.849 850        // First we compute the right offset where the first architecture will851        // fit followin the 32 bit universal header. The 32 bit universal header852        // starts with a uint32_t magic and a uint32_t number of architecture853        // infos. Then it is followed by 5 uint32_t values for each854        // architecture. So we set the start offset to the right value so we can855        // calculate the exact offset that the first architecture slice can856        // start at.857        constexpr uint64_t MagicAndCountSize = 2 * 4;858        constexpr uint64_t UniversalArchInfoSize = 5 * 4;859        uint64_t FileOffset =860            MagicAndCountSize + UniversalArchInfoSize * TempFiles.size();861        for (const auto &File : TempFiles) {862          ErrorOr<vfs::Status> stat =863              Options.LinkOpts.VFS->status(File.getPath());864          if (!stat)865            break;866          if (FileOffset > UINT32_MAX) {867            Fat64 = true;868            WithColor::warning() << formatv(869                "the universal binary has a slice with a starting offset "870                "({0:x}) that exceeds 4GB. To avoid producing an invalid "871                "Mach-O file, a universal binary with a 64-bit header will be "872                "generated, which may not be supported by older tools. Use the "873                "-fat64 flag to force a 64-bit header and silence this "874                "warning.",875                FileOffset);876            return EXIT_FAILURE;877          }878          FileOffset += stat->getSize();879        }880      }881      if (!MachOUtils::generateUniversalBinary(882              TempFiles, OutputLocationOrErr->DWARFFile, Options.LinkOpts,883              SDKPath, Fat64))884        return EXIT_FAILURE;885    }886  }887 888  return EXIT_SUCCESS;889}890