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