[docs] Add LICENSE.txt to the root of the mono-repo
[llvm-project.git] / llvm / tools / dsymutil / dsymutil.cpp
blob0ebdcc1f2506f24d574a4707643c5f3b981bf977
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 //===----------------------------------------------------------------------===//
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/CrashRecoveryContext.h"
37 #include "llvm/Support/FileCollector.h"
38 #include "llvm/Support/FileSystem.h"
39 #include "llvm/Support/InitLLVM.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>
52 using namespace llvm;
53 using namespace llvm::dsymutil;
54 using namespace object;
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
66 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
67 #include "Options.inc"
68 #undef PREFIX
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
82 class DsymutilOptTable : public opt::OptTable {
83 public:
84 DsymutilOptTable() : OptTable(InfoTable) {}
86 } // namespace
88 enum class DWARFVerify : uint8_t {
89 None = 0,
90 Input = 1 << 0,
91 Output = 1 << 1,
92 All = Input | Output,
95 inline bool flagIsSet(DWARFVerify Flags, DWARFVerify SingleFlag) {
96 return static_cast<uint8_t>(Flags) & static_cast<uint8_t>(SingleFlag);
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::GenerateOnCrash;
115 dsymutil::LinkOptions LinkOpts;
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());
128 if (!DsymAsInput)
129 return InputFiles;
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;
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());
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);
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);
163 return Inputs;
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);
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);
184 if (!Options.Flat && Options.OutputFile == "-")
185 return make_error<StringError>(
186 "cannot emit to standard output without --flat.",
187 errc::invalid_argument);
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);
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);
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);
206 return Error::success();
209 static Expected<DwarfLinkerAccelTableKind>
210 getAccelTableKind(opt::InputArgList &Args) {
211 if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) {
212 StringRef S = Accelerator->getValue();
213 if (S == "Apple")
214 return DwarfLinkerAccelTableKind::Apple;
215 if (S == "Dwarf")
216 return DwarfLinkerAccelTableKind::Dwarf;
217 if (S == "Pub")
218 return DwarfLinkerAccelTableKind::Pub;
219 if (S == "Default")
220 return DwarfLinkerAccelTableKind::Default;
221 if (S == "None")
222 return DwarfLinkerAccelTableKind::None;
223 return make_error<StringError>("invalid accelerator type specified: '" + S +
224 "'. Supported values are 'Apple', "
225 "'Dwarf', 'Pub', 'Default' and 'None'.",
226 inconvertibleErrorCode());
228 return DwarfLinkerAccelTableKind::Default;
231 static Expected<ReproducerMode> getReproducerMode(opt::InputArgList &Args) {
232 if (Args.hasArg(OPT_gen_reproducer))
233 return ReproducerMode::GenerateOnExit;
234 if (opt::Arg *Reproducer = Args.getLastArg(OPT_reproducer)) {
235 StringRef S = Reproducer->getValue();
236 if (S == "GenerateOnExit")
237 return ReproducerMode::GenerateOnExit;
238 if (S == "GenerateOnCrash")
239 return ReproducerMode::GenerateOnCrash;
240 if (S == "Use")
241 return ReproducerMode::Use;
242 if (S == "Off")
243 return ReproducerMode::Off;
244 return make_error<StringError>(
245 "invalid reproducer mode: '" + S +
246 "'. Supported values are 'GenerateOnExit', 'GenerateOnCrash', "
247 "'Use', 'Off'.",
248 inconvertibleErrorCode());
250 return ReproducerMode::GenerateOnCrash;
253 static Expected<DWARFVerify> getVerifyKind(opt::InputArgList &Args) {
254 if (Args.hasArg(OPT_verify))
255 return DWARFVerify::Output;
256 if (opt::Arg *Verify = Args.getLastArg(OPT_verify_dwarf)) {
257 StringRef S = Verify->getValue();
258 if (S == "input")
259 return DWARFVerify::Input;
260 if (S == "output")
261 return DWARFVerify::Output;
262 if (S == "all")
263 return DWARFVerify::All;
264 if (S == "none")
265 return DWARFVerify::None;
266 return make_error<StringError>(
267 "invalid verify type specified: '" + S +
268 "'. Supported values are 'input', 'output', 'all' and 'none'.",
269 inconvertibleErrorCode());
271 return DWARFVerify::None;
274 /// Parses the command line options into the LinkOptions struct and performs
275 /// some sanity checking. Returns an error in case the latter fails.
276 static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
277 DsymutilOptions Options;
279 Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);
280 Options.DumpStab = Args.hasArg(OPT_symtab);
281 Options.Flat = Args.hasArg(OPT_flat);
282 Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);
283 Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail);
285 if (Expected<DWARFVerify> Verify = getVerifyKind(Args)) {
286 Options.Verify = *Verify;
287 } else {
288 return Verify.takeError();
291 Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr);
292 Options.LinkOpts.VerifyInputDWARF =
293 flagIsSet(Options.Verify, DWARFVerify::Input);
294 Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output);
295 Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
296 Options.LinkOpts.Update = Args.hasArg(OPT_update);
297 Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
298 Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);
299 Options.LinkOpts.KeepFunctionForStatic =
300 Args.hasArg(OPT_keep_func_for_static);
302 if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) {
303 Options.ReproMode = ReproducerMode::Use;
304 Options.ReproducerPath = ReproducerPath->getValue();
305 } else {
306 if (Expected<ReproducerMode> ReproMode = getReproducerMode(Args)) {
307 Options.ReproMode = *ReproMode;
308 } else {
309 return ReproMode.takeError();
313 if (Expected<DwarfLinkerAccelTableKind> AccelKind = getAccelTableKind(Args)) {
314 Options.LinkOpts.TheAccelTableKind = *AccelKind;
315 } else {
316 return AccelKind.takeError();
319 if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap))
320 Options.SymbolMap = SymbolMap->getValue();
322 if (Args.hasArg(OPT_symbolmap))
323 Options.LinkOpts.Update = true;
325 if (Expected<std::vector<std::string>> InputFiles =
326 getInputs(Args, Options.LinkOpts.Update)) {
327 Options.InputFiles = std::move(*InputFiles);
328 } else {
329 return InputFiles.takeError();
332 for (auto *Arch : Args.filtered(OPT_arch))
333 Options.Archs.push_back(Arch->getValue());
335 if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))
336 Options.LinkOpts.PrependPath = OsoPrependPath->getValue();
338 for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) {
339 auto Split = StringRef(Arg).split('=');
340 Options.LinkOpts.ObjectPrefixMap.insert(
341 {std::string(Split.first), std::string(Split.second)});
344 if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))
345 Options.OutputFile = OutputFile->getValue();
347 if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))
348 Options.Toolchain = Toolchain->getValue();
350 if (Args.hasArg(OPT_assembly))
351 Options.LinkOpts.FileType = OutputFileType::Assembly;
353 if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
354 Options.LinkOpts.Threads = atoi(NumThreads->getValue());
355 else
356 Options.LinkOpts.Threads = 0; // Use all available hardware threads
358 if (Options.DumpDebugMap || Options.LinkOpts.Verbose)
359 Options.LinkOpts.Threads = 1;
361 if (getenv("RC_DEBUG_OPTIONS"))
362 Options.PaperTrailWarnings = true;
364 if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path))
365 Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue();
367 if (opt::Arg *RemarksOutputFormat =
368 Args.getLastArg(OPT_remarks_output_format)) {
369 if (Expected<remarks::Format> FormatOrErr =
370 remarks::parseFormat(RemarksOutputFormat->getValue()))
371 Options.LinkOpts.RemarksFormat = *FormatOrErr;
372 else
373 return FormatOrErr.takeError();
376 if (Error E = verifyOptions(Options))
377 return std::move(E);
378 return Options;
381 static Error createPlistFile(StringRef Bin, StringRef BundleRoot,
382 StringRef Toolchain) {
383 // Create plist file to write to.
384 SmallString<128> InfoPlist(BundleRoot);
385 sys::path::append(InfoPlist, "Contents/Info.plist");
386 std::error_code EC;
387 raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF);
388 if (EC)
389 return make_error<StringError>(
390 "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
392 CFBundleInfo BI = getBundleInfo(Bin);
394 if (BI.IDStr.empty()) {
395 StringRef BundleID = *sys::path::rbegin(BundleRoot);
396 if (sys::path::extension(BundleRoot) == ".dSYM")
397 BI.IDStr = std::string(sys::path::stem(BundleID));
398 else
399 BI.IDStr = std::string(BundleID);
402 // Print out information to the plist file.
403 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
404 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
405 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
406 << "<plist version=\"1.0\">\n"
407 << "\t<dict>\n"
408 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
409 << "\t\t<string>English</string>\n"
410 << "\t\t<key>CFBundleIdentifier</key>\n"
411 << "\t\t<string>com.apple.xcode.dsym.";
412 printHTMLEscaped(BI.IDStr, PL);
413 PL << "</string>\n"
414 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
415 << "\t\t<string>6.0</string>\n"
416 << "\t\t<key>CFBundlePackageType</key>\n"
417 << "\t\t<string>dSYM</string>\n"
418 << "\t\t<key>CFBundleSignature</key>\n"
419 << "\t\t<string>\?\?\?\?</string>\n";
421 if (!BI.OmitShortVersion()) {
422 PL << "\t\t<key>CFBundleShortVersionString</key>\n";
423 PL << "\t\t<string>";
424 printHTMLEscaped(BI.ShortVersionStr, PL);
425 PL << "</string>\n";
428 PL << "\t\t<key>CFBundleVersion</key>\n";
429 PL << "\t\t<string>";
430 printHTMLEscaped(BI.VersionStr, PL);
431 PL << "</string>\n";
433 if (!Toolchain.empty()) {
434 PL << "\t\t<key>Toolchain</key>\n";
435 PL << "\t\t<string>";
436 printHTMLEscaped(Toolchain, PL);
437 PL << "</string>\n";
440 PL << "\t</dict>\n"
441 << "</plist>\n";
443 PL.close();
444 return Error::success();
447 static Error createBundleDir(StringRef BundleBase) {
448 SmallString<128> Bundle(BundleBase);
449 sys::path::append(Bundle, "Contents", "Resources", "DWARF");
450 if (std::error_code EC =
451 create_directories(Bundle.str(), true, sys::fs::perms::all_all))
452 return make_error<StringError>(
453 "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
455 return Error::success();
458 static bool verifyOutput(StringRef OutputFile, StringRef Arch, bool Verbose) {
459 if (OutputFile == "-") {
460 WithColor::warning() << "verification skipped for " << Arch
461 << "because writing to stdout.\n";
462 return true;
465 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
466 if (!BinOrErr) {
467 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
468 return false;
471 Binary &Binary = *BinOrErr.get().getBinary();
472 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
473 raw_ostream &os = Verbose ? errs() : nulls();
474 os << "Verifying DWARF for architecture: " << Arch << "\n";
475 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
476 DIDumpOptions DumpOpts;
477 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
478 if (!success)
479 WithColor::error() << "output verification failed for " << Arch << '\n';
480 return success;
483 return false;
486 namespace {
487 struct OutputLocation {
488 OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {})
489 : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
490 /// This method is a workaround for older compilers.
491 Optional<std::string> getResourceDir() const { return ResourceDir; }
492 std::string DWARFFile;
493 Optional<std::string> ResourceDir;
495 } // namespace
497 static Expected<OutputLocation>
498 getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {
499 if (Options.OutputFile == "-")
500 return OutputLocation(Options.OutputFile);
502 // When updating, do in place replacement.
503 if (Options.OutputFile.empty() &&
504 (Options.LinkOpts.Update || !Options.SymbolMap.empty()))
505 return OutputLocation(std::string(InputFile));
507 // When dumping the debug map, just return an empty output location. This
508 // allows us to compute the output location once.
509 if (Options.DumpDebugMap)
510 return OutputLocation("");
512 // If a flat dSYM has been requested, things are pretty simple.
513 if (Options.Flat) {
514 if (Options.OutputFile.empty()) {
515 if (InputFile == "-")
516 return OutputLocation{"a.out.dwarf", {}};
517 return OutputLocation((InputFile + ".dwarf").str());
520 return OutputLocation(Options.OutputFile);
523 // We need to create/update a dSYM bundle.
524 // A bundle hierarchy looks like this:
525 // <bundle name>.dSYM/
526 // Contents/
527 // Info.plist
528 // Resources/
529 // DWARF/
530 // <DWARF file(s)>
531 std::string DwarfFile =
532 std::string(InputFile == "-" ? StringRef("a.out") : InputFile);
533 SmallString<128> Path(Options.OutputFile);
534 if (Path.empty())
535 Path = DwarfFile + ".dSYM";
536 if (!Options.LinkOpts.NoOutput) {
537 if (auto E = createBundleDir(Path))
538 return std::move(E);
539 if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
540 return std::move(E);
543 sys::path::append(Path, "Contents", "Resources");
544 std::string ResourceDir = std::string(Path.str());
545 sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));
546 return OutputLocation(std::string(Path.str()), ResourceDir);
549 int dsymutil_main(int argc, char **argv) {
550 InitLLVM X(argc, argv);
552 // Parse arguments.
553 DsymutilOptTable T;
554 unsigned MAI;
555 unsigned MAC;
556 ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1);
557 opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
559 void *P = (void *)(intptr_t)getOutputFileName;
560 std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);
561 SDKPath = std::string(sys::path::parent_path(SDKPath));
563 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
564 WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()
565 << '\n';
568 if (Args.hasArg(OPT_help)) {
569 T.printHelp(
570 outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),
571 "manipulate archived DWARF debug symbol files.\n\n"
572 "dsymutil links the DWARF debug information found in the object files\n"
573 "for the executable <input file> by using debug symbols information\n"
574 "contained in its symbol table.\n",
575 false);
576 return EXIT_SUCCESS;
579 if (Args.hasArg(OPT_version)) {
580 cl::PrintVersionMessage();
581 return EXIT_SUCCESS;
584 auto OptionsOrErr = getOptions(Args);
585 if (!OptionsOrErr) {
586 WithColor::error() << toString(OptionsOrErr.takeError());
587 return EXIT_FAILURE;
590 auto &Options = *OptionsOrErr;
592 InitializeAllTargetInfos();
593 InitializeAllTargetMCs();
594 InitializeAllTargets();
595 InitializeAllAsmPrinters();
597 auto Repro = Reproducer::createReproducer(Options.ReproMode,
598 Options.ReproducerPath, argc, argv);
599 if (!Repro) {
600 WithColor::error() << toString(Repro.takeError());
601 return EXIT_FAILURE;
604 Options.LinkOpts.VFS = (*Repro)->getVFS();
606 for (const auto &Arch : Options.Archs)
607 if (Arch != "*" && Arch != "all" &&
608 !object::MachOObjectFile::isValidArch(Arch)) {
609 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
610 return EXIT_FAILURE;
613 SymbolMapLoader SymMapLoader(Options.SymbolMap);
615 for (auto &InputFile : Options.InputFiles) {
616 // Dump the symbol table for each input file and requested arch
617 if (Options.DumpStab) {
618 if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs,
619 Options.LinkOpts.PrependPath))
620 return EXIT_FAILURE;
621 continue;
624 auto DebugMapPtrsOrErr =
625 parseDebugMap(Options.LinkOpts.VFS, InputFile, Options.Archs,
626 Options.LinkOpts.PrependPath, Options.PaperTrailWarnings,
627 Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap);
629 if (auto EC = DebugMapPtrsOrErr.getError()) {
630 WithColor::error() << "cannot parse the debug map for '" << InputFile
631 << "': " << EC.message() << '\n';
632 return EXIT_FAILURE;
635 // Remember the number of debug maps that are being processed to decide how
636 // to name the remark files.
637 Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size();
639 if (Options.LinkOpts.Update) {
640 // The debug map should be empty. Add one object file corresponding to
641 // the input file.
642 for (auto &Map : *DebugMapPtrsOrErr)
643 Map->addDebugMapObject(InputFile,
644 sys::TimePoint<std::chrono::seconds>());
647 // Ensure that the debug map is not empty (anymore).
648 if (DebugMapPtrsOrErr->empty()) {
649 WithColor::error() << "no architecture to link\n";
650 return EXIT_FAILURE;
653 // Shared a single binary holder for all the link steps.
654 BinaryHolder BinHolder(Options.LinkOpts.VFS);
656 // Compute the output location and update the resource directory.
657 Expected<OutputLocation> OutputLocationOrErr =
658 getOutputFileName(InputFile, Options);
659 if (!OutputLocationOrErr) {
660 WithColor::error() << toString(OutputLocationOrErr.takeError());
661 return EXIT_FAILURE;
663 Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();
665 // Statistics only require different architectures to be processed
666 // sequentially, the link itself can still happen in parallel. Change the
667 // thread pool strategy here instead of modifying LinkOpts.Threads.
668 ThreadPoolStrategy S = hardware_concurrency(
669 Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);
670 if (Options.LinkOpts.Threads == 0) {
671 // If NumThreads is not specified, create one thread for each input, up to
672 // the number of hardware threads.
673 S.ThreadsRequested = DebugMapPtrsOrErr->size();
674 S.Limit = true;
676 ThreadPool Threads(S);
678 // If there is more than one link to execute, we need to generate
679 // temporary files.
680 const bool NeedsTempFiles =
681 !Options.DumpDebugMap && (Options.OutputFile != "-") &&
682 (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);
683 bool VerifyOutput = flagIsSet(Options.Verify, DWARFVerify::Output);
684 if (VerifyOutput && Options.LinkOpts.NoOutput) {
685 WithColor::warning()
686 << "skipping output verification because --no-output was passed\n";
687 VerifyOutput = false;
690 // Set up a crash recovery context.
691 CrashRecoveryContext::Enable();
692 CrashRecoveryContext CRC;
693 CRC.DumpStackAndCleanupOnFailure = true;
695 std::atomic_char AllOK(1);
696 SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
698 const bool Crashed = !CRC.RunSafely([&]() {
699 for (auto &Map : *DebugMapPtrsOrErr) {
700 if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
701 Map->print(outs());
703 if (Options.DumpDebugMap)
704 continue;
706 if (!Options.SymbolMap.empty())
707 Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map);
709 if (Map->begin() == Map->end())
710 WithColor::warning()
711 << "no debug symbols in executable (-arch "
712 << MachOUtils::getArchName(Map->getTriple().getArchName())
713 << ")\n";
715 // Using a std::shared_ptr rather than std::unique_ptr because move-only
716 // types don't work with std::bind in the ThreadPool implementation.
717 std::shared_ptr<raw_fd_ostream> OS;
719 std::string OutputFile = OutputLocationOrErr->DWARFFile;
720 if (NeedsTempFiles) {
721 TempFiles.emplace_back(Map->getTriple().getArchName().str());
723 auto E = TempFiles.back().createTempFile();
724 if (E) {
725 WithColor::error() << toString(std::move(E));
726 AllOK.fetch_and(false);
727 return;
730 auto &TempFile = *(TempFiles.back().File);
731 OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
732 /*shouldClose*/ false);
733 OutputFile = TempFile.TmpName;
734 } else {
735 std::error_code EC;
736 OS = std::make_shared<raw_fd_ostream>(
737 Options.LinkOpts.NoOutput ? "-" : OutputFile, EC,
738 sys::fs::OF_None);
739 if (EC) {
740 WithColor::error() << OutputFile << ": " << EC.message();
741 AllOK.fetch_and(false);
742 return;
746 auto LinkLambda = [&,
747 OutputFile](std::shared_ptr<raw_fd_ostream> Stream,
748 LinkOptions Options) {
749 AllOK.fetch_and(
750 linkDwarf(*Stream, BinHolder, *Map, std::move(Options)));
751 Stream->flush();
752 if (VerifyOutput) {
753 AllOK.fetch_and(verifyOutput(
754 OutputFile, Map->getTriple().getArchName(), Options.Verbose));
758 // FIXME: The DwarfLinker can have some very deep recursion that can max
759 // out the (significantly smaller) stack when using threads. We don't
760 // want this limitation when we only have a single thread.
761 if (S.ThreadsRequested == 1)
762 LinkLambda(OS, Options.LinkOpts);
763 else
764 Threads.async(LinkLambda, OS, Options.LinkOpts);
767 Threads.wait();
770 if (Crashed)
771 (*Repro)->generate();
773 if (!AllOK)
774 return EXIT_FAILURE;
776 if (NeedsTempFiles) {
777 // Universal Mach-O files can't have an archicture slice that starts
778 // beyond the 4GB boundary. "lipo" can creeate a 64 bit universal header,
779 // but not all tools can parse these files so we want to return an error
780 // if the file can't be encoded as a file with a 32 bit universal header.
781 // To detect this, we check the size of each architecture's skinny Mach-O
782 // file and add up the offsets. If they exceed 4GB, then we return an
783 // error.
785 // First we compute the right offset where the first architecture will fit
786 // followin the 32 bit universal header. The 32 bit universal header
787 // starts with a uint32_t magic and a uint32_t number of architecture
788 // infos. Then it is followed by 5 uint32_t values for each architecture.
789 // So we set the start offset to the right value so we can calculate the
790 // exact offset that the first architecture slice can start at.
791 constexpr uint64_t MagicAndCountSize = 2 * 4;
792 constexpr uint64_t UniversalArchInfoSize = 5 * 4;
793 uint64_t FileOffset = MagicAndCountSize +
794 UniversalArchInfoSize * TempFiles.size();
795 for (const auto &File: TempFiles) {
796 ErrorOr<vfs::Status> stat = Options.LinkOpts.VFS->status(File.path());
797 if (!stat)
798 break;
799 FileOffset += stat->getSize();
800 if (FileOffset > UINT32_MAX) {
801 WithColor::error() << "the univesral binary has a slice with an "
802 "offset exceeds 4GB and will produce an invalid Mach-O file.";
803 return EXIT_FAILURE;
806 if (!MachOUtils::generateUniversalBinary(TempFiles,
807 OutputLocationOrErr->DWARFFile,
808 Options.LinkOpts, SDKPath))
809 return EXIT_FAILURE;
813 return EXIT_SUCCESS;