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