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