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