xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- llvm-dwarfdump.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 works like "dwarfdump".
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm-dwarfdump.h"
14 #include "llvm/ADT/MapVector.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/SmallSet.h"
17 #include "llvm/ADT/StringSet.h"
18 #include "llvm/DebugInfo/DIContext.h"
19 #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
20 #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
21 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
22 #include "llvm/MC/MCRegisterInfo.h"
23 #include "llvm/MC/TargetRegistry.h"
24 #include "llvm/Object/Archive.h"
25 #include "llvm/Object/MachOUniversal.h"
26 #include "llvm/Object/ObjectFile.h"
27 #include "llvm/Support/CommandLine.h"
28 #include "llvm/Support/Debug.h"
29 #include "llvm/Support/Format.h"
30 #include "llvm/Support/FormatVariadic.h"
31 #include "llvm/Support/InitLLVM.h"
32 #include "llvm/Support/MemoryBuffer.h"
33 #include "llvm/Support/Parallel.h"
34 #include "llvm/Support/Path.h"
35 #include "llvm/Support/Regex.h"
36 #include "llvm/Support/TargetSelect.h"
37 #include "llvm/Support/Threading.h"
38 #include "llvm/Support/ToolOutputFile.h"
39 #include "llvm/Support/WithColor.h"
40 #include "llvm/Support/raw_ostream.h"
41 #include "llvm/TargetParser/Triple.h"
42 #include <cstdlib>
43 
44 using namespace llvm;
45 using namespace llvm::dwarfdump;
46 using namespace llvm::object;
47 
48 namespace {
49 /// Parser for options that take an optional offest argument.
50 /// @{
51 struct OffsetOption {
52   uint64_t Val = 0;
53   bool HasValue = false;
54   bool IsRequested = false;
55 };
56 struct BoolOption : public OffsetOption {};
57 } // namespace
58 
59 namespace llvm {
60 namespace cl {
61 template <>
62 class parser<OffsetOption> final : public basic_parser<OffsetOption> {
63 public:
parser(Option & O)64   parser(Option &O) : basic_parser(O) {}
65 
66   /// Return true on error.
parse(Option & O,StringRef ArgName,StringRef Arg,OffsetOption & Val)67   bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) {
68     if (Arg == "") {
69       Val.Val = 0;
70       Val.HasValue = false;
71       Val.IsRequested = true;
72       return false;
73     }
74     if (Arg.getAsInteger(0, Val.Val))
75       return O.error("'" + Arg + "' value invalid for integer argument");
76     Val.HasValue = true;
77     Val.IsRequested = true;
78     return false;
79   }
80 
getValueExpectedFlagDefault() const81   enum ValueExpected getValueExpectedFlagDefault() const {
82     return ValueOptional;
83   }
84 
getValueName() const85   StringRef getValueName() const override { return StringRef("offset"); }
86 
printOptionDiff(const Option & O,OffsetOption V,OptVal Default,size_t GlobalWidth) const87   void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
88                        size_t GlobalWidth) const {
89     printOptionName(O, GlobalWidth);
90     outs() << "[=offset]";
91   }
92 };
93 
94 template <> class parser<BoolOption> final : public basic_parser<BoolOption> {
95 public:
parser(Option & O)96   parser(Option &O) : basic_parser(O) {}
97 
98   /// Return true on error.
parse(Option & O,StringRef ArgName,StringRef Arg,BoolOption & Val)99   bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) {
100     if (Arg != "")
101       return O.error("this is a flag and does not take a value");
102     Val.Val = 0;
103     Val.HasValue = false;
104     Val.IsRequested = true;
105     return false;
106   }
107 
getValueExpectedFlagDefault() const108   enum ValueExpected getValueExpectedFlagDefault() const {
109     return ValueOptional;
110   }
111 
getValueName() const112   StringRef getValueName() const override { return StringRef(); }
113 
printOptionDiff(const Option & O,OffsetOption V,OptVal Default,size_t GlobalWidth) const114   void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
115                        size_t GlobalWidth) const {
116     printOptionName(O, GlobalWidth);
117   }
118 };
119 } // namespace cl
120 } // namespace llvm
121 
122 /// @}
123 /// Command line options.
124 /// @{
125 
126 namespace {
127 using namespace cl;
128 
129 enum ErrorDetailLevel {
130   OnlyDetailsNoSummary,
131   NoDetailsOnlySummary,
132   NoDetailsOrSummary,
133   BothDetailsAndSummary,
134   Unspecified
135 };
136 
137 OptionCategory DwarfDumpCategory("Specific Options");
138 static list<std::string>
139     InputFilenames(Positional, desc("<input object files or .dSYM bundles>"),
140                    cat(DwarfDumpCategory));
141 
142 cl::OptionCategory SectionCategory("Section-specific Dump Options",
143                                    "These control which sections are dumped. "
144                                    "Where applicable these parameters take an "
145                                    "optional =<offset> argument to dump only "
146                                    "the entry at the specified offset.");
147 
148 static opt<bool> DumpAll("all", desc("Dump all debug info sections"),
149                          cat(SectionCategory));
150 static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll),
151                           cl::NotHidden);
152 
153 // Options for dumping specific sections.
154 static unsigned DumpType = DIDT_Null;
155 static std::array<std::optional<uint64_t>, (unsigned)DIDT_ID_Count> DumpOffsets;
156 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION)        \
157   static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME,                             \
158                                      desc("Dump the " ELF_NAME " section"),    \
159                                      cat(SectionCategory));
160 #include "llvm/BinaryFormat/Dwarf.def"
161 #undef HANDLE_DWARF_SECTION
162 
163 // The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above.
164 static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"),
165                                  NotHidden, cat(SectionCategory),
166                                  aliasopt(DumpDebugFrame));
167 static list<std::string>
168     ArchFilters("arch",
169                 desc("Dump debug information for the specified CPU "
170                      "architecture only. Architectures may be specified by "
171                      "name or by number. This option can be specified "
172                      "multiple times, once for each desired architecture."),
173                 cat(DwarfDumpCategory));
174 static opt<bool>
175     Diff("diff",
176          desc("Emit diff-friendly output by omitting offsets and addresses."),
177          cat(DwarfDumpCategory));
178 static list<std::string>
179     Find("find",
180          desc("Search for the exact match for <name> in the accelerator tables "
181               "and print the matching debug information entries. When no "
182               "accelerator tables are available, the slower but more complete "
183               "-name option can be used instead."),
184          value_desc("name"), cat(DwarfDumpCategory));
185 static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find),
186                        cl::NotHidden);
187 static opt<bool> FindAllApple(
188     "find-all-apple",
189     desc("Print every debug information entry in the accelerator tables."),
190     cat(DwarfDumpCategory));
191 static opt<bool> IgnoreCase("ignore-case",
192                             desc("Ignore case distinctions when using --name."),
193                             value_desc("i"), cat(DwarfDumpCategory));
194 static opt<bool> DumpNonSkeleton(
195     "dwo",
196     desc("Dump the non skeleton DIE in the .dwo or .dwp file after dumping the "
197          "skeleton DIE from the main executable. This allows dumping the .dwo "
198          "files with resolved addresses."),
199     value_desc("d"), cat(DwarfDumpCategory));
200 
201 static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."),
202                              aliasopt(IgnoreCase), cl::NotHidden);
203 static list<std::string> Name(
204     "name",
205     desc("Find and print all debug info entries whose name (DW_AT_name "
206          "attribute) matches the exact text in <pattern>.  When used with the "
207          "the -regex option <pattern> is interpreted as a regular expression."),
208     value_desc("pattern"), cat(DwarfDumpCategory));
209 static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name),
210                        cl::NotHidden);
211 static opt<uint64_t>
212     Lookup("lookup",
213            desc("Lookup <address> in the debug information and print out any "
214                 "available file, function, block and line table details."),
215            value_desc("address"), cat(DwarfDumpCategory));
216 static opt<std::string>
217     OutputFilename("o", cl::init("-"),
218                    cl::desc("Redirect output to the specified file."),
219                    cl::value_desc("filename"), cat(DwarfDumpCategory));
220 static alias OutputFilenameAlias("out-file", desc("Alias for -o."),
221                                  aliasopt(OutputFilename));
222 static opt<bool> UseRegex(
223     "regex",
224     desc("Treat any <pattern> strings as regular "
225          "expressions when searching with --name. If --ignore-case is also "
226          "specified, the regular expression becomes case-insensitive."),
227     cat(DwarfDumpCategory));
228 static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex),
229                         cl::NotHidden);
230 static opt<bool>
231     ShowChildren("show-children",
232                  desc("Show a debug info entry's children when selectively "
233                       "printing entries."),
234                  cat(DwarfDumpCategory));
235 static alias ShowChildrenAlias("c", desc("Alias for --show-children."),
236                                aliasopt(ShowChildren), cl::NotHidden);
237 static opt<bool>
238     ShowParents("show-parents",
239                 desc("Show a debug info entry's parents when selectively "
240                      "printing entries."),
241                 cat(DwarfDumpCategory));
242 static alias ShowParentsAlias("p", desc("Alias for --show-parents."),
243                               aliasopt(ShowParents), cl::NotHidden);
244 static opt<bool>
245     ShowForm("show-form",
246              desc("Show DWARF form types after the DWARF attribute types."),
247              cat(DwarfDumpCategory));
248 static alias ShowFormAlias("F", desc("Alias for --show-form."),
249                            aliasopt(ShowForm), cat(DwarfDumpCategory),
250                            cl::NotHidden);
251 static opt<unsigned>
252     ChildRecurseDepth("recurse-depth",
253                       desc("Only recurse to a depth of N when displaying "
254                            "children of debug info entries."),
255                       cat(DwarfDumpCategory), init(-1U), value_desc("N"));
256 static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."),
257                                     aliasopt(ChildRecurseDepth), cl::NotHidden);
258 static opt<unsigned>
259     ParentRecurseDepth("parent-recurse-depth",
260                        desc("Only recurse to a depth of N when displaying "
261                             "parents of debug info entries."),
262                        cat(DwarfDumpCategory), init(-1U), value_desc("N"));
263 static opt<bool>
264     SummarizeTypes("summarize-types",
265                    desc("Abbreviate the description of type unit entries."),
266                    cat(DwarfDumpCategory));
267 static cl::opt<bool>
268     Statistics("statistics",
269                cl::desc("Emit JSON-formatted debug info quality metrics."),
270                cat(DwarfDumpCategory));
271 static cl::opt<bool>
272     ShowSectionSizes("show-section-sizes",
273                      cl::desc("Show the sizes of all debug sections, "
274                               "expressed in bytes."),
275                      cat(DwarfDumpCategory));
276 static cl::opt<bool> ManuallyGenerateUnitIndex(
277     "manually-generate-unit-index",
278     cl::desc("if the input is dwp file, parse .debug_info "
279              "section and use it to populate "
280              "DW_SECT_INFO contributions in cu-index. "
281              "For DWARF5 it also populated TU Index."),
282     cl::init(false), cl::Hidden, cl::cat(DwarfDumpCategory));
283 static cl::opt<bool>
284     ShowSources("show-sources",
285                 cl::desc("Show the sources across all compilation units."),
286                 cat(DwarfDumpCategory));
287 static opt<bool> Verify("verify", desc("Verify the DWARF debug info."),
288                         cat(DwarfDumpCategory));
289 static opt<unsigned> VerifyNumThreads(
290     "verify-num-threads", init(1),
291     desc("Number of threads to use for --verify. Single threaded verification "
292          "is the default unless this option is specified. If 0 is specified, "
293          "maximum hardware threads will be used. This can cause the "
294          "output to be non determinisitic, but can speed up verification and "
295          "is useful when running with the summary only or JSON summary modes."),
296     cat(DwarfDumpCategory));
297 static opt<ErrorDetailLevel> ErrorDetails(
298     "error-display", init(Unspecified),
299     desc("Set the level of detail and summary to display when verifying "
300          "(implies --verify)"),
301     values(clEnumValN(NoDetailsOrSummary, "quiet",
302                       "Only display whether errors occurred."),
303            clEnumValN(NoDetailsOnlySummary, "summary",
304                       "Display only a summary of the errors found."),
305            clEnumValN(OnlyDetailsNoSummary, "details",
306                       "Display each error in detail but no summary."),
307            clEnumValN(BothDetailsAndSummary, "full",
308                       "Display each error as well as a summary. [default]")),
309     cat(DwarfDumpCategory));
310 static opt<std::string> JsonErrSummaryFile(
311     "verify-json", init(""),
312     desc("Output JSON-formatted error summary to the specified file. "
313          "(Implies --verify)"),
314     value_desc("filename.json"), cat(DwarfDumpCategory));
315 static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),
316                        cat(DwarfDumpCategory));
317 static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),
318                           cat(DwarfDumpCategory));
319 static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID),
320                            cl::NotHidden);
321 static opt<bool> Verbose("verbose",
322                          desc("Print more low-level encoding details."),
323                          cat(DwarfDumpCategory));
324 static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose),
325                           cat(DwarfDumpCategory), cl::NotHidden);
326 static cl::extrahelp
327     HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
328 } // namespace
329 /// @}
330 //===----------------------------------------------------------------------===//
331 
error(Error Err)332 static void error(Error Err) {
333   if (!Err)
334     return;
335   WithColor::error() << toString(std::move(Err)) << "\n";
336   exit(1);
337 }
338 
error(StringRef Prefix,Error Err)339 static void error(StringRef Prefix, Error Err) {
340   if (!Err)
341     return;
342   WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n";
343   exit(1);
344 }
345 
error(StringRef Prefix,std::error_code EC)346 static void error(StringRef Prefix, std::error_code EC) {
347   error(Prefix, errorCodeToError(EC));
348 }
349 
getDumpOpts(DWARFContext & C)350 static DIDumpOptions getDumpOpts(DWARFContext &C) {
351   DIDumpOptions DumpOpts;
352   DumpOpts.DumpType = DumpType;
353   DumpOpts.ChildRecurseDepth = ChildRecurseDepth;
354   DumpOpts.ParentRecurseDepth = ParentRecurseDepth;
355   DumpOpts.ShowAddresses = !Diff;
356   DumpOpts.ShowChildren = ShowChildren;
357   DumpOpts.ShowParents = ShowParents;
358   DumpOpts.ShowForm = ShowForm;
359   DumpOpts.SummarizeTypes = SummarizeTypes;
360   DumpOpts.Verbose = Verbose;
361   DumpOpts.DumpNonSkeleton = DumpNonSkeleton;
362   DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler();
363   // In -verify mode, print DIEs without children in error messages.
364   if (Verify) {
365     DumpOpts.Verbose = ErrorDetails != NoDetailsOnlySummary &&
366                        ErrorDetails != NoDetailsOrSummary;
367     DumpOpts.ShowAggregateErrors = ErrorDetails != OnlyDetailsNoSummary &&
368                                    ErrorDetails != NoDetailsOnlySummary;
369     DumpOpts.JsonErrSummaryFile = JsonErrSummaryFile;
370     return DumpOpts.noImplicitRecursion();
371   }
372   return DumpOpts;
373 }
374 
getCPUType(MachOObjectFile & MachO)375 static uint32_t getCPUType(MachOObjectFile &MachO) {
376   if (MachO.is64Bit())
377     return MachO.getHeader64().cputype;
378   else
379     return MachO.getHeader().cputype;
380 }
381 
382 /// Return true if the object file has not been filtered by an --arch option.
filterArch(ObjectFile & Obj)383 static bool filterArch(ObjectFile &Obj) {
384   if (ArchFilters.empty())
385     return true;
386 
387   if (auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) {
388     for (const StringRef Arch : ArchFilters) {
389       // Match architecture number.
390       unsigned Value;
391       if (!Arch.getAsInteger(0, Value))
392         if (Value == getCPUType(*MachO))
393           return true;
394 
395       // Match as name.
396       if (MachO->getArchTriple().getArchName() == Triple(Arch).getArchName())
397         return true;
398     }
399   }
400   return false;
401 }
402 
403 using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx,
404                                      const Twine &, raw_ostream &)>;
405 
406 /// Print only DIEs that have a certain name.
filterByName(const StringSet<> & Names,DWARFDie Die,StringRef NameRef,raw_ostream & OS,std::function<StringRef (uint64_t RegNum,bool IsEH)> GetNameForDWARFReg)407 static bool filterByName(
408     const StringSet<> &Names, DWARFDie Die, StringRef NameRef, raw_ostream &OS,
409     std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {
410   DIDumpOptions DumpOpts = getDumpOpts(Die.getDwarfUnit()->getContext());
411   DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;
412   std::string Name =
413       (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str();
414   if (UseRegex) {
415     // Match regular expression.
416     for (auto Pattern : Names.keys()) {
417       Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags);
418       std::string Error;
419       if (!RE.isValid(Error)) {
420         errs() << "error in regular expression: " << Error << "\n";
421         exit(1);
422       }
423       if (RE.match(Name)) {
424         Die.dump(OS, 0, DumpOpts);
425         return true;
426       }
427     }
428   } else if (Names.count(Name)) {
429     // Match full text.
430     Die.dump(OS, 0, DumpOpts);
431     return true;
432   }
433   return false;
434 }
435 
436 /// Print only DIEs that have a certain name.
filterByName(const StringSet<> & Names,DWARFContext::unit_iterator_range CUs,raw_ostream & OS,std::function<StringRef (uint64_t RegNum,bool IsEH)> GetNameForDWARFReg)437 static void filterByName(
438     const StringSet<> &Names, DWARFContext::unit_iterator_range CUs,
439     raw_ostream &OS,
440     std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {
441   auto filterDieNames = [&](DWARFUnit *Unit) {
442     for (const auto &Entry : Unit->dies()) {
443       DWARFDie Die = {Unit, &Entry};
444       if (const char *Name = Die.getName(DINameKind::ShortName))
445         if (filterByName(Names, Die, Name, OS, GetNameForDWARFReg))
446           continue;
447       if (const char *Name = Die.getName(DINameKind::LinkageName))
448         filterByName(Names, Die, Name, OS, GetNameForDWARFReg);
449     }
450   };
451   for (const auto &CU : CUs) {
452     filterDieNames(CU.get());
453     if (DumpNonSkeleton) {
454       // If we have split DWARF, then recurse down into the .dwo files as well.
455       DWARFDie CUDie = CU->getUnitDIE(false);
456       DWARFDie CUNonSkeletonDie = CU->getNonSkeletonUnitDIE(false);
457       // If we have a DWO file, we need to search it as well
458       if (CUNonSkeletonDie && CUDie != CUNonSkeletonDie)
459         filterDieNames(CUNonSkeletonDie.getDwarfUnit());
460     }
461   }
462 }
463 
getDies(DWARFContext & DICtx,const AppleAcceleratorTable & Accel,StringRef Name,SmallVectorImpl<DWARFDie> & Dies)464 static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel,
465                     StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
466   for (const auto &Entry : Accel.equal_range(Name)) {
467     if (std::optional<uint64_t> Off = Entry.getDIESectionOffset()) {
468       if (DWARFDie Die = DICtx.getDIEForOffset(*Off))
469         Dies.push_back(Die);
470     }
471   }
472 }
473 
toDie(const DWARFDebugNames::Entry & Entry,DWARFContext & DICtx)474 static DWARFDie toDie(const DWARFDebugNames::Entry &Entry,
475                       DWARFContext &DICtx) {
476   std::optional<uint64_t> CUOff = Entry.getCUOffset();
477   std::optional<uint64_t> Off = Entry.getDIEUnitOffset();
478   if (!CUOff || !Off)
479     return DWARFDie();
480 
481   DWARFCompileUnit *CU = DICtx.getCompileUnitForOffset(*CUOff);
482   if (!CU)
483     return DWARFDie();
484 
485   if (std::optional<uint64_t> DWOId = CU->getDWOId()) {
486     // This is a skeleton unit. Look up the DIE in the DWO unit.
487     CU = DICtx.getDWOCompileUnitForHash(*DWOId);
488     if (!CU)
489       return DWARFDie();
490   }
491 
492   return CU->getDIEForOffset(CU->getOffset() + *Off);
493 }
494 
getDies(DWARFContext & DICtx,const DWARFDebugNames & Accel,StringRef Name,SmallVectorImpl<DWARFDie> & Dies)495 static void getDies(DWARFContext &DICtx, const DWARFDebugNames &Accel,
496                     StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
497   for (const auto &Entry : Accel.equal_range(Name)) {
498     if (DWARFDie Die = toDie(Entry, DICtx))
499       Dies.push_back(Die);
500   }
501 }
502 
503 /// Print only DIEs that have a certain name.
filterByAccelName(ArrayRef<std::string> Names,DWARFContext & DICtx,raw_ostream & OS,std::function<StringRef (uint64_t RegNum,bool IsEH)> GetNameForDWARFReg)504 static void filterByAccelName(
505     ArrayRef<std::string> Names, DWARFContext &DICtx, raw_ostream &OS,
506     std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {
507   SmallVector<DWARFDie, 4> Dies;
508   for (const auto &Name : Names) {
509     getDies(DICtx, DICtx.getAppleNames(), Name, Dies);
510     getDies(DICtx, DICtx.getAppleTypes(), Name, Dies);
511     getDies(DICtx, DICtx.getAppleNamespaces(), Name, Dies);
512     getDies(DICtx, DICtx.getDebugNames(), Name, Dies);
513   }
514   llvm::sort(Dies);
515   Dies.erase(llvm::unique(Dies), Dies.end());
516 
517   DIDumpOptions DumpOpts = getDumpOpts(DICtx);
518   DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;
519   for (DWARFDie Die : Dies)
520     Die.dump(OS, 0, DumpOpts);
521 }
522 
523 /// Print all DIEs in apple accelerator tables
findAllApple(DWARFContext & DICtx,raw_ostream & OS,std::function<StringRef (uint64_t RegNum,bool IsEH)> GetNameForDWARFReg)524 static void findAllApple(
525     DWARFContext &DICtx, raw_ostream &OS,
526     std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) {
527   MapVector<StringRef, llvm::SmallSet<DWARFDie, 2>> NameToDies;
528 
529   auto PushDIEs = [&](const AppleAcceleratorTable &Accel) {
530     for (const auto &Entry : Accel.entries()) {
531       if (std::optional<uint64_t> Off = Entry.BaseEntry.getDIESectionOffset()) {
532         std::optional<StringRef> MaybeName = Entry.readName();
533         DWARFDie Die = DICtx.getDIEForOffset(*Off);
534         if (Die && MaybeName)
535           NameToDies[*MaybeName].insert(Die);
536       }
537     }
538   };
539 
540   PushDIEs(DICtx.getAppleNames());
541   PushDIEs(DICtx.getAppleNamespaces());
542   PushDIEs(DICtx.getAppleTypes());
543 
544   DIDumpOptions DumpOpts = getDumpOpts(DICtx);
545   DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;
546   for (const auto &[Name, Dies] : NameToDies) {
547     OS << llvm::formatv("\nApple accelerator entries with name = \"{0}\":\n",
548                         Name);
549     for (DWARFDie Die : Dies)
550       Die.dump(OS, 0, DumpOpts);
551   }
552 }
553 
554 /// Handle the --lookup option and dump the DIEs and line info for the given
555 /// address.
556 /// TODO: specified Address for --lookup option could relate for several
557 /// different sections(in case not-linked object file). llvm-dwarfdump
558 /// need to do something with this: extend lookup option with section
559 /// information or probably display all matched entries, or something else...
lookup(ObjectFile & Obj,DWARFContext & DICtx,uint64_t Address,raw_ostream & OS)560 static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address,
561                    raw_ostream &OS) {
562   auto DIEsForAddr = DICtx.getDIEsForAddress(Lookup, DumpNonSkeleton);
563 
564   if (!DIEsForAddr)
565     return false;
566 
567   DIDumpOptions DumpOpts = getDumpOpts(DICtx);
568   DumpOpts.ChildRecurseDepth = 0;
569   DIEsForAddr.CompileUnit->dump(OS, DumpOpts);
570   if (DIEsForAddr.FunctionDIE) {
571     DIEsForAddr.FunctionDIE.dump(OS, 2, DumpOpts);
572     if (DIEsForAddr.BlockDIE)
573       DIEsForAddr.BlockDIE.dump(OS, 4, DumpOpts);
574   }
575 
576   // TODO: it is neccessary to set proper SectionIndex here.
577   // object::SectionedAddress::UndefSection works for only absolute addresses.
578   if (DILineInfo LineInfo =
579           DICtx
580               .getLineInfoForAddress(
581                   {Lookup, object::SectionedAddress::UndefSection})
582               .value_or(DILineInfo())) {
583     LineInfo.dump(OS);
584   }
585 
586   return true;
587 }
588 
589 // Collect all sources referenced from the given line table, scoped to the given
590 // CU compilation directory.
collectLineTableSources(const DWARFDebugLine::LineTable & LT,StringRef CompDir,std::vector<std::string> & Sources)591 static bool collectLineTableSources(const DWARFDebugLine::LineTable &LT,
592                                     StringRef CompDir,
593                                     std::vector<std::string> &Sources) {
594   bool Result = true;
595   std::optional<uint64_t> LastIndex = LT.getLastValidFileIndex();
596   for (uint64_t I = LT.hasFileAtIndex(0) ? 0 : 1,
597                 E = LastIndex ? *LastIndex + 1 : 0;
598        I < E; ++I) {
599     std::string Path;
600     Result &= LT.getFileNameByIndex(
601         I, CompDir, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
602         Path);
603     Sources.push_back(std::move(Path));
604   }
605   return Result;
606 }
607 
collectObjectSources(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)608 static bool collectObjectSources(ObjectFile &Obj, DWARFContext &DICtx,
609                                  const Twine &Filename, raw_ostream &OS) {
610   bool Result = true;
611   std::vector<std::string> Sources;
612 
613   bool HasCompileUnits = false;
614   for (const auto &CU : DICtx.compile_units()) {
615     HasCompileUnits = true;
616     // Extract paths from the line table for this CU. This allows combining the
617     // compilation directory with the line information, in case both the include
618     // directory and file names in the line table are relative.
619     const DWARFDebugLine::LineTable *LT = DICtx.getLineTableForUnit(CU.get());
620     StringRef CompDir = CU->getCompilationDir();
621     if (LT) {
622       Result &= collectLineTableSources(*LT, CompDir, Sources);
623     } else {
624       // Since there's no line table for this CU, collect the name from the CU
625       // itself.
626       const char *Name = CU->getUnitDIE().getShortName();
627       if (!Name) {
628         WithColor::warning()
629             << Filename << ": missing name for compilation unit\n";
630         continue;
631       }
632       SmallString<64> AbsName;
633       if (sys::path::is_relative(Name, sys::path::Style::posix) &&
634           sys::path::is_relative(Name, sys::path::Style::windows))
635         AbsName = CompDir;
636       sys::path::append(AbsName, Name);
637       Sources.push_back(std::string(AbsName));
638     }
639   }
640 
641   if (!HasCompileUnits) {
642     // Since there's no compile units available, walk the line tables and
643     // extract out any referenced paths.
644     DWARFDataExtractor LineData(DICtx.getDWARFObj(),
645                                 DICtx.getDWARFObj().getLineSection(),
646                                 DICtx.isLittleEndian(), 0);
647     DWARFDebugLine::SectionParser Parser(LineData, DICtx, DICtx.normal_units());
648     while (!Parser.done()) {
649       const auto RecoverableErrorHandler = [&](Error Err) {
650         Result = false;
651         WithColor::defaultErrorHandler(std::move(Err));
652       };
653       void (*UnrecoverableErrorHandler)(Error Err) = error;
654 
655       DWARFDebugLine::LineTable LT =
656           Parser.parseNext(RecoverableErrorHandler, UnrecoverableErrorHandler);
657       Result &= collectLineTableSources(LT, /*CompDir=*/"", Sources);
658     }
659   }
660 
661   // Dedup and order the sources.
662   llvm::sort(Sources);
663   Sources.erase(llvm::unique(Sources), Sources.end());
664 
665   for (StringRef Name : Sources)
666     OS << Name << "\n";
667   return Result;
668 }
669 
670 static std::unique_ptr<MCRegisterInfo>
createRegInfo(const object::ObjectFile & Obj)671 createRegInfo(const object::ObjectFile &Obj) {
672   std::unique_ptr<MCRegisterInfo> MCRegInfo;
673   Triple TT;
674   TT.setArch(Triple::ArchType(Obj.getArch()));
675   TT.setVendor(Triple::UnknownVendor);
676   TT.setOS(Triple::UnknownOS);
677   std::string TargetLookupError;
678   const Target *TheTarget = TargetRegistry::lookupTarget(TT, TargetLookupError);
679   if (!TargetLookupError.empty())
680     return nullptr;
681   MCRegInfo.reset(TheTarget->createMCRegInfo(TT.str()));
682   return MCRegInfo;
683 }
684 
dumpObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)685 static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
686                            const Twine &Filename, raw_ostream &OS) {
687 
688   auto MCRegInfo = createRegInfo(Obj);
689   if (!MCRegInfo)
690     logAllUnhandledErrors(createStringError(inconvertibleErrorCode(),
691                                             "Error in creating MCRegInfo"),
692                           errs(), Filename.str() + ": ");
693 
694   auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> StringRef {
695     if (!MCRegInfo)
696       return {};
697     if (std::optional<MCRegister> LLVMRegNum =
698             MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH))
699       if (const char *RegName = MCRegInfo->getName(*LLVMRegNum))
700         return StringRef(RegName);
701     return {};
702   };
703 
704   // The UUID dump already contains all the same information.
705   if (!(DumpType & DIDT_UUID) || DumpType == DIDT_All)
706     OS << Filename << ":\tfile format " << Obj.getFileFormatName() << '\n';
707 
708   // Handle the --lookup option.
709   if (Lookup)
710     return lookup(Obj, DICtx, Lookup, OS);
711 
712   // Handle the --name option.
713   if (!Name.empty()) {
714     StringSet<> Names;
715     for (const auto &name : Name)
716       Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name);
717 
718     filterByName(Names, DICtx.normal_units(), OS, GetRegName);
719     filterByName(Names, DICtx.dwo_units(), OS, GetRegName);
720     return true;
721   }
722 
723   // Handle the --find option and lower it to --debug-info=<offset>.
724   if (!Find.empty()) {
725     filterByAccelName(Find, DICtx, OS, GetRegName);
726     return true;
727   }
728 
729   // Handle the --find-all-apple option and lower it to --debug-info=<offset>.
730   if (FindAllApple) {
731     findAllApple(DICtx, OS, GetRegName);
732     return true;
733   }
734 
735   // Dump the complete DWARF structure.
736   auto DumpOpts = getDumpOpts(DICtx);
737   DumpOpts.GetNameForDWARFReg = GetRegName;
738   DICtx.dump(OS, DumpOpts, DumpOffsets);
739   return true;
740 }
741 
verifyObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)742 static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
743                              const Twine &Filename, raw_ostream &OS) {
744   // Verify the DWARF and exit with non-zero exit status if verification
745   // fails.
746   raw_ostream &stream = Quiet ? nulls() : OS;
747   stream << "Verifying " << Filename.str() << ":\tfile format "
748          << Obj.getFileFormatName() << "\n";
749   bool Result = DICtx.verify(stream, getDumpOpts(DICtx));
750   if (Result)
751     stream << "No errors.\n";
752   else
753     stream << "Errors detected.\n";
754   return Result;
755 }
756 
757 static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
758                          HandlerFn HandleObj, raw_ostream &OS);
759 
handleArchive(StringRef Filename,Archive & Arch,HandlerFn HandleObj,raw_ostream & OS)760 static bool handleArchive(StringRef Filename, Archive &Arch,
761                           HandlerFn HandleObj, raw_ostream &OS) {
762   bool Result = true;
763   Error Err = Error::success();
764   for (const auto &Child : Arch.children(Err)) {
765     auto BuffOrErr = Child.getMemoryBufferRef();
766     error(Filename, BuffOrErr.takeError());
767     auto NameOrErr = Child.getName();
768     error(Filename, NameOrErr.takeError());
769     std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
770     Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS);
771   }
772   error(Filename, std::move(Err));
773 
774   return Result;
775 }
776 
handleBuffer(StringRef Filename,MemoryBufferRef Buffer,HandlerFn HandleObj,raw_ostream & OS)777 static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
778                          HandlerFn HandleObj, raw_ostream &OS) {
779   Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer);
780   error(Filename, BinOrErr.takeError());
781 
782   bool Result = true;
783   auto RecoverableErrorHandler = [&](Error E) {
784     Result = false;
785     WithColor::defaultErrorHandler(std::move(E));
786   };
787   if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) {
788     if (filterArch(*Obj)) {
789       std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
790           *Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",
791           RecoverableErrorHandler, WithColor::defaultWarningHandler,
792           /*ThreadSafe=*/true);
793       DICtx->setParseCUTUIndexManually(ManuallyGenerateUnitIndex);
794       if (!HandleObj(*Obj, *DICtx, Filename, OS))
795         Result = false;
796     }
797   } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get()))
798     for (auto &ObjForArch : Fat->objects()) {
799       std::string ObjName =
800           (Filename + "(" + ObjForArch.getArchFlagName() + ")").str();
801       if (auto MachOOrErr = ObjForArch.getAsObjectFile()) {
802         auto &Obj = **MachOOrErr;
803         if (filterArch(Obj)) {
804           std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
805               Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",
806               RecoverableErrorHandler);
807           if (!HandleObj(Obj, *DICtx, ObjName, OS))
808             Result = false;
809         }
810         continue;
811       } else
812         consumeError(MachOOrErr.takeError());
813       if (auto ArchiveOrErr = ObjForArch.getAsArchive()) {
814         error(ObjName, ArchiveOrErr.takeError());
815         if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS))
816           Result = false;
817         continue;
818       } else
819         consumeError(ArchiveOrErr.takeError());
820     }
821   else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get()))
822     Result = handleArchive(Filename, *Arch, HandleObj, OS);
823   return Result;
824 }
825 
handleFile(StringRef Filename,HandlerFn HandleObj,raw_ostream & OS)826 static bool handleFile(StringRef Filename, HandlerFn HandleObj,
827                        raw_ostream &OS) {
828   ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
829       MemoryBuffer::getFileOrSTDIN(Filename);
830   error(Filename, BuffOrErr.getError());
831   std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
832   return handleBuffer(Filename, *Buffer, HandleObj, OS);
833 }
834 
main(int argc,char ** argv)835 int main(int argc, char **argv) {
836   InitLLVM X(argc, argv);
837 
838   // Flush outs() when printing to errs(). This avoids interleaving output
839   // between the two.
840   errs().tie(&outs());
841 
842   llvm::InitializeAllTargetInfos();
843   llvm::InitializeAllTargetMCs();
844 
845   HideUnrelatedOptions(
846       {&DwarfDumpCategory, &SectionCategory, &getColorCategory()});
847   cl::ParseCommandLineOptions(
848       argc, argv,
849       "pretty-print DWARF debug information in object files"
850       " and debug info archives.\n");
851 
852   // FIXME: Audit interactions between these two options and make them
853   //        compatible.
854   if (Diff && Verbose) {
855     WithColor::error() << "incompatible arguments: specifying both -diff and "
856                           "-verbose is currently not supported";
857     return 1;
858   }
859   // -error-detail and -json-summary-file both imply -verify
860   if (ErrorDetails != Unspecified || !JsonErrSummaryFile.empty()) {
861     Verify = true;
862   }
863 
864   std::error_code EC;
865   ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF);
866   error("unable to open output file " + OutputFilename, EC);
867   // Don't remove output file if we exit with an error.
868   OutputFile.keep();
869 
870   bool OffsetRequested = false;
871 
872   // Defaults to dumping only debug_info, unless: A) verbose mode is specified,
873   // in which case all sections are dumped, or B) a specific section is
874   // requested.
875 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION)        \
876   if (Dump##ENUM_NAME.IsRequested) {                                           \
877     DumpType |= DIDT_##ENUM_NAME;                                              \
878     if (Dump##ENUM_NAME.HasValue) {                                            \
879       DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val;                  \
880       OffsetRequested = true;                                                  \
881     }                                                                          \
882   }
883 #include "llvm/BinaryFormat/Dwarf.def"
884 #undef HANDLE_DWARF_SECTION
885   if (DumpUUID)
886     DumpType |= DIDT_UUID;
887   if (DumpAll)
888     DumpType = DIDT_All;
889   if (DumpType == DIDT_Null) {
890     if (Verbose || Verify)
891       DumpType = DIDT_All;
892     else
893       DumpType = DIDT_DebugInfo;
894   }
895 
896   // Unless dumping a specific DIE, default to --show-children.
897   if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() &&
898       Find.empty() && !FindAllApple)
899     ShowChildren = true;
900 
901   // Defaults to a.out if no filenames specified.
902   if (InputFilenames.empty())
903     InputFilenames.push_back("a.out");
904 
905   // Expand any .dSYM bundles to the individual object files contained therein.
906   std::vector<std::string> Objects;
907   for (const auto &F : InputFilenames) {
908     if (auto DsymObjectsOrErr = MachOObjectFile::findDsymObjectMembers(F)) {
909       if (DsymObjectsOrErr->empty())
910         Objects.push_back(F);
911       else
912         llvm::append_range(Objects, *DsymObjectsOrErr);
913     } else {
914       error(DsymObjectsOrErr.takeError());
915     }
916   }
917 
918   bool Success = true;
919   if (Verify) {
920     if (!VerifyNumThreads)
921       parallel::strategy =
922           hardware_concurrency(hardware_concurrency().compute_thread_count());
923     else
924       parallel::strategy = hardware_concurrency(VerifyNumThreads);
925     for (StringRef Object : Objects)
926       Success &= handleFile(Object, verifyObjectFile, OutputFile.os());
927   } else if (Statistics) {
928     for (StringRef Object : Objects)
929       Success &= handleFile(Object, collectStatsForObjectFile, OutputFile.os());
930   } else if (ShowSectionSizes) {
931     for (StringRef Object : Objects)
932       Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os());
933   } else if (ShowSources) {
934     for (StringRef Object : Objects)
935       Success &= handleFile(Object, collectObjectSources, OutputFile.os());
936   } else {
937     for (StringRef Object : Objects)
938       Success &= handleFile(Object, dumpObjectFile, OutputFile.os());
939   }
940 
941   return Success ? EXIT_SUCCESS : EXIT_FAILURE;
942 }
943