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