[clang][modules] Don't prevent translation of FW_Private includes when explicitly...
[llvm-project.git] / llvm / tools / dsymutil / dsymutil.cpp
blob2dd123318e00bd0a6153e5d59ec57456223610a8
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 "DwarfLinkerForBinary.h"
18 #include "LinkUtils.h"
19 #include "MachOUtils.h"
20 #include "Reproducer.h"
21 #include "llvm/ADT/STLExtras.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/SmallVector.h"
24 #include "llvm/ADT/StringExtras.h"
25 #include "llvm/ADT/StringRef.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/FormatVariadic.h"
40 #include "llvm/Support/InitLLVM.h"
41 #include "llvm/Support/LLVMDriver.h"
42 #include "llvm/Support/Path.h"
43 #include "llvm/Support/TargetSelect.h"
44 #include "llvm/Support/ThreadPool.h"
45 #include "llvm/Support/WithColor.h"
46 #include "llvm/Support/raw_ostream.h"
47 #include "llvm/Support/thread.h"
48 #include "llvm/TargetParser/Triple.h"
49 #include <algorithm>
50 #include <cstdint>
51 #include <cstdlib>
52 #include <string>
53 #include <system_error>
55 using namespace llvm;
56 using namespace llvm::dsymutil;
57 using namespace object;
59 namespace {
60 enum ID {
61 OPT_INVALID = 0, // This is not an option ID.
62 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
63 #include "Options.inc"
64 #undef OPTION
67 #define PREFIX(NAME, VALUE) \
68 static constexpr StringLiteral NAME##_init[] = VALUE; \
69 static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
70 std::size(NAME##_init) - 1);
71 #include "Options.inc"
72 #undef PREFIX
74 using namespace llvm::opt;
75 static constexpr opt::OptTable::Info InfoTable[] = {
76 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
77 #include "Options.inc"
78 #undef OPTION
81 class DsymutilOptTable : public opt::GenericOptTable {
82 public:
83 DsymutilOptTable() : opt::GenericOptTable(InfoTable) {}
85 } // namespace
87 enum class DWARFVerify : uint8_t {
88 None = 0,
89 Input = 1 << 0,
90 Output = 1 << 1,
91 OutputOnValidInput = 1 << 2,
92 All = Input | Output,
93 Auto = Input | OutputOnValidInput,
94 #if !defined(NDEBUG) || defined(EXPENSIVE_CHECKS)
95 Default = Auto
96 #else
97 Default = None
98 #endif
101 inline bool flagIsSet(DWARFVerify Flags, DWARFVerify SingleFlag) {
102 return static_cast<uint8_t>(Flags) & static_cast<uint8_t>(SingleFlag);
105 struct DsymutilOptions {
106 bool DumpDebugMap = false;
107 bool DumpStab = false;
108 bool Flat = false;
109 bool InputIsYAMLDebugMap = false;
110 bool ForceKeepFunctionForStatic = false;
111 std::string SymbolMap;
112 std::string OutputFile;
113 std::string Toolchain;
114 std::string ReproducerPath;
115 std::vector<std::string> Archs;
116 std::vector<std::string> InputFiles;
117 unsigned NumThreads;
118 DWARFVerify Verify = DWARFVerify::Default;
119 ReproducerMode ReproMode = ReproducerMode::GenerateOnCrash;
120 dsymutil::LinkOptions LinkOpts;
123 /// Return a list of input files. This function has logic for dealing with the
124 /// special case where we might have dSYM bundles as input. The function
125 /// returns an error when the directory structure doesn't match that of a dSYM
126 /// bundle.
127 static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args,
128 bool DsymAsInput) {
129 std::vector<std::string> InputFiles;
130 for (auto *File : Args.filtered(OPT_INPUT))
131 InputFiles.push_back(File->getValue());
133 if (!DsymAsInput)
134 return InputFiles;
136 // If we are updating, we might get dSYM bundles as input.
137 std::vector<std::string> Inputs;
138 for (const auto &Input : InputFiles) {
139 if (!sys::fs::is_directory(Input)) {
140 Inputs.push_back(Input);
141 continue;
144 // Make sure that we're dealing with a dSYM bundle.
145 SmallString<256> BundlePath(Input);
146 sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
147 if (!sys::fs::is_directory(BundlePath))
148 return make_error<StringError>(
149 Input + " is a directory, but doesn't look like a dSYM bundle.",
150 inconvertibleErrorCode());
152 // Create a directory iterator to iterate over all the entries in the
153 // bundle.
154 std::error_code EC;
155 sys::fs::directory_iterator DirIt(BundlePath, EC);
156 sys::fs::directory_iterator DirEnd;
157 if (EC)
158 return errorCodeToError(EC);
160 // Add each entry to the list of inputs.
161 while (DirIt != DirEnd) {
162 Inputs.push_back(DirIt->path());
163 DirIt.increment(EC);
164 if (EC)
165 return errorCodeToError(EC);
168 return Inputs;
171 // Verify that the given combination of options makes sense.
172 static Error verifyOptions(const DsymutilOptions &Options) {
173 if (Options.InputFiles.empty()) {
174 return make_error<StringError>("no input files specified",
175 errc::invalid_argument);
178 if (Options.LinkOpts.Update && llvm::is_contained(Options.InputFiles, "-")) {
179 // FIXME: We cannot use stdin for an update because stdin will be
180 // consumed by the BinaryHolder during the debugmap parsing, and
181 // then we will want to consume it again in DwarfLinker. If we
182 // used a unique BinaryHolder object that could cache multiple
183 // binaries this restriction would go away.
184 return make_error<StringError>(
185 "standard input cannot be used as input for a dSYM update.",
186 errc::invalid_argument);
189 if (!Options.Flat && Options.OutputFile == "-")
190 return make_error<StringError>(
191 "cannot emit to standard output without --flat.",
192 errc::invalid_argument);
194 if (Options.InputFiles.size() > 1 && Options.Flat &&
195 !Options.OutputFile.empty())
196 return make_error<StringError>(
197 "cannot use -o with multiple inputs in flat mode.",
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<DsymutilAccelTableKind>
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 DsymutilAccelTableKind::Apple;
215 if (S == "Dwarf")
216 return DsymutilAccelTableKind::Dwarf;
217 if (S == "Pub")
218 return DsymutilAccelTableKind::Pub;
219 if (S == "Default")
220 return DsymutilAccelTableKind::Default;
221 if (S == "None")
222 return DsymutilAccelTableKind::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 DsymutilAccelTableKind::Default;
231 static Expected<DsymutilDWARFLinkerType>
232 getDWARFLinkerType(opt::InputArgList &Args) {
233 if (opt::Arg *LinkerType = Args.getLastArg(OPT_linker)) {
234 StringRef S = LinkerType->getValue();
235 if (S == "apple")
236 return DsymutilDWARFLinkerType::Apple;
237 if (S == "llvm")
238 return DsymutilDWARFLinkerType::LLVM;
239 return make_error<StringError>("invalid DWARF linker type specified: '" +
241 "'. Supported values are 'apple', "
242 "'llvm'.",
243 inconvertibleErrorCode());
246 return DsymutilDWARFLinkerType::Apple;
249 static Expected<ReproducerMode> getReproducerMode(opt::InputArgList &Args) {
250 if (Args.hasArg(OPT_gen_reproducer))
251 return ReproducerMode::GenerateOnExit;
252 if (opt::Arg *Reproducer = Args.getLastArg(OPT_reproducer)) {
253 StringRef S = Reproducer->getValue();
254 if (S == "GenerateOnExit")
255 return ReproducerMode::GenerateOnExit;
256 if (S == "GenerateOnCrash")
257 return ReproducerMode::GenerateOnCrash;
258 if (S == "Off")
259 return ReproducerMode::Off;
260 return make_error<StringError>(
261 "invalid reproducer mode: '" + S +
262 "'. Supported values are 'GenerateOnExit', 'GenerateOnCrash', "
263 "'Off'.",
264 inconvertibleErrorCode());
266 return ReproducerMode::GenerateOnCrash;
269 static Expected<DWARFVerify> getVerifyKind(opt::InputArgList &Args) {
270 if (Args.hasArg(OPT_verify))
271 return DWARFVerify::Output;
272 if (opt::Arg *Verify = Args.getLastArg(OPT_verify_dwarf)) {
273 StringRef S = Verify->getValue();
274 if (S == "input")
275 return DWARFVerify::Input;
276 if (S == "output")
277 return DWARFVerify::Output;
278 if (S == "all")
279 return DWARFVerify::All;
280 if (S == "auto")
281 return DWARFVerify::Auto;
282 if (S == "none")
283 return DWARFVerify::None;
284 return make_error<StringError>("invalid verify type specified: '" + S +
285 "'. Supported values are 'none', "
286 "'input', 'output', 'all' and 'auto'.",
287 inconvertibleErrorCode());
289 return DWARFVerify::Default;
292 /// Parses the command line options into the LinkOptions struct and performs
293 /// some sanity checking. Returns an error in case the latter fails.
294 static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
295 DsymutilOptions Options;
297 Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);
298 Options.DumpStab = Args.hasArg(OPT_symtab);
299 Options.Flat = Args.hasArg(OPT_flat);
300 Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);
302 if (Expected<DWARFVerify> Verify = getVerifyKind(Args)) {
303 Options.Verify = *Verify;
304 } else {
305 return Verify.takeError();
308 Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr);
309 Options.LinkOpts.VerifyInputDWARF =
310 flagIsSet(Options.Verify, DWARFVerify::Input);
311 Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output);
312 Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
313 Options.LinkOpts.Update = Args.hasArg(OPT_update);
314 Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
315 Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics);
316 Options.LinkOpts.Fat64 = Args.hasArg(OPT_fat64);
317 Options.LinkOpts.KeepFunctionForStatic =
318 Args.hasArg(OPT_keep_func_for_static);
320 if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) {
321 Options.ReproMode = ReproducerMode::Use;
322 Options.ReproducerPath = ReproducerPath->getValue();
323 } else {
324 if (Expected<ReproducerMode> ReproMode = getReproducerMode(Args)) {
325 Options.ReproMode = *ReproMode;
326 } else {
327 return ReproMode.takeError();
331 if (Expected<DsymutilAccelTableKind> AccelKind = getAccelTableKind(Args)) {
332 Options.LinkOpts.TheAccelTableKind = *AccelKind;
333 } else {
334 return AccelKind.takeError();
337 if (Expected<DsymutilDWARFLinkerType> DWARFLinkerType =
338 getDWARFLinkerType(Args)) {
339 Options.LinkOpts.DWARFLinkerType = *DWARFLinkerType;
340 } else {
341 return DWARFLinkerType.takeError();
344 if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap))
345 Options.SymbolMap = SymbolMap->getValue();
347 if (Args.hasArg(OPT_symbolmap))
348 Options.LinkOpts.Update = true;
350 if (Expected<std::vector<std::string>> InputFiles =
351 getInputs(Args, Options.LinkOpts.Update)) {
352 Options.InputFiles = std::move(*InputFiles);
353 } else {
354 return InputFiles.takeError();
357 for (auto *Arch : Args.filtered(OPT_arch))
358 Options.Archs.push_back(Arch->getValue());
360 if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))
361 Options.LinkOpts.PrependPath = OsoPrependPath->getValue();
363 for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) {
364 auto Split = StringRef(Arg).split('=');
365 Options.LinkOpts.ObjectPrefixMap.insert(
366 {std::string(Split.first), std::string(Split.second)});
369 if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))
370 Options.OutputFile = OutputFile->getValue();
372 if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))
373 Options.Toolchain = Toolchain->getValue();
375 if (Args.hasArg(OPT_assembly))
376 Options.LinkOpts.FileType = DWARFLinker::OutputFileType::Assembly;
378 if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
379 Options.LinkOpts.Threads = atoi(NumThreads->getValue());
380 else
381 Options.LinkOpts.Threads = 0; // Use all available hardware threads
383 if (Options.DumpDebugMap || Options.LinkOpts.Verbose)
384 Options.LinkOpts.Threads = 1;
386 if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path))
387 Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue();
389 if (opt::Arg *RemarksOutputFormat =
390 Args.getLastArg(OPT_remarks_output_format)) {
391 if (Expected<remarks::Format> FormatOrErr =
392 remarks::parseFormat(RemarksOutputFormat->getValue()))
393 Options.LinkOpts.RemarksFormat = *FormatOrErr;
394 else
395 return FormatOrErr.takeError();
398 Options.LinkOpts.RemarksKeepAll =
399 !Args.hasArg(OPT_remarks_drop_without_debug);
401 if (opt::Arg *BuildVariantSuffix = Args.getLastArg(OPT_build_variant_suffix))
402 Options.LinkOpts.BuildVariantSuffix = BuildVariantSuffix->getValue();
404 for (auto *SearchPath : Args.filtered(OPT_dsym_search_path))
405 Options.LinkOpts.DSYMSearchPaths.push_back(SearchPath->getValue());
407 if (Error E = verifyOptions(Options))
408 return std::move(E);
409 return Options;
412 static Error createPlistFile(StringRef Bin, StringRef BundleRoot,
413 StringRef Toolchain) {
414 // Create plist file to write to.
415 SmallString<128> InfoPlist(BundleRoot);
416 sys::path::append(InfoPlist, "Contents/Info.plist");
417 std::error_code EC;
418 raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF);
419 if (EC)
420 return make_error<StringError>(
421 "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
423 CFBundleInfo BI = getBundleInfo(Bin);
425 if (BI.IDStr.empty()) {
426 StringRef BundleID = *sys::path::rbegin(BundleRoot);
427 if (sys::path::extension(BundleRoot) == ".dSYM")
428 BI.IDStr = std::string(sys::path::stem(BundleID));
429 else
430 BI.IDStr = std::string(BundleID);
433 // Print out information to the plist file.
434 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
435 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
436 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
437 << "<plist version=\"1.0\">\n"
438 << "\t<dict>\n"
439 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
440 << "\t\t<string>English</string>\n"
441 << "\t\t<key>CFBundleIdentifier</key>\n"
442 << "\t\t<string>com.apple.xcode.dsym.";
443 printHTMLEscaped(BI.IDStr, PL);
444 PL << "</string>\n"
445 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
446 << "\t\t<string>6.0</string>\n"
447 << "\t\t<key>CFBundlePackageType</key>\n"
448 << "\t\t<string>dSYM</string>\n"
449 << "\t\t<key>CFBundleSignature</key>\n"
450 << "\t\t<string>\?\?\?\?</string>\n";
452 if (!BI.OmitShortVersion()) {
453 PL << "\t\t<key>CFBundleShortVersionString</key>\n";
454 PL << "\t\t<string>";
455 printHTMLEscaped(BI.ShortVersionStr, PL);
456 PL << "</string>\n";
459 PL << "\t\t<key>CFBundleVersion</key>\n";
460 PL << "\t\t<string>";
461 printHTMLEscaped(BI.VersionStr, PL);
462 PL << "</string>\n";
464 if (!Toolchain.empty()) {
465 PL << "\t\t<key>Toolchain</key>\n";
466 PL << "\t\t<string>";
467 printHTMLEscaped(Toolchain, PL);
468 PL << "</string>\n";
471 PL << "\t</dict>\n"
472 << "</plist>\n";
474 PL.close();
475 return Error::success();
478 static Error createBundleDir(StringRef BundleBase) {
479 SmallString<128> Bundle(BundleBase);
480 sys::path::append(Bundle, "Contents", "Resources", "DWARF");
481 if (std::error_code EC =
482 create_directories(Bundle.str(), true, sys::fs::perms::all_all))
483 return make_error<StringError>(
484 "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
486 return Error::success();
489 static bool verifyOutput(StringRef OutputFile, StringRef Arch,
490 DsymutilOptions Options, std::mutex &Mutex) {
492 if (OutputFile == "-") {
493 std::lock_guard<std::mutex> Guard(Mutex);
494 WithColor::warning() << "verification skipped for " << Arch
495 << " because writing to stdout.\n";
496 return true;
499 if (Options.LinkOpts.NoOutput) {
500 std::lock_guard<std::mutex> Guard(Mutex);
501 WithColor::warning() << "verification skipped for " << Arch
502 << " because --no-output was passed.\n";
503 return true;
506 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
507 if (!BinOrErr) {
508 std::lock_guard<std::mutex> Guard(Mutex);
509 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
510 return false;
513 Binary &Binary = *BinOrErr.get().getBinary();
514 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
515 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
516 if (DICtx->getMaxVersion() > 5) {
517 std::lock_guard<std::mutex> Guard(Mutex);
518 WithColor::warning()
519 << "verification skipped for " << Arch
520 << " because DWARF standard greater than v5 is not supported yet.\n";
521 return true;
524 if (Options.LinkOpts.Verbose) {
525 std::lock_guard<std::mutex> Guard(Mutex);
526 errs() << "Verifying DWARF for architecture: " << Arch << "\n";
529 std::string Buffer;
530 raw_string_ostream OS(Buffer);
532 DIDumpOptions DumpOpts;
533 bool success = DICtx->verify(OS, DumpOpts.noImplicitRecursion());
534 if (!success) {
535 std::lock_guard<std::mutex> Guard(Mutex);
536 errs() << OS.str();
537 WithColor::error() << "output verification failed for " << Arch << '\n';
539 return success;
542 return false;
545 namespace {
546 struct OutputLocation {
547 OutputLocation(std::string DWARFFile,
548 std::optional<std::string> ResourceDir = {})
549 : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
550 /// This method is a workaround for older compilers.
551 std::optional<std::string> getResourceDir() const { return ResourceDir; }
552 std::string DWARFFile;
553 std::optional<std::string> ResourceDir;
555 } // namespace
557 static Expected<OutputLocation>
558 getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {
559 if (Options.OutputFile == "-")
560 return OutputLocation(Options.OutputFile);
562 // When updating, do in place replacement.
563 if (Options.OutputFile.empty() &&
564 (Options.LinkOpts.Update || !Options.SymbolMap.empty()))
565 return OutputLocation(std::string(InputFile));
567 // When dumping the debug map, just return an empty output location. This
568 // allows us to compute the output location once.
569 if (Options.DumpDebugMap)
570 return OutputLocation("");
572 // If a flat dSYM has been requested, things are pretty simple.
573 if (Options.Flat) {
574 if (Options.OutputFile.empty()) {
575 if (InputFile == "-")
576 return OutputLocation{"a.out.dwarf", {}};
577 return OutputLocation((InputFile + ".dwarf").str());
580 return OutputLocation(Options.OutputFile);
583 // We need to create/update a dSYM bundle.
584 // A bundle hierarchy looks like this:
585 // <bundle name>.dSYM/
586 // Contents/
587 // Info.plist
588 // Resources/
589 // DWARF/
590 // <DWARF file(s)>
591 std::string DwarfFile =
592 std::string(InputFile == "-" ? StringRef("a.out") : InputFile);
593 SmallString<128> Path(Options.OutputFile);
594 if (Path.empty())
595 Path = DwarfFile + ".dSYM";
596 if (!Options.LinkOpts.NoOutput) {
597 if (auto E = createBundleDir(Path))
598 return std::move(E);
599 if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
600 return std::move(E);
603 sys::path::append(Path, "Contents", "Resources");
604 std::string ResourceDir = std::string(Path.str());
605 sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));
606 return OutputLocation(std::string(Path.str()), ResourceDir);
609 int dsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
610 InitLLVM X(argc, argv);
612 // Parse arguments.
613 DsymutilOptTable T;
614 unsigned MAI;
615 unsigned MAC;
616 ArrayRef<const char *> ArgsArr = ArrayRef(argv + 1, argc - 1);
617 opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
619 void *P = (void *)(intptr_t)getOutputFileName;
620 std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);
621 SDKPath = std::string(sys::path::parent_path(SDKPath));
623 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
624 WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()
625 << '\n';
628 if (Args.hasArg(OPT_help)) {
629 T.printHelp(
630 outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),
631 "manipulate archived DWARF debug symbol files.\n\n"
632 "dsymutil links the DWARF debug information found in the object files\n"
633 "for the executable <input file> by using debug symbols information\n"
634 "contained in its symbol table.\n",
635 false);
636 return EXIT_SUCCESS;
639 if (Args.hasArg(OPT_version)) {
640 cl::PrintVersionMessage();
641 return EXIT_SUCCESS;
644 auto OptionsOrErr = getOptions(Args);
645 if (!OptionsOrErr) {
646 WithColor::error() << toString(OptionsOrErr.takeError()) << '\n';
647 return EXIT_FAILURE;
650 auto &Options = *OptionsOrErr;
652 InitializeAllTargetInfos();
653 InitializeAllTargetMCs();
654 InitializeAllTargets();
655 InitializeAllAsmPrinters();
657 auto Repro = Reproducer::createReproducer(Options.ReproMode,
658 Options.ReproducerPath, argc, argv);
659 if (!Repro) {
660 WithColor::error() << toString(Repro.takeError()) << '\n';
661 return EXIT_FAILURE;
664 Options.LinkOpts.VFS = (*Repro)->getVFS();
666 for (const auto &Arch : Options.Archs)
667 if (Arch != "*" && Arch != "all" &&
668 !object::MachOObjectFile::isValidArch(Arch)) {
669 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
670 return EXIT_FAILURE;
673 SymbolMapLoader SymMapLoader(Options.SymbolMap);
675 for (auto &InputFile : Options.InputFiles) {
676 // Dump the symbol table for each input file and requested arch
677 if (Options.DumpStab) {
678 if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs,
679 Options.LinkOpts.DSYMSearchPaths,
680 Options.LinkOpts.PrependPath,
681 Options.LinkOpts.BuildVariantSuffix))
682 return EXIT_FAILURE;
683 continue;
686 auto DebugMapPtrsOrErr = parseDebugMap(
687 Options.LinkOpts.VFS, InputFile, Options.Archs,
688 Options.LinkOpts.DSYMSearchPaths, Options.LinkOpts.PrependPath,
689 Options.LinkOpts.BuildVariantSuffix, Options.LinkOpts.Verbose,
690 Options.InputIsYAMLDebugMap);
692 if (auto EC = DebugMapPtrsOrErr.getError()) {
693 WithColor::error() << "cannot parse the debug map for '" << InputFile
694 << "': " << EC.message() << '\n';
695 return EXIT_FAILURE;
698 // Remember the number of debug maps that are being processed to decide how
699 // to name the remark files.
700 Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size();
702 if (Options.LinkOpts.Update) {
703 // The debug map should be empty. Add one object file corresponding to
704 // the input file.
705 for (auto &Map : *DebugMapPtrsOrErr)
706 Map->addDebugMapObject(InputFile,
707 sys::TimePoint<std::chrono::seconds>());
710 // Ensure that the debug map is not empty (anymore).
711 if (DebugMapPtrsOrErr->empty()) {
712 WithColor::error() << "no architecture to link\n";
713 return EXIT_FAILURE;
716 // Shared a single binary holder for all the link steps.
717 BinaryHolder BinHolder(Options.LinkOpts.VFS);
719 // Compute the output location and update the resource directory.
720 Expected<OutputLocation> OutputLocationOrErr =
721 getOutputFileName(InputFile, Options);
722 if (!OutputLocationOrErr) {
723 WithColor::error() << toString(OutputLocationOrErr.takeError());
724 return EXIT_FAILURE;
726 Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();
728 // Statistics only require different architectures to be processed
729 // sequentially, the link itself can still happen in parallel. Change the
730 // thread pool strategy here instead of modifying LinkOpts.Threads.
731 ThreadPoolStrategy S = hardware_concurrency(
732 Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads);
733 if (Options.LinkOpts.Threads == 0) {
734 // If NumThreads is not specified, create one thread for each input, up to
735 // the number of hardware threads.
736 S.ThreadsRequested = DebugMapPtrsOrErr->size();
737 S.Limit = true;
739 ThreadPool Threads(S);
741 // If there is more than one link to execute, we need to generate
742 // temporary files.
743 const bool NeedsTempFiles =
744 !Options.DumpDebugMap && (Options.OutputFile != "-") &&
745 (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);
747 std::atomic_char AllOK(1);
748 SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
750 std::mutex ErrorHandlerMutex;
752 // Set up a crash recovery context.
753 CrashRecoveryContext::Enable();
754 CrashRecoveryContext CRC;
755 CRC.DumpStackAndCleanupOnFailure = true;
757 const bool Crashed = !CRC.RunSafely([&]() {
758 for (auto &Map : *DebugMapPtrsOrErr) {
759 if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
760 Map->print(outs());
762 if (Options.DumpDebugMap)
763 continue;
765 if (!Options.SymbolMap.empty())
766 Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map);
768 if (Map->begin() == Map->end()) {
769 std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
770 WithColor::warning()
771 << "no debug symbols in executable (-arch "
772 << MachOUtils::getArchName(Map->getTriple().getArchName())
773 << ")\n";
776 // Using a std::shared_ptr rather than std::unique_ptr because move-only
777 // types don't work with std::bind in the ThreadPool implementation.
778 std::shared_ptr<raw_fd_ostream> OS;
780 std::string OutputFile = OutputLocationOrErr->DWARFFile;
781 if (NeedsTempFiles) {
782 TempFiles.emplace_back(Map->getTriple().getArchName().str());
784 auto E = TempFiles.back().createTempFile();
785 if (E) {
786 std::lock_guard<std::mutex> Guard(ErrorHandlerMutex);
787 WithColor::error() << toString(std::move(E));
788 AllOK.fetch_and(false);
789 return;
792 MachOUtils::ArchAndFile &AF = TempFiles.back();
793 OS = std::make_shared<raw_fd_ostream>(AF.getFD(),
794 /*shouldClose*/ false);
795 OutputFile = AF.getPath();
796 } else {
797 std::error_code EC;
798 OS = std::make_shared<raw_fd_ostream>(
799 Options.LinkOpts.NoOutput ? "-" : OutputFile, EC,
800 sys::fs::OF_None);
801 if (EC) {
802 WithColor::error() << OutputFile << ": " << EC.message();
803 AllOK.fetch_and(false);
804 return;
808 auto LinkLambda = [&,
809 OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
810 DwarfLinkerForBinary Linker(*Stream, BinHolder, Options.LinkOpts,
811 ErrorHandlerMutex);
812 AllOK.fetch_and(Linker.link(*Map));
813 Stream->flush();
814 if (flagIsSet(Options.Verify, DWARFVerify::Output) ||
815 (flagIsSet(Options.Verify, DWARFVerify::OutputOnValidInput) &&
816 !Linker.InputVerificationFailed())) {
817 AllOK.fetch_and(verifyOutput(OutputFile,
818 Map->getTriple().getArchName(),
819 Options, ErrorHandlerMutex));
823 // FIXME: The DwarfLinker can have some very deep recursion that can max
824 // out the (significantly smaller) stack when using threads. We don't
825 // want this limitation when we only have a single thread.
826 if (S.ThreadsRequested == 1)
827 LinkLambda(OS);
828 else
829 Threads.async(LinkLambda, OS);
832 Threads.wait();
835 if (Crashed)
836 (*Repro)->generate();
838 if (!AllOK)
839 return EXIT_FAILURE;
841 if (NeedsTempFiles) {
842 const bool Fat64 = Options.LinkOpts.Fat64;
843 if (!Fat64) {
844 // Universal Mach-O files can't have an archicture slice that starts
845 // beyond the 4GB boundary. "lipo" can create a 64 bit universal
846 // header, but not all tools can parse these files so we want to return
847 // an error if the file can't be encoded as a file with a 32 bit
848 // universal header. To detect this, we check the size of each
849 // architecture's skinny Mach-O file and add up the offsets. If they
850 // exceed 4GB, then we return an error.
852 // First we compute the right offset where the first architecture will
853 // fit followin the 32 bit universal header. The 32 bit universal header
854 // starts with a uint32_t magic and a uint32_t number of architecture
855 // infos. Then it is followed by 5 uint32_t values for each
856 // architecture. So we set the start offset to the right value so we can
857 // calculate the exact offset that the first architecture slice can
858 // start at.
859 constexpr uint64_t MagicAndCountSize = 2 * 4;
860 constexpr uint64_t UniversalArchInfoSize = 5 * 4;
861 uint64_t FileOffset =
862 MagicAndCountSize + UniversalArchInfoSize * TempFiles.size();
863 for (const auto &File : TempFiles) {
864 ErrorOr<vfs::Status> stat =
865 Options.LinkOpts.VFS->status(File.getPath());
866 if (!stat)
867 break;
868 if (FileOffset > UINT32_MAX) {
869 WithColor::error()
870 << formatv("the universal binary has a slice with a starting "
871 "offset ({0:x}) that exceeds 4GB and will produce "
872 "an invalid Mach-O file. Use the -fat64 flag to "
873 "generate a universal binary with a 64-bit header "
874 "but note that not all tools support this format.",
875 FileOffset);
876 return EXIT_FAILURE;
878 FileOffset += stat->getSize();
881 if (!MachOUtils::generateUniversalBinary(
882 TempFiles, OutputLocationOrErr->DWARFFile, Options.LinkOpts,
883 SDKPath, Fat64))
884 return EXIT_FAILURE;
888 return EXIT_SUCCESS;