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