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/StringSwitch.h"
26 #include "llvm/ADT/Triple.h"
27 #include "llvm/DebugInfo/DIContext.h"
28 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
29 #include "llvm/DebugInfo/DWARF/DWARFVerifier.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:
DsymutilOptTable()84 DsymutilOptTable() : OptTable(InfoTable) {}
85 };
86 } // namespace
87
88 struct DsymutilOptions {
89 bool DumpDebugMap = false;
90 bool DumpStab = false;
91 bool Flat = false;
92 bool InputIsYAMLDebugMap = false;
93 bool PaperTrailWarnings = false;
94 bool Verify = 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.
getInputs(opt::InputArgList & Args,bool DsymAsInput)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.
verifyOptions(const DsymutilOptions & Options)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
getAccelTableKind(opt::InputArgList & Args)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 == "Default")
205 return AccelTableKind::Default;
206 return make_error<StringError>(
207 "invalid accelerator type specified: '" + S +
208 "'. Support values are 'Apple', 'Dwarf' and 'Default'.",
209 inconvertibleErrorCode());
210 }
211 return AccelTableKind::Default;
212 }
213
214 /// Parses the command line options into the LinkOptions struct and performs
215 /// some sanity checking. Returns an error in case the latter fails.
getOptions(opt::InputArgList & Args)216 static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
217 DsymutilOptions Options;
218
219 Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);
220 Options.DumpStab = Args.hasArg(OPT_symtab);
221 Options.Flat = Args.hasArg(OPT_flat);
222 Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);
223 Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail);
224 Options.Verify = Args.hasArg(OPT_verify);
225
226 Options.LinkOpts.Minimize = Args.hasArg(OPT_minimize);
227 Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr);
228 Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output);
229 Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
230 Options.LinkOpts.Update = Args.hasArg(OPT_update);
231 Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
232 Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);
233
234 if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) {
235 Options.ReproMode = ReproducerMode::Use;
236 Options.ReproducerPath = ReproducerPath->getValue();
237 }
238
239 if (Args.hasArg(OPT_gen_reproducer))
240 Options.ReproMode = ReproducerMode::Generate;
241
242 if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) {
243 Options.LinkOpts.TheAccelTableKind = *AccelKind;
244 } else {
245 return AccelKind.takeError();
246 }
247
248 if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap))
249 Options.SymbolMap = SymbolMap->getValue();
250
251 if (Args.hasArg(OPT_symbolmap))
252 Options.LinkOpts.Update = true;
253
254 if (Expected<std::vector<std::string>> InputFiles =
255 getInputs(Args, Options.LinkOpts.Update)) {
256 Options.InputFiles = std::move(*InputFiles);
257 } else {
258 return InputFiles.takeError();
259 }
260
261 for (auto *Arch : Args.filtered(OPT_arch))
262 Options.Archs.push_back(Arch->getValue());
263
264 if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))
265 Options.LinkOpts.PrependPath = OsoPrependPath->getValue();
266
267 for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) {
268 auto Split = StringRef(Arg).split('=');
269 Options.LinkOpts.ObjectPrefixMap.insert(
270 {std::string(Split.first), std::string(Split.second)});
271 }
272
273 if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))
274 Options.OutputFile = OutputFile->getValue();
275
276 if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))
277 Options.Toolchain = Toolchain->getValue();
278
279 if (Args.hasArg(OPT_assembly))
280 Options.LinkOpts.FileType = OutputFileType::Assembly;
281
282 if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
283 Options.LinkOpts.Threads = atoi(NumThreads->getValue());
284 else
285 Options.LinkOpts.Threads = 0; // Use all available hardware threads
286
287 if (Options.DumpDebugMap || Options.LinkOpts.Verbose)
288 Options.LinkOpts.Threads = 1;
289
290 if (getenv("RC_DEBUG_OPTIONS"))
291 Options.PaperTrailWarnings = true;
292
293 if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path))
294 Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue();
295
296 if (opt::Arg *RemarksOutputFormat =
297 Args.getLastArg(OPT_remarks_output_format)) {
298 if (Expected<remarks::Format> FormatOrErr =
299 remarks::parseFormat(RemarksOutputFormat->getValue()))
300 Options.LinkOpts.RemarksFormat = *FormatOrErr;
301 else
302 return FormatOrErr.takeError();
303 }
304
305 if (Error E = verifyOptions(Options))
306 return std::move(E);
307 return Options;
308 }
309
createPlistFile(StringRef Bin,StringRef BundleRoot,StringRef Toolchain)310 static Error createPlistFile(StringRef Bin, StringRef BundleRoot,
311 StringRef Toolchain) {
312 // Create plist file to write to.
313 SmallString<128> InfoPlist(BundleRoot);
314 sys::path::append(InfoPlist, "Contents/Info.plist");
315 std::error_code EC;
316 raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_Text);
317 if (EC)
318 return make_error<StringError>(
319 "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
320
321 CFBundleInfo BI = getBundleInfo(Bin);
322
323 if (BI.IDStr.empty()) {
324 StringRef BundleID = *sys::path::rbegin(BundleRoot);
325 if (sys::path::extension(BundleRoot) == ".dSYM")
326 BI.IDStr = std::string(sys::path::stem(BundleID));
327 else
328 BI.IDStr = std::string(BundleID);
329 }
330
331 // Print out information to the plist file.
332 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
333 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
334 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
335 << "<plist version=\"1.0\">\n"
336 << "\t<dict>\n"
337 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
338 << "\t\t<string>English</string>\n"
339 << "\t\t<key>CFBundleIdentifier</key>\n"
340 << "\t\t<string>com.apple.xcode.dsym.";
341 printHTMLEscaped(BI.IDStr, PL);
342 PL << "</string>\n"
343 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
344 << "\t\t<string>6.0</string>\n"
345 << "\t\t<key>CFBundlePackageType</key>\n"
346 << "\t\t<string>dSYM</string>\n"
347 << "\t\t<key>CFBundleSignature</key>\n"
348 << "\t\t<string>\?\?\?\?</string>\n";
349
350 if (!BI.OmitShortVersion()) {
351 PL << "\t\t<key>CFBundleShortVersionString</key>\n";
352 PL << "\t\t<string>";
353 printHTMLEscaped(BI.ShortVersionStr, PL);
354 PL << "</string>\n";
355 }
356
357 PL << "\t\t<key>CFBundleVersion</key>\n";
358 PL << "\t\t<string>";
359 printHTMLEscaped(BI.VersionStr, PL);
360 PL << "</string>\n";
361
362 if (!Toolchain.empty()) {
363 PL << "\t\t<key>Toolchain</key>\n";
364 PL << "\t\t<string>";
365 printHTMLEscaped(Toolchain, PL);
366 PL << "</string>\n";
367 }
368
369 PL << "\t</dict>\n"
370 << "</plist>\n";
371
372 PL.close();
373 return Error::success();
374 }
375
createBundleDir(StringRef BundleBase)376 static Error createBundleDir(StringRef BundleBase) {
377 SmallString<128> Bundle(BundleBase);
378 sys::path::append(Bundle, "Contents", "Resources", "DWARF");
379 if (std::error_code EC =
380 create_directories(Bundle.str(), true, sys::fs::perms::all_all))
381 return make_error<StringError>(
382 "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
383
384 return Error::success();
385 }
386
verify(StringRef OutputFile,StringRef Arch,bool Verbose)387 static bool verify(StringRef OutputFile, StringRef Arch, bool Verbose) {
388 if (OutputFile == "-") {
389 WithColor::warning() << "verification skipped for " << Arch
390 << "because writing to stdout.\n";
391 return true;
392 }
393
394 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
395 if (!BinOrErr) {
396 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
397 return false;
398 }
399
400 Binary &Binary = *BinOrErr.get().getBinary();
401 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
402 raw_ostream &os = Verbose ? errs() : nulls();
403 os << "Verifying DWARF for architecture: " << Arch << "\n";
404 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
405 DIDumpOptions DumpOpts;
406 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
407 if (!success)
408 WithColor::error() << "verification failed for " << Arch << '\n';
409 return success;
410 }
411
412 return false;
413 }
414
415 namespace {
416 struct OutputLocation {
OutputLocation__anon86db60ff0211::OutputLocation417 OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {})
418 : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
419 /// This method is a workaround for older compilers.
getResourceDir__anon86db60ff0211::OutputLocation420 Optional<std::string> getResourceDir() const { return ResourceDir; }
421 std::string DWARFFile;
422 Optional<std::string> ResourceDir;
423 };
424 } // namespace
425
426 static Expected<OutputLocation>
getOutputFileName(StringRef InputFile,const DsymutilOptions & Options)427 getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {
428 if (Options.OutputFile == "-")
429 return OutputLocation(Options.OutputFile);
430
431 // When updating, do in place replacement.
432 if (Options.OutputFile.empty() &&
433 (Options.LinkOpts.Update || !Options.SymbolMap.empty()))
434 return OutputLocation(std::string(InputFile));
435
436 // If a flat dSYM has been requested, things are pretty simple.
437 if (Options.Flat) {
438 if (Options.OutputFile.empty()) {
439 if (InputFile == "-")
440 return OutputLocation{"a.out.dwarf", {}};
441 return OutputLocation((InputFile + ".dwarf").str());
442 }
443
444 return OutputLocation(Options.OutputFile);
445 }
446
447 // We need to create/update a dSYM bundle.
448 // A bundle hierarchy looks like this:
449 // <bundle name>.dSYM/
450 // Contents/
451 // Info.plist
452 // Resources/
453 // DWARF/
454 // <DWARF file(s)>
455 std::string DwarfFile =
456 std::string(InputFile == "-" ? StringRef("a.out") : InputFile);
457 SmallString<128> Path(Options.OutputFile);
458 if (Path.empty())
459 Path = DwarfFile + ".dSYM";
460 if (!Options.LinkOpts.NoOutput) {
461 if (auto E = createBundleDir(Path))
462 return std::move(E);
463 if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
464 return std::move(E);
465 }
466
467 sys::path::append(Path, "Contents", "Resources");
468 std::string ResourceDir = std::string(Path.str());
469 sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));
470 return OutputLocation(std::string(Path.str()), ResourceDir);
471 }
472
main(int argc,char ** argv)473 int main(int argc, char **argv) {
474 InitLLVM X(argc, argv);
475
476 // Parse arguments.
477 DsymutilOptTable T;
478 unsigned MAI;
479 unsigned MAC;
480 ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1);
481 opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
482
483 void *P = (void *)(intptr_t)getOutputFileName;
484 std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);
485 SDKPath = std::string(sys::path::parent_path(SDKPath));
486
487 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
488 WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()
489 << '\n';
490 }
491
492 if (Args.hasArg(OPT_help)) {
493 T.PrintHelp(
494 outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),
495 "manipulate archived DWARF debug symbol files.\n\n"
496 "dsymutil links the DWARF debug information found in the object files\n"
497 "for the executable <input file> by using debug symbols information\n"
498 "contained in its symbol table.\n",
499 false);
500 return 0;
501 }
502
503 if (Args.hasArg(OPT_version)) {
504 cl::PrintVersionMessage();
505 return 0;
506 }
507
508 auto OptionsOrErr = getOptions(Args);
509 if (!OptionsOrErr) {
510 WithColor::error() << toString(OptionsOrErr.takeError());
511 return 1;
512 }
513
514 auto &Options = *OptionsOrErr;
515
516 InitializeAllTargetInfos();
517 InitializeAllTargetMCs();
518 InitializeAllTargets();
519 InitializeAllAsmPrinters();
520
521 auto Repro =
522 Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath);
523 if (!Repro) {
524 WithColor::error() << toString(Repro.takeError());
525 return 1;
526 }
527
528 Options.LinkOpts.VFS = (*Repro)->getVFS();
529
530 for (const auto &Arch : Options.Archs)
531 if (Arch != "*" && Arch != "all" &&
532 !object::MachOObjectFile::isValidArch(Arch)) {
533 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
534 return 1;
535 }
536
537 SymbolMapLoader SymMapLoader(Options.SymbolMap);
538
539 for (auto &InputFile : Options.InputFiles) {
540 // Dump the symbol table for each input file and requested arch
541 if (Options.DumpStab) {
542 if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs,
543 Options.LinkOpts.PrependPath))
544 return 1;
545 continue;
546 }
547
548 auto DebugMapPtrsOrErr =
549 parseDebugMap(Options.LinkOpts.VFS, InputFile, Options.Archs,
550 Options.LinkOpts.PrependPath, Options.PaperTrailWarnings,
551 Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap);
552
553 if (auto EC = DebugMapPtrsOrErr.getError()) {
554 WithColor::error() << "cannot parse the debug map for '" << InputFile
555 << "': " << EC.message() << '\n';
556 return 1;
557 }
558
559 // Remember the number of debug maps that are being processed to decide how
560 // to name the remark files.
561 Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size();
562
563 if (Options.LinkOpts.Update) {
564 // The debug map should be empty. Add one object file corresponding to
565 // the input file.
566 for (auto &Map : *DebugMapPtrsOrErr)
567 Map->addDebugMapObject(InputFile,
568 sys::TimePoint<std::chrono::seconds>());
569 }
570
571 // Ensure that the debug map is not empty (anymore).
572 if (DebugMapPtrsOrErr->empty()) {
573 WithColor::error() << "no architecture to link\n";
574 return 1;
575 }
576
577 // Shared a single binary holder for all the link steps.
578 BinaryHolder BinHolder(Options.LinkOpts.VFS);
579
580 // Statistics only require different architectures to be processed
581 // sequentially, the link itself can still happen in parallel. Change the
582 // thread pool strategy here instead of modifying LinkOpts.Threads.
583 ThreadPoolStrategy S = hardware_concurrency(
584 Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);
585 if (Options.LinkOpts.Threads == 0) {
586 // If NumThreads is not specified, create one thread for each input, up to
587 // the number of hardware threads.
588 S.ThreadsRequested = DebugMapPtrsOrErr->size();
589 S.Limit = true;
590 }
591 ThreadPool Threads(S);
592
593 // If there is more than one link to execute, we need to generate
594 // temporary files.
595 const bool NeedsTempFiles =
596 !Options.DumpDebugMap && (Options.OutputFile != "-") &&
597 (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);
598 const bool Verify = Options.Verify && !Options.LinkOpts.NoOutput;
599
600 SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
601 std::atomic_char AllOK(1);
602 for (auto &Map : *DebugMapPtrsOrErr) {
603 if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
604 Map->print(outs());
605
606 if (Options.DumpDebugMap)
607 continue;
608
609 if (!Options.SymbolMap.empty())
610 Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map);
611
612 if (Map->begin() == Map->end())
613 WithColor::warning()
614 << "no debug symbols in executable (-arch "
615 << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
616
617 // Using a std::shared_ptr rather than std::unique_ptr because move-only
618 // types don't work with std::bind in the ThreadPool implementation.
619 std::shared_ptr<raw_fd_ostream> OS;
620
621 Expected<OutputLocation> OutputLocationOrErr =
622 getOutputFileName(InputFile, Options);
623 if (!OutputLocationOrErr) {
624 WithColor::error() << toString(OutputLocationOrErr.takeError());
625 return 1;
626 }
627 Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();
628
629 std::string OutputFile = OutputLocationOrErr->DWARFFile;
630 if (NeedsTempFiles) {
631 TempFiles.emplace_back(Map->getTriple().getArchName().str());
632
633 auto E = TempFiles.back().createTempFile();
634 if (E) {
635 WithColor::error() << toString(std::move(E));
636 return 1;
637 }
638
639 auto &TempFile = *(TempFiles.back().File);
640 OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
641 /*shouldClose*/ false);
642 OutputFile = TempFile.TmpName;
643 } else {
644 std::error_code EC;
645 OS = std::make_shared<raw_fd_ostream>(
646 Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None);
647 if (EC) {
648 WithColor::error() << OutputFile << ": " << EC.message();
649 return 1;
650 }
651 }
652
653 auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream,
654 LinkOptions Options) {
655 AllOK.fetch_and(
656 linkDwarf(*Stream, BinHolder, *Map, std::move(Options)));
657 Stream->flush();
658 if (Verify)
659 AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(),
660 Options.Verbose));
661 };
662
663 // FIXME: The DwarfLinker can have some very deep recursion that can max
664 // out the (significantly smaller) stack when using threads. We don't
665 // want this limitation when we only have a single thread.
666 if (S.ThreadsRequested == 1)
667 LinkLambda(OS, Options.LinkOpts);
668 else
669 Threads.async(LinkLambda, OS, Options.LinkOpts);
670 }
671
672 Threads.wait();
673
674 if (!AllOK)
675 return 1;
676
677 if (NeedsTempFiles) {
678 Expected<OutputLocation> OutputLocationOrErr =
679 getOutputFileName(InputFile, Options);
680 if (!OutputLocationOrErr) {
681 WithColor::error() << toString(OutputLocationOrErr.takeError());
682 return 1;
683 }
684 if (!MachOUtils::generateUniversalBinary(TempFiles,
685 OutputLocationOrErr->DWARFFile,
686 Options.LinkOpts, SDKPath))
687 return 1;
688 }
689 }
690
691 return 0;
692 }
693