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