xref: /llvm-project/llvm/tools/dsymutil/dsymutil.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
1 //===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This program is a utility that aims to be a dropin replacement for Darwin's
10 // dsymutil.
11 //===----------------------------------------------------------------------===//
12 
13 #include "dsymutil.h"
14 #include "BinaryHolder.h"
15 #include "CFBundle.h"
16 #include "DebugMap.h"
17 #include "DwarfLinkerForBinary.h"
18 #include "LinkUtils.h"
19 #include "MachOUtils.h"
20 #include "Reproducer.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/ADT/StringExtras.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/DebugInfo/DIContext.h"
27 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
28 #include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
29 #include "llvm/MC/MCSubtargetInfo.h"
30 #include "llvm/Object/Binary.h"
31 #include "llvm/Object/MachO.h"
32 #include "llvm/Option/Arg.h"
33 #include "llvm/Option/ArgList.h"
34 #include "llvm/Option/Option.h"
35 #include "llvm/Support/CommandLine.h"
36 #include "llvm/Support/CrashRecoveryContext.h"
37 #include "llvm/Support/FileCollector.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/FormatVariadic.h"
40 #include "llvm/Support/LLVMDriver.h"
41 #include "llvm/Support/Path.h"
42 #include "llvm/Support/TargetSelect.h"
43 #include "llvm/Support/ThreadPool.h"
44 #include "llvm/Support/WithColor.h"
45 #include "llvm/Support/raw_ostream.h"
46 #include "llvm/Support/thread.h"
47 #include "llvm/TargetParser/Triple.h"
48 #include <algorithm>
49 #include <cstdint>
50 #include <cstdlib>
51 #include <string>
52 #include <system_error>
53 
54 using namespace llvm;
55 using namespace llvm::dsymutil;
56 using namespace object;
57 using namespace llvm::dwarf_linker;
58 
59 namespace {
60 enum 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 OPTION
65 };
66 
67 #define OPTTABLE_STR_TABLE_CODE
68 #include "Options.inc"
69 #undef OPTTABLE_STR_TABLE_CODE
70 
71 #define OPTTABLE_PREFIXES_TABLE_CODE
72 #include "Options.inc"
73 #undef OPTTABLE_PREFIXES_TABLE_CODE
74 
75 using namespace llvm::opt;
76 static constexpr opt::OptTable::Info InfoTable[] = {
77 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
78 #include "Options.inc"
79 #undef OPTION
80 };
81 
82 class DsymutilOptTable : public opt::GenericOptTable {
83 public:
84   DsymutilOptTable()
85       : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
86 };
87 } // namespace
88 
89 enum 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 = Auto
98 #else
99   Default = None
100 #endif
101 };
102 
103 inline bool flagIsSet(DWARFVerify Flags, DWARFVerify SingleFlag) {
104   return static_cast<uint8_t>(Flags) & static_cast<uint8_t>(SingleFlag);
105 }
106 
107 struct 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 the
126 /// special case where we might have dSYM bundles as input. The function
127 /// returns an error when the directory structure doesn't match that of a dSYM
128 /// bundle.
129 static 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 the
155     // 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.
174 static 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 
206 static Expected<DsymutilAccelTableKind>
207 getAccelTableKind(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 
228 static Expected<DsymutilDWARFLinkerType>
229 getDWARFLinkerType(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 
246 static 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 
266 static 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 performs
290 /// some sanity checking. Returns an error in case the latter fails.
291 static 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   else
374     Options.LinkOpts.Threads = 0; // Use all available hardware threads
375 
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     else
388       return FormatOrErr.takeError();
389   }
390 
391   Options.LinkOpts.RemarksKeepAll =
392       !Args.hasArg(OPT_remarks_drop_without_debug);
393 
394   if (opt::Arg *BuildVariantSuffix = Args.getLastArg(OPT_build_variant_suffix))
395     Options.LinkOpts.BuildVariantSuffix = BuildVariantSuffix->getValue();
396 
397   for (auto *SearchPath : Args.filtered(OPT_dsym_search_path))
398     Options.LinkOpts.DSYMSearchPaths.push_back(SearchPath->getValue());
399 
400   if (Error E = verifyOptions(Options))
401     return std::move(E);
402   return Options;
403 }
404 
405 static Error createPlistFile(StringRef Bin, StringRef BundleRoot,
406                              StringRef Toolchain) {
407   // Create plist file to write to.
408   SmallString<128> InfoPlist(BundleRoot);
409   sys::path::append(InfoPlist, "Contents/Info.plist");
410   std::error_code EC;
411   raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF);
412   if (EC)
413     return make_error<StringError>(
414         "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
415 
416   CFBundleInfo BI = getBundleInfo(Bin);
417 
418   if (BI.IDStr.empty()) {
419     StringRef BundleID = *sys::path::rbegin(BundleRoot);
420     if (sys::path::extension(BundleRoot) == ".dSYM")
421       BI.IDStr = std::string(sys::path::stem(BundleID));
422     else
423       BI.IDStr = std::string(BundleID);
424   }
425 
426   // Print out information to the plist file.
427   PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
428      << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
429      << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
430      << "<plist version=\"1.0\">\n"
431      << "\t<dict>\n"
432      << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
433      << "\t\t<string>English</string>\n"
434      << "\t\t<key>CFBundleIdentifier</key>\n"
435      << "\t\t<string>com.apple.xcode.dsym.";
436   printHTMLEscaped(BI.IDStr, PL);
437   PL << "</string>\n"
438      << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
439      << "\t\t<string>6.0</string>\n"
440      << "\t\t<key>CFBundlePackageType</key>\n"
441      << "\t\t<string>dSYM</string>\n"
442      << "\t\t<key>CFBundleSignature</key>\n"
443      << "\t\t<string>\?\?\?\?</string>\n";
444 
445   if (!BI.OmitShortVersion()) {
446     PL << "\t\t<key>CFBundleShortVersionString</key>\n";
447     PL << "\t\t<string>";
448     printHTMLEscaped(BI.ShortVersionStr, PL);
449     PL << "</string>\n";
450   }
451 
452   PL << "\t\t<key>CFBundleVersion</key>\n";
453   PL << "\t\t<string>";
454   printHTMLEscaped(BI.VersionStr, PL);
455   PL << "</string>\n";
456 
457   if (!Toolchain.empty()) {
458     PL << "\t\t<key>Toolchain</key>\n";
459     PL << "\t\t<string>";
460     printHTMLEscaped(Toolchain, PL);
461     PL << "</string>\n";
462   }
463 
464   PL << "\t</dict>\n"
465      << "</plist>\n";
466 
467   PL.close();
468   return Error::success();
469 }
470 
471 static Error createBundleDir(StringRef BundleBase) {
472   SmallString<128> Bundle(BundleBase);
473   sys::path::append(Bundle, "Contents", "Resources", "DWARF");
474   if (std::error_code EC =
475           create_directories(Bundle.str(), true, sys::fs::perms::all_all))
476     return make_error<StringError>(
477         "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
478 
479   return Error::success();
480 }
481 
482 static bool verifyOutput(StringRef OutputFile, StringRef Arch,
483                          DsymutilOptions Options, std::mutex &Mutex) {
484 
485   if (OutputFile == "-") {
486     if (!Options.LinkOpts.Quiet) {
487       std::lock_guard<std::mutex> Guard(Mutex);
488       WithColor::warning() << "verification skipped for " << Arch
489                            << " because writing to stdout.\n";
490     }
491     return true;
492   }
493 
494   if (Options.LinkOpts.NoOutput) {
495     if (!Options.LinkOpts.Quiet) {
496       std::lock_guard<std::mutex> Guard(Mutex);
497       WithColor::warning() << "verification skipped for " << Arch
498                            << " because --no-output was passed.\n";
499     }
500     return true;
501   }
502 
503   Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
504   if (!BinOrErr) {
505     std::lock_guard<std::mutex> Guard(Mutex);
506     WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
507     return false;
508   }
509 
510   Binary &Binary = *BinOrErr.get().getBinary();
511   if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
512     std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
513     if (DICtx->getMaxVersion() > 5) {
514       if (!Options.LinkOpts.Quiet) {
515         std::lock_guard<std::mutex> Guard(Mutex);
516         WithColor::warning() << "verification skipped for " << Arch
517                              << " because DWARF standard greater than v5 is "
518                                 "not supported yet.\n";
519       }
520       return true;
521     }
522 
523     if (Options.LinkOpts.Verbose) {
524       std::lock_guard<std::mutex> Guard(Mutex);
525       errs() << "Verifying DWARF for architecture: " << Arch << "\n";
526     }
527 
528     std::string Buffer;
529     raw_string_ostream OS(Buffer);
530 
531     DIDumpOptions DumpOpts;
532     bool success = DICtx->verify(OS, DumpOpts.noImplicitRecursion());
533     if (!success) {
534       std::lock_guard<std::mutex> Guard(Mutex);
535       errs() << OS.str();
536       WithColor::error() << "output verification failed for " << Arch << '\n';
537     }
538     return success;
539   }
540 
541   return false;
542 }
543 
544 namespace {
545 struct OutputLocation {
546   OutputLocation(std::string DWARFFile,
547                  std::optional<std::string> ResourceDir = {})
548       : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
549   /// This method is a workaround for older compilers.
550   std::optional<std::string> getResourceDir() const { return ResourceDir; }
551   std::string DWARFFile;
552   std::optional<std::string> ResourceDir;
553 };
554 } // namespace
555 
556 static Expected<OutputLocation>
557 getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {
558   if (Options.OutputFile == "-")
559     return OutputLocation(Options.OutputFile);
560 
561   // When updating, do in place replacement.
562   if (Options.OutputFile.empty() && Options.LinkOpts.Update)
563     return OutputLocation(std::string(InputFile));
564 
565   // When dumping the debug map, just return an empty output location. This
566   // allows us to compute the output location once.
567   if (Options.DumpDebugMap)
568     return OutputLocation("");
569 
570   // If a flat dSYM has been requested, things are pretty simple.
571   if (Options.Flat) {
572     if (Options.OutputFile.empty()) {
573       if (InputFile == "-")
574         return OutputLocation{"a.out.dwarf", {}};
575       return OutputLocation((InputFile + ".dwarf").str());
576     }
577 
578     return OutputLocation(Options.OutputFile);
579   }
580 
581   // We need to create/update a dSYM bundle.
582   // A bundle hierarchy looks like this:
583   //   <bundle name>.dSYM/
584   //       Contents/
585   //          Info.plist
586   //          Resources/
587   //             DWARF/
588   //                <DWARF file(s)>
589   std::string DwarfFile =
590       std::string(InputFile == "-" ? StringRef("a.out") : InputFile);
591   SmallString<128> Path(Options.OutputFile);
592   if (Path.empty())
593     Path = DwarfFile + ".dSYM";
594   if (!Options.LinkOpts.NoOutput) {
595     if (auto E = createBundleDir(Path))
596       return std::move(E);
597     if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
598       return std::move(E);
599   }
600 
601   sys::path::append(Path, "Contents", "Resources");
602   std::string ResourceDir = std::string(Path);
603   sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));
604   return OutputLocation(std::string(Path), ResourceDir);
605 }
606 
607 int dsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
608   // Parse arguments.
609   DsymutilOptTable T;
610   unsigned MAI;
611   unsigned MAC;
612   ArrayRef<const char *> ArgsArr = ArrayRef(argv + 1, argc - 1);
613   opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
614 
615   void *P = (void *)(intptr_t)getOutputFileName;
616   std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);
617   SDKPath = std::string(sys::path::parent_path(SDKPath));
618 
619   for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
620     WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()
621                          << '\n';
622   }
623 
624   if (Args.hasArg(OPT_help)) {
625     T.printHelp(
626         outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),
627         "manipulate archived DWARF debug symbol files.\n\n"
628         "dsymutil links the DWARF debug information found in the object files\n"
629         "for the executable <input file> by using debug symbols information\n"
630         "contained in its symbol table.\n",
631         false);
632     return EXIT_SUCCESS;
633   }
634 
635   if (Args.hasArg(OPT_version)) {
636     cl::PrintVersionMessage();
637     return EXIT_SUCCESS;
638   }
639 
640   auto OptionsOrErr = getOptions(Args);
641   if (!OptionsOrErr) {
642     WithColor::error() << toString(OptionsOrErr.takeError()) << '\n';
643     return EXIT_FAILURE;
644   }
645 
646   auto &Options = *OptionsOrErr;
647 
648   InitializeAllTargetInfos();
649   InitializeAllTargetMCs();
650   InitializeAllTargets();
651   InitializeAllAsmPrinters();
652 
653   auto Repro = Reproducer::createReproducer(Options.ReproMode,
654                                             Options.ReproducerPath, argc, argv);
655   if (!Repro) {
656     WithColor::error() << toString(Repro.takeError()) << '\n';
657     return EXIT_FAILURE;
658   }
659 
660   Options.LinkOpts.VFS = (*Repro)->getVFS();
661 
662   for (const auto &Arch : Options.Archs)
663     if (Arch != "*" && Arch != "all" &&
664         !object::MachOObjectFile::isValidArch(Arch)) {
665       WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
666       return EXIT_FAILURE;
667     }
668 
669   for (auto &InputFile : Options.InputFiles) {
670     // Shared a single binary holder for all the link steps.
671     BinaryHolder::Options BinOpts;
672     BinOpts.Verbose = Options.LinkOpts.Verbose;
673     BinOpts.Warn = !Options.NoObjectTimestamp;
674     BinaryHolder BinHolder(Options.LinkOpts.VFS, BinOpts);
675 
676     // Dump the symbol table for each input file and requested arch
677     if (Options.DumpStab) {
678       if (!dumpStab(BinHolder, InputFile, Options.Archs,
679                     Options.LinkOpts.DSYMSearchPaths,
680                     Options.LinkOpts.PrependPath,
681                     Options.LinkOpts.BuildVariantSuffix))
682         return EXIT_FAILURE;
683       continue;
684     }
685 
686     auto DebugMapPtrsOrErr = parseDebugMap(
687         BinHolder, InputFile, Options.Archs, Options.LinkOpts.DSYMSearchPaths,
688         Options.LinkOpts.PrependPath, Options.LinkOpts.BuildVariantSuffix,
689         Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap);
690 
691     if (auto EC = DebugMapPtrsOrErr.getError()) {
692       WithColor::error() << "cannot parse the debug map for '" << InputFile
693                          << "': " << EC.message() << '\n';
694       return EXIT_FAILURE;
695     }
696 
697     // Remember the number of debug maps that are being processed to decide how
698     // to name the remark files.
699     Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size();
700 
701     if (Options.LinkOpts.Update) {
702       // The debug map should be empty. Add one object file corresponding to
703       // the input file.
704       for (auto &Map : *DebugMapPtrsOrErr)
705         Map->addDebugMapObject(InputFile,
706                                sys::TimePoint<std::chrono::seconds>());
707     }
708 
709     // Ensure that the debug map is not empty (anymore).
710     if (DebugMapPtrsOrErr->empty()) {
711       WithColor::error() << "no architecture to link\n";
712       return EXIT_FAILURE;
713     }
714 
715     // Compute the output location and update the resource directory.
716     Expected<OutputLocation> OutputLocationOrErr =
717         getOutputFileName(InputFile, Options);
718     if (!OutputLocationOrErr) {
719       WithColor::error() << toString(OutputLocationOrErr.takeError()) << "\n";
720       return EXIT_FAILURE;
721     }
722     Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();
723 
724     // Statistics only require different architectures to be processed
725     // sequentially, the link itself can still happen in parallel. Change the
726     // thread pool strategy here instead of modifying LinkOpts.Threads.
727     ThreadPoolStrategy S = hardware_concurrency(
728         Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);
729     if (Options.LinkOpts.Threads == 0) {
730       // If NumThreads is not specified, create one thread for each input, up to
731       // the number of hardware threads.
732       S.ThreadsRequested = DebugMapPtrsOrErr->size();
733       S.Limit = true;
734     }
735     DefaultThreadPool Threads(S);
736 
737     // If there is more than one link to execute, we need to generate
738     // temporary files.
739     const bool NeedsTempFiles =
740         !Options.DumpDebugMap && (Options.OutputFile != "-") &&
741         (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);
742 
743     std::atomic_char AllOK(1);
744     SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
745 
746     std::mutex ErrorHandlerMutex;
747 
748     // Set up a crash recovery context.
749     CrashRecoveryContext::Enable();
750     CrashRecoveryContext CRC;
751     CRC.DumpStackAndCleanupOnFailure = true;
752 
753     const bool Crashed = !CRC.RunSafely([&]() {
754       for (auto &Map : *DebugMapPtrsOrErr) {
755         if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
756           Map->print(outs());
757 
758         if (Options.DumpDebugMap)
759           continue;
760 
761         if (Map->begin() == Map->end()) {
762           if (!Options.LinkOpts.Quiet) {
763             std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
764             WithColor::warning()
765                 << "no debug symbols in executable (-arch "
766                 << MachOUtils::getArchName(Map->getTriple().getArchName())
767                 << ")\n";
768           }
769         }
770 
771         // Using a std::shared_ptr rather than std::unique_ptr because move-only
772         // types don't work with std::bind in the ThreadPool implementation.
773         std::shared_ptr<raw_fd_ostream> OS;
774 
775         std::string OutputFile = OutputLocationOrErr->DWARFFile;
776         if (NeedsTempFiles) {
777           TempFiles.emplace_back(Map->getTriple().getArchName().str());
778 
779           auto E = TempFiles.back().createTempFile();
780           if (E) {
781             std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
782             WithColor::error() << toString(std::move(E));
783             AllOK.fetch_and(false);
784             return;
785           }
786 
787           MachOUtils::ArchAndFile &AF = TempFiles.back();
788           OS = std::make_shared<raw_fd_ostream>(AF.getFD(),
789                                                 /*shouldClose*/ false);
790           OutputFile = AF.getPath();
791         } else {
792           std::error_code EC;
793           OS = std::make_shared<raw_fd_ostream>(
794               Options.LinkOpts.NoOutput ? "-" : OutputFile, EC,
795               sys::fs::OF_None);
796           if (EC) {
797             WithColor::error() << OutputFile << ": " << EC.message() << "\n";
798             AllOK.fetch_and(false);
799             return;
800           }
801         }
802 
803         auto LinkLambda = [&,
804                            OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
805           DwarfLinkerForBinary Linker(*Stream, BinHolder, Options.LinkOpts,
806                                       ErrorHandlerMutex);
807           AllOK.fetch_and(Linker.link(*Map));
808           Stream->flush();
809           if (flagIsSet(Options.Verify, DWARFVerify::Output) ||
810               (flagIsSet(Options.Verify, DWARFVerify::OutputOnValidInput) &&
811                !Linker.InputVerificationFailed())) {
812             AllOK.fetch_and(verifyOutput(OutputFile,
813                                          Map->getTriple().getArchName(),
814                                          Options, ErrorHandlerMutex));
815           }
816         };
817 
818         // FIXME: The DwarfLinker can have some very deep recursion that can max
819         // out the (significantly smaller) stack when using threads. We don't
820         // want this limitation when we only have a single thread.
821         if (S.ThreadsRequested == 1)
822           LinkLambda(OS);
823         else
824           Threads.async(LinkLambda, OS);
825       }
826 
827       Threads.wait();
828     });
829 
830     if (Crashed)
831       (*Repro)->generate();
832 
833     if (!AllOK || Crashed)
834       return EXIT_FAILURE;
835 
836     if (NeedsTempFiles) {
837       bool Fat64 = Options.LinkOpts.Fat64;
838       if (!Fat64) {
839         // Universal Mach-O files can't have an archicture slice that starts
840         // beyond the 4GB boundary. "lipo" can create a 64 bit universal
841         // header, but older tools may not support these files so we want to
842         // emit a warning if the file can't be encoded as a file with a 32 bit
843         // universal header. To detect this, we check the size of each
844         // architecture's skinny Mach-O file and add up the offsets. If they
845         // exceed 4GB, we emit a warning.
846 
847         // First we compute the right offset where the first architecture will
848         // fit followin the 32 bit universal header. The 32 bit universal header
849         // starts with a uint32_t magic and a uint32_t number of architecture
850         // infos. Then it is followed by 5 uint32_t values for each
851         // architecture. So we set the start offset to the right value so we can
852         // calculate the exact offset that the first architecture slice can
853         // start at.
854         constexpr uint64_t MagicAndCountSize = 2 * 4;
855         constexpr uint64_t UniversalArchInfoSize = 5 * 4;
856         uint64_t FileOffset =
857             MagicAndCountSize + UniversalArchInfoSize * TempFiles.size();
858         for (const auto &File : TempFiles) {
859           ErrorOr<vfs::Status> stat =
860               Options.LinkOpts.VFS->status(File.getPath());
861           if (!stat)
862             break;
863           if (FileOffset > UINT32_MAX) {
864             Fat64 = true;
865             WithColor::warning() << formatv(
866                 "the universal binary has a slice with a starting offset "
867                 "({0:x}) that exceeds 4GB. To avoid producing an invalid "
868                 "Mach-O file, a universal binary with a 64-bit header will be "
869                 "generated, which may not be supported by older tools. Use the "
870                 "-fat64 flag to force a 64-bit header and silence this "
871                 "warning.",
872                 FileOffset);
873             return EXIT_FAILURE;
874           }
875           FileOffset += stat->getSize();
876         }
877       }
878       if (!MachOUtils::generateUniversalBinary(
879               TempFiles, OutputLocationOrErr->DWARFFile, Options.LinkOpts,
880               SDKPath, Fat64))
881         return EXIT_FAILURE;
882     }
883   }
884 
885   return EXIT_SUCCESS;
886 }
887