Revert r354244 "[DAGCombiner] Eliminate dead stores to stack."
[llvm-complete.git] / tools / dsymutil / dsymutil.cpp
blob14f9a0e5137f66f2a5169c744710e47daa6a2970
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 "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/Triple.h"
24 #include "llvm/DebugInfo/DIContext.h"
25 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
26 #include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
27 #include "llvm/Object/Binary.h"
28 #include "llvm/Object/MachO.h"
29 #include "llvm/Support/CommandLine.h"
30 #include "llvm/Support/FileSystem.h"
31 #include "llvm/Support/InitLLVM.h"
32 #include "llvm/Support/ManagedStatic.h"
33 #include "llvm/Support/Path.h"
34 #include "llvm/Support/TargetSelect.h"
35 #include "llvm/Support/ThreadPool.h"
36 #include "llvm/Support/WithColor.h"
37 #include "llvm/Support/raw_ostream.h"
38 #include "llvm/Support/thread.h"
39 #include <algorithm>
40 #include <cstdint>
41 #include <cstdlib>
42 #include <string>
43 #include <system_error>
45 using namespace llvm;
46 using namespace llvm::cl;
47 using namespace llvm::dsymutil;
48 using namespace object;
50 static OptionCategory DsymCategory("Specific Options");
51 static opt<bool> Help("h", desc("Alias for -help"), Hidden);
52 static opt<bool> Version("v", desc("Alias for -version"), Hidden);
54 static list<std::string> InputFiles(Positional, OneOrMore,
55 desc("<input files>"), cat(DsymCategory));
57 static opt<std::string>
58 OutputFileOpt("o",
59 desc("Specify the output file. default: <input file>.dwarf"),
60 value_desc("filename"), cat(DsymCategory));
61 static alias OutputFileOptA("out", desc("Alias for -o"),
62 aliasopt(OutputFileOpt));
64 static opt<std::string> OsoPrependPath(
65 "oso-prepend-path",
66 desc("Specify a directory to prepend to the paths of object files."),
67 value_desc("path"), cat(DsymCategory));
69 static opt<bool> Assembly(
70 "S",
71 desc("Output textual assembly instead of a binary dSYM companion file."),
72 init(false), cat(DsymCategory), cl::Hidden);
74 static opt<bool> DumpStab(
75 "symtab",
76 desc("Dumps the symbol table found in executable or object file(s) and\n"
77 "exits."),
78 init(false), cat(DsymCategory));
79 static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
81 static opt<bool> FlatOut("flat",
82 desc("Produce a flat dSYM file (not a bundle)."),
83 init(false), cat(DsymCategory));
84 static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
86 static opt<bool> Minimize(
87 "minimize",
88 desc("When used when creating a dSYM file with Apple accelerator tables,\n"
89 "this option will suppress the emission of the .debug_inlines, \n"
90 ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n"
91 "has better equivalents: .apple_names and .apple_types. When used in\n"
92 "conjunction with --update option, this option will cause redundant\n"
93 "accelerator tables to be removed."),
94 init(false), cat(DsymCategory));
95 static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize));
97 static opt<bool> Update(
98 "update",
99 desc("Updates existing dSYM files to contain the latest accelerator\n"
100 "tables and other DWARF optimizations."),
101 init(false), cat(DsymCategory));
102 static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
104 static opt<std::string> SymbolMap(
105 "symbol-map",
106 desc("Updates the existing dSYMs inplace using symbol map specified."),
107 value_desc("bcsymbolmap"), cat(DsymCategory));
109 static cl::opt<AccelTableKind> AcceleratorTable(
110 "accelerator", cl::desc("Output accelerator tables."),
111 cl::values(clEnumValN(AccelTableKind::Default, "Default",
112 "Default for input."),
113 clEnumValN(AccelTableKind::Apple, "Apple", "Apple"),
114 clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")),
115 cl::init(AccelTableKind::Default), cat(DsymCategory));
117 static opt<unsigned> NumThreads(
118 "num-threads",
119 desc("Specifies the maximum number (n) of simultaneous threads to use\n"
120 "when linking multiple architectures."),
121 value_desc("n"), init(0), cat(DsymCategory));
122 static alias NumThreadsA("j", desc("Alias for --num-threads"),
123 aliasopt(NumThreads));
125 static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
126 cat(DsymCategory));
128 static opt<bool>
129 NoOutput("no-output",
130 desc("Do the link in memory, but do not emit the result file."),
131 init(false), cat(DsymCategory));
133 static opt<bool>
134 NoTimestamp("no-swiftmodule-timestamp",
135 desc("Don't check timestamp for swiftmodule files."),
136 init(false), cat(DsymCategory));
138 static list<std::string> ArchFlags(
139 "arch",
140 desc("Link DWARF debug information only for specified CPU architecture\n"
141 "types. This option can be specified multiple times, once for each\n"
142 "desired architecture. All CPU architectures will be linked by\n"
143 "default."),
144 value_desc("arch"), ZeroOrMore, cat(DsymCategory));
146 static opt<bool>
147 NoODR("no-odr",
148 desc("Do not use ODR (One Definition Rule) for type uniquing."),
149 init(false), cat(DsymCategory));
151 static opt<bool> DumpDebugMap(
152 "dump-debug-map",
153 desc("Parse and dump the debug map to standard output. Not DWARF link "
154 "will take place."),
155 init(false), cat(DsymCategory));
157 static opt<bool> InputIsYAMLDebugMap(
158 "y", desc("Treat the input file is a YAML debug map rather than a binary."),
159 init(false), cat(DsymCategory));
161 static opt<bool> Verify("verify", desc("Verify the linked DWARF debug info."),
162 cat(DsymCategory));
164 static opt<std::string>
165 Toolchain("toolchain", desc("Embed toolchain information in dSYM bundle."),
166 cat(DsymCategory));
168 static opt<bool>
169 PaperTrailWarnings("papertrail",
170 desc("Embed warnings in the linked DWARF debug info."),
171 cat(DsymCategory));
173 static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
174 if (NoOutput)
175 return Error::success();
177 // Create plist file to write to.
178 llvm::SmallString<128> InfoPlist(BundleRoot);
179 llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
180 std::error_code EC;
181 llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
182 if (EC)
183 return make_error<StringError>(
184 "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
186 CFBundleInfo BI = getBundleInfo(Bin);
188 if (BI.IDStr.empty()) {
189 llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
190 if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
191 BI.IDStr = llvm::sys::path::stem(BundleID);
192 else
193 BI.IDStr = BundleID;
196 // Print out information to the plist file.
197 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
198 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
199 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
200 << "<plist version=\"1.0\">\n"
201 << "\t<dict>\n"
202 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
203 << "\t\t<string>English</string>\n"
204 << "\t\t<key>CFBundleIdentifier</key>\n"
205 << "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n"
206 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
207 << "\t\t<string>6.0</string>\n"
208 << "\t\t<key>CFBundlePackageType</key>\n"
209 << "\t\t<string>dSYM</string>\n"
210 << "\t\t<key>CFBundleSignature</key>\n"
211 << "\t\t<string>\?\?\?\?</string>\n";
213 if (!BI.OmitShortVersion()) {
214 PL << "\t\t<key>CFBundleShortVersionString</key>\n";
215 PL << "\t\t<string>";
216 printHTMLEscaped(BI.ShortVersionStr, PL);
217 PL << "</string>\n";
220 PL << "\t\t<key>CFBundleVersion</key>\n";
221 PL << "\t\t<string>";
222 printHTMLEscaped(BI.VersionStr, PL);
223 PL << "</string>\n";
225 if (!Toolchain.empty()) {
226 PL << "\t\t<key>Toolchain</key>\n";
227 PL << "\t\t<string>";
228 printHTMLEscaped(Toolchain, PL);
229 PL << "</string>\n";
232 PL << "\t</dict>\n"
233 << "</plist>\n";
235 PL.close();
236 return Error::success();
239 static Error createBundleDir(llvm::StringRef BundleBase) {
240 if (NoOutput)
241 return Error::success();
243 llvm::SmallString<128> Bundle(BundleBase);
244 llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
245 if (std::error_code EC =
246 create_directories(Bundle.str(), true, llvm::sys::fs::perms::all_all))
247 return make_error<StringError>(
248 "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
250 return Error::success();
253 static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
254 if (OutputFile == "-") {
255 WithColor::warning() << "verification skipped for " << Arch
256 << "because writing to stdout.\n";
257 return true;
260 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
261 if (!BinOrErr) {
262 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
263 return false;
266 Binary &Binary = *BinOrErr.get().getBinary();
267 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
268 raw_ostream &os = Verbose ? errs() : nulls();
269 os << "Verifying DWARF for architecture: " << Arch << "\n";
270 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
271 DIDumpOptions DumpOpts;
272 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
273 if (!success)
274 WithColor::error() << "verification failed for " << Arch << '\n';
275 return success;
278 return false;
281 static Expected<std::string> getOutputFileName(llvm::StringRef InputFile) {
282 if (OutputFileOpt == "-")
283 return OutputFileOpt;
285 // When updating, do in place replacement.
286 if (OutputFileOpt.empty() && (Update || !SymbolMap.empty()))
287 return InputFile;
289 // If a flat dSYM has been requested, things are pretty simple.
290 if (FlatOut) {
291 if (OutputFileOpt.empty()) {
292 if (InputFile == "-")
293 return "a.out.dwarf";
294 return (InputFile + ".dwarf").str();
297 return OutputFileOpt;
300 // We need to create/update a dSYM bundle.
301 // A bundle hierarchy looks like this:
302 // <bundle name>.dSYM/
303 // Contents/
304 // Info.plist
305 // Resources/
306 // DWARF/
307 // <DWARF file(s)>
308 std::string DwarfFile =
309 InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
310 llvm::SmallString<128> BundleDir(OutputFileOpt);
311 if (BundleDir.empty())
312 BundleDir = DwarfFile + ".dSYM";
313 if (auto E = createBundleDir(BundleDir))
314 return std::move(E);
315 if (auto E = createPlistFile(DwarfFile, BundleDir))
316 return std::move(E);
318 llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
319 llvm::sys::path::filename(DwarfFile));
320 return BundleDir.str();
323 /// Parses the command line options into the LinkOptions struct and performs
324 /// some sanity checking. Returns an error in case the latter fails.
325 static Expected<LinkOptions> getOptions() {
326 LinkOptions Options;
328 Options.Verbose = Verbose;
329 Options.NoOutput = NoOutput;
330 Options.NoODR = NoODR;
331 Options.Minimize = Minimize;
332 Options.Update = Update;
333 Options.NoTimestamp = NoTimestamp;
334 Options.PrependPath = OsoPrependPath;
335 Options.TheAccelTableKind = AcceleratorTable;
337 if (!SymbolMap.empty())
338 Options.Update = true;
340 if (Assembly)
341 Options.FileType = OutputFileType::Assembly;
343 if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") !=
344 InputFiles.end()) {
345 // FIXME: We cannot use stdin for an update because stdin will be
346 // consumed by the BinaryHolder during the debugmap parsing, and
347 // then we will want to consume it again in DwarfLinker. If we
348 // used a unique BinaryHolder object that could cache multiple
349 // binaries this restriction would go away.
350 return make_error<StringError>(
351 "standard input cannot be used as input for a dSYM update.",
352 inconvertibleErrorCode());
355 if (NumThreads == 0)
356 Options.Threads = llvm::thread::hardware_concurrency();
357 if (DumpDebugMap || Verbose)
358 Options.Threads = 1;
360 return Options;
363 /// Return a list of input files. This function has logic for dealing with the
364 /// special case where we might have dSYM bundles as input. The function
365 /// returns an error when the directory structure doesn't match that of a dSYM
366 /// bundle.
367 static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) {
368 if (!DsymAsInput)
369 return InputFiles;
371 // If we are updating, we might get dSYM bundles as input.
372 std::vector<std::string> Inputs;
373 for (const auto &Input : InputFiles) {
374 if (!llvm::sys::fs::is_directory(Input)) {
375 Inputs.push_back(Input);
376 continue;
379 // Make sure that we're dealing with a dSYM bundle.
380 SmallString<256> BundlePath(Input);
381 sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
382 if (!llvm::sys::fs::is_directory(BundlePath))
383 return make_error<StringError>(
384 Input + " is a directory, but doesn't look like a dSYM bundle.",
385 inconvertibleErrorCode());
387 // Create a directory iterator to iterate over all the entries in the
388 // bundle.
389 std::error_code EC;
390 llvm::sys::fs::directory_iterator DirIt(BundlePath, EC);
391 llvm::sys::fs::directory_iterator DirEnd;
392 if (EC)
393 return errorCodeToError(EC);
395 // Add each entry to the list of inputs.
396 while (DirIt != DirEnd) {
397 Inputs.push_back(DirIt->path());
398 DirIt.increment(EC);
399 if (EC)
400 return errorCodeToError(EC);
403 return Inputs;
406 int main(int argc, char **argv) {
407 InitLLVM X(argc, argv);
409 void *P = (void *)(intptr_t)getOutputFileName;
410 std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P);
411 SDKPath = llvm::sys::path::parent_path(SDKPath);
413 HideUnrelatedOptions({&DsymCategory, &ColorCategory});
414 llvm::cl::ParseCommandLineOptions(
415 argc, argv,
416 "manipulate archived DWARF debug symbol files.\n\n"
417 "dsymutil links the DWARF debug information found in the object files\n"
418 "for the executable <input file> by using debug symbols information\n"
419 "contained in its symbol table.\n");
421 if (Help) {
422 PrintHelpMessage();
423 return 0;
426 if (Version) {
427 llvm::cl::PrintVersionMessage();
428 return 0;
431 auto OptionsOrErr = getOptions();
432 if (!OptionsOrErr) {
433 WithColor::error() << toString(OptionsOrErr.takeError());
434 return 1;
437 llvm::InitializeAllTargetInfos();
438 llvm::InitializeAllTargetMCs();
439 llvm::InitializeAllTargets();
440 llvm::InitializeAllAsmPrinters();
442 auto InputsOrErr = getInputs(OptionsOrErr->Update);
443 if (!InputsOrErr) {
444 WithColor::error() << toString(InputsOrErr.takeError()) << '\n';
445 return 1;
448 if (!FlatOut && OutputFileOpt == "-") {
449 WithColor::error() << "cannot emit to standard output without --flat\n";
450 return 1;
453 if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) {
454 WithColor::error() << "cannot use -o with multiple inputs in flat mode\n";
455 return 1;
458 if (InputFiles.size() > 1 && !SymbolMap.empty() &&
459 !llvm::sys::fs::is_directory(SymbolMap)) {
460 WithColor::error() << "when unobfuscating multiple files, --symbol-map "
461 << "needs to point to a directory.\n";
462 return 1;
465 if (getenv("RC_DEBUG_OPTIONS"))
466 PaperTrailWarnings = true;
468 if (PaperTrailWarnings && InputIsYAMLDebugMap)
469 WithColor::warning()
470 << "Paper trail warnings are not supported for YAML input";
472 for (const auto &Arch : ArchFlags)
473 if (Arch != "*" && Arch != "all" &&
474 !llvm::object::MachOObjectFile::isValidArch(Arch)) {
475 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
476 return 1;
479 SymbolMapLoader SymMapLoader(SymbolMap);
481 for (auto &InputFile : *InputsOrErr) {
482 // Dump the symbol table for each input file and requested arch
483 if (DumpStab) {
484 if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
485 return 1;
486 continue;
489 auto DebugMapPtrsOrErr =
490 parseDebugMap(InputFile, ArchFlags, OsoPrependPath, PaperTrailWarnings,
491 Verbose, InputIsYAMLDebugMap);
493 if (auto EC = DebugMapPtrsOrErr.getError()) {
494 WithColor::error() << "cannot parse the debug map for '" << InputFile
495 << "': " << EC.message() << '\n';
496 return 1;
499 if (OptionsOrErr->Update) {
500 // The debug map should be empty. Add one object file corresponding to
501 // the input file.
502 for (auto &Map : *DebugMapPtrsOrErr)
503 Map->addDebugMapObject(InputFile,
504 llvm::sys::TimePoint<std::chrono::seconds>());
507 // Ensure that the debug map is not empty (anymore).
508 if (DebugMapPtrsOrErr->empty()) {
509 WithColor::error() << "no architecture to link\n";
510 return 1;
513 // Shared a single binary holder for all the link steps.
514 BinaryHolder BinHolder;
516 NumThreads =
517 std::min<unsigned>(OptionsOrErr->Threads, DebugMapPtrsOrErr->size());
518 llvm::ThreadPool Threads(NumThreads);
520 // If there is more than one link to execute, we need to generate
521 // temporary files.
522 bool NeedsTempFiles =
523 !DumpDebugMap && (OutputFileOpt != "-") &&
524 (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update);
526 llvm::SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
527 std::atomic_char AllOK(1);
528 for (auto &Map : *DebugMapPtrsOrErr) {
529 if (Verbose || DumpDebugMap)
530 Map->print(llvm::outs());
532 if (DumpDebugMap)
533 continue;
535 if (!SymbolMap.empty())
536 OptionsOrErr->Translator = SymMapLoader.Load(InputFile, *Map);
538 if (Map->begin() == Map->end())
539 WithColor::warning()
540 << "no debug symbols in executable (-arch "
541 << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
543 // Using a std::shared_ptr rather than std::unique_ptr because move-only
544 // types don't work with std::bind in the ThreadPool implementation.
545 std::shared_ptr<raw_fd_ostream> OS;
547 Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
548 if (!OutputFileOrErr) {
549 WithColor::error() << toString(OutputFileOrErr.takeError());
550 return 1;
553 std::string OutputFile = *OutputFileOrErr;
554 if (NeedsTempFiles) {
555 TempFiles.emplace_back(Map->getTriple().getArchName().str());
557 auto E = TempFiles.back().createTempFile();
558 if (E) {
559 WithColor::error() << toString(std::move(E));
560 return 1;
563 auto &TempFile = *(TempFiles.back().File);
564 OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
565 /*shouldClose*/ false);
566 OutputFile = TempFile.TmpName;
567 } else {
568 std::error_code EC;
569 OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC,
570 sys::fs::F_None);
571 if (EC) {
572 WithColor::error() << OutputFile << ": " << EC.message();
573 return 1;
577 auto LinkLambda = [&,
578 OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
579 AllOK.fetch_and(linkDwarf(*Stream, BinHolder, *Map, *OptionsOrErr));
580 Stream->flush();
581 if (Verify && !NoOutput)
582 AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName()));
585 // FIXME: The DwarfLinker can have some very deep recursion that can max
586 // out the (significantly smaller) stack when using threads. We don't
587 // want this limitation when we only have a single thread.
588 if (NumThreads == 1)
589 LinkLambda(OS);
590 else
591 Threads.async(LinkLambda, OS);
594 Threads.wait();
596 if (!AllOK)
597 return 1;
599 if (NeedsTempFiles) {
600 Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile);
601 if (!OutputFileOrErr) {
602 WithColor::error() << toString(OutputFileOrErr.takeError());
603 return 1;
605 if (!MachOUtils::generateUniversalBinary(TempFiles, *OutputFileOrErr,
606 *OptionsOrErr, SDKPath))
607 return 1;
611 return 0;