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
44using namespace llvm;
45using namespace llvm::dwarfdump;
46using namespace llvm::object;
47
48namespace {
49/// Parser for options that take an optional offest argument.
50/// @{
51struct OffsetOption {
52 uint64_t Val = 0;
53 bool HasValue = false;
54 bool IsRequested = false;
55};
56struct BoolOption : public OffsetOption {};
57} // namespace
58
59namespace llvm {
60namespace cl {
61template <>
62class parser<OffsetOption> final : public basic_parser<OffsetOption> {
63public:
64 parser(Option &O) : basic_parser(O) {}
65
66 /// Return true on error.
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(Radix: 0, Result&: Val.Val))
75 return O.error(Message: "'" + Arg + "' value invalid for integer argument");
76 Val.HasValue = true;
77 Val.IsRequested = true;
78 return false;
79 }
80
81 enum ValueExpected getValueExpectedFlagDefault() const {
82 return ValueOptional;
83 }
84
85 StringRef getValueName() const override { return StringRef("offset"); }
86
87 void printOptionDiff(const Option &O, OffsetOption V, OptVal Default,
88 size_t GlobalWidth) const {
89 printOptionName(O, GlobalWidth);
90 outs() << "[=offset]";
91 }
92};
93
94template <> class parser<BoolOption> final : public basic_parser<BoolOption> {
95public:
96 parser(Option &O) : basic_parser(O) {}
97
98 /// Return true on error.
99 bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) {
100 if (Arg != "")
101 return O.error(Message: "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
108 enum ValueExpected getValueExpectedFlagDefault() const {
109 return ValueOptional;
110 }
111
112 StringRef getValueName() const override { return StringRef(); }
113
114 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
126namespace {
127using namespace cl;
128
129enum ErrorDetailLevel {
130 OnlyDetailsNoSummary,
131 NoDetailsOnlySummary,
132 NoDetailsOrSummary,
133 BothDetailsAndSummary,
134 Unspecified
135};
136
137OptionCategory DwarfDumpCategory("Specific Options");
138static list<std::string>
139 InputFilenames(Positional, desc("<input object files or .dSYM bundles>"),
140 cat(DwarfDumpCategory));
141
142cl::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
148static opt<bool> DumpAll("all", desc("Dump all debug info sections"),
149 cat(SectionCategory));
150static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll),
151 cl::NotHidden);
152
153// Options for dumping specific sections.
154static unsigned DumpType = DIDT_Null;
155static 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.
164static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"),
165 NotHidden, cat(SectionCategory),
166 aliasopt(DumpDebugFrame));
167static 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));
174static opt<bool>
175 Diff("diff",
176 desc("Emit diff-friendly output by omitting offsets and addresses."),
177 cat(DwarfDumpCategory));
178static 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));
185static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find),
186 cl::NotHidden);
187static opt<bool> FindAllApple(
188 "find-all-apple",
189 desc("Print every debug information entry in the accelerator tables."),
190 cat(DwarfDumpCategory));
191static opt<bool> IgnoreCase("ignore-case",
192 desc("Ignore case distinctions when using --name."),
193 value_desc("i"), cat(DwarfDumpCategory));
194static 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
201static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."),
202 aliasopt(IgnoreCase), cl::NotHidden);
203static 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));
209static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name),
210 cl::NotHidden);
211static 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));
216static opt<std::string>
217 OutputFilename("o", cl::init(Val: "-"),
218 cl::desc("Redirect output to the specified file."),
219 cl::value_desc("filename"), cat(DwarfDumpCategory));
220static alias OutputFilenameAlias("out-file", desc("Alias for -o."),
221 aliasopt(OutputFilename));
222static 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));
228static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex),
229 cl::NotHidden);
230static opt<bool>
231 ShowChildren("show-children",
232 desc("Show a debug info entry's children when selectively "
233 "printing entries."),
234 cat(DwarfDumpCategory));
235static alias ShowChildrenAlias("c", desc("Alias for --show-children."),
236 aliasopt(ShowChildren), cl::NotHidden);
237static opt<bool>
238 ShowParents("show-parents",
239 desc("Show a debug info entry's parents when selectively "
240 "printing entries."),
241 cat(DwarfDumpCategory));
242static alias ShowParentsAlias("p", desc("Alias for --show-parents."),
243 aliasopt(ShowParents), cl::NotHidden);
244static opt<bool>
245 ShowForm("show-form",
246 desc("Show DWARF form types after the DWARF attribute types."),
247 cat(DwarfDumpCategory));
248static alias ShowFormAlias("F", desc("Alias for --show-form."),
249 aliasopt(ShowForm), cat(DwarfDumpCategory),
250 cl::NotHidden);
251static 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(Val: -1U), value_desc("N"));
256static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."),
257 aliasopt(ChildRecurseDepth), cl::NotHidden);
258static 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(Val: -1U), value_desc("N"));
263static opt<bool>
264 SummarizeTypes("summarize-types",
265 desc("Abbreviate the description of type unit entries."),
266 cat(DwarfDumpCategory));
267static cl::opt<bool>
268 Statistics("statistics",
269 cl::desc("Emit JSON-formatted debug info quality metrics."),
270 cat(DwarfDumpCategory));
271static 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));
276static 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(Val: false), cl::Hidden, cl::cat(DwarfDumpCategory));
283static cl::opt<bool>
284 ShowSources("show-sources",
285 cl::desc("Show the sources across all compilation units."),
286 cat(DwarfDumpCategory));
287static opt<bool> Verify("verify", desc("Verify the DWARF debug info."),
288 cat(DwarfDumpCategory));
289static opt<unsigned> VerifyNumThreads(
290 "verify-num-threads", init(Val: 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));
297static opt<ErrorDetailLevel> ErrorDetails(
298 "error-display", init(Val: 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));
310static opt<std::string> JsonErrSummaryFile(
311 "verify-json", init(Val: ""),
312 desc("Output JSON-formatted error summary to the specified file. "
313 "(Implies --verify)"),
314 value_desc("filename.json"), cat(DwarfDumpCategory));
315static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),
316 cat(DwarfDumpCategory));
317static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),
318 cat(DwarfDumpCategory));
319static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID),
320 cl::NotHidden);
321static opt<bool> Verbose("verbose",
322 desc("Print more low-level encoding details."),
323 cat(DwarfDumpCategory));
324static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose),
325 cat(DwarfDumpCategory), cl::NotHidden);
326static cl::extrahelp
327 HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
328} // namespace
329/// @}
330//===----------------------------------------------------------------------===//
331
332static void error(Error Err) {
333 if (!Err)
334 return;
335 WithColor::error() << toString(E: std::move(Err)) << "\n";
336 exit(status: 1);
337}
338
339static void error(StringRef Prefix, Error Err) {
340 if (!Err)
341 return;
342 WithColor::error() << Prefix << ": " << toString(E: std::move(Err)) << "\n";
343 exit(status: 1);
344}
345
346static void error(StringRef Prefix, std::error_code EC) {
347 error(Prefix, Err: errorCodeToError(EC));
348}
349
350static 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
375static 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.
383static bool filterArch(ObjectFile &Obj) {
384 if (ArchFilters.empty())
385 return true;
386
387 if (auto *MachO = dyn_cast<MachOObjectFile>(Val: &Obj)) {
388 for (const StringRef Arch : ArchFilters) {
389 // Match architecture number.
390 unsigned Value;
391 if (!Arch.getAsInteger(Radix: 0, Result&: Value))
392 if (Value == getCPUType(MachO&: *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
403using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx,
404 const Twine &, raw_ostream &)>;
405
406/// Print only DIEs that have a certain name.
407static 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(C&: 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(status: 1);
422 }
423 if (RE.match(String: Name)) {
424 Die.dump(OS, indent: 0, DumpOpts);
425 return true;
426 }
427 }
428 } else if (Names.count(Key: Name)) {
429 // Match full text.
430 Die.dump(OS, indent: 0, DumpOpts);
431 return true;
432 }
433 return false;
434}
435
436/// Print only DIEs that have a certain name.
437static 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(Kind: DINameKind::ShortName))
445 if (filterByName(Names, Die, NameRef: Name, OS, GetNameForDWARFReg))
446 continue;
447 if (const char *Name = Die.getName(Kind: DINameKind::LinkageName))
448 filterByName(Names, Die, NameRef: 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(ExtractUnitDIEOnly: false);
456 DWARFDie CUNonSkeletonDie = CU->getNonSkeletonUnitDIE(ExtractUnitDIEOnly: 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
464static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel,
465 StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
466 for (const auto &Entry : Accel.equal_range(Key: Name)) {
467 if (std::optional<uint64_t> Off = Entry.getDIESectionOffset()) {
468 if (DWARFDie Die = DICtx.getDIEForOffset(Offset: *Off))
469 Dies.push_back(Elt: Die);
470 }
471 }
472}
473
474static 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(Offset: *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(Hash: *DWOId);
488 if (!CU)
489 return DWARFDie();
490 }
491
492 return CU->getDIEForOffset(Offset: CU->getOffset() + *Off);
493}
494
495static void getDies(DWARFContext &DICtx, const DWARFDebugNames &Accel,
496 StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
497 for (const auto &Entry : Accel.equal_range(Key: Name)) {
498 if (DWARFDie Die = toDie(Entry, DICtx))
499 Dies.push_back(Elt: Die);
500 }
501}
502
503/// Print only DIEs that have a certain name.
504static 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, Accel: DICtx.getAppleNames(), Name, Dies);
510 getDies(DICtx, Accel: DICtx.getAppleTypes(), Name, Dies);
511 getDies(DICtx, Accel: DICtx.getAppleNamespaces(), Name, Dies);
512 getDies(DICtx, Accel: DICtx.getDebugNames(), Name, Dies);
513 }
514 llvm::sort(C&: Dies);
515 Dies.erase(CS: llvm::unique(R&: Dies), CE: Dies.end());
516
517 DIDumpOptions DumpOpts = getDumpOpts(C&: DICtx);
518 DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;
519 for (DWARFDie Die : Dies)
520 Die.dump(OS, indent: 0, DumpOpts);
521}
522
523/// Print all DIEs in apple accelerator tables
524static 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(Offset: *Off);
534 if (Die && MaybeName)
535 NameToDies[*MaybeName].insert(V: Die);
536 }
537 }
538 };
539
540 PushDIEs(DICtx.getAppleNames());
541 PushDIEs(DICtx.getAppleNamespaces());
542 PushDIEs(DICtx.getAppleTypes());
543
544 DIDumpOptions DumpOpts = getDumpOpts(C&: DICtx);
545 DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg;
546 for (const auto &[Name, Dies] : NameToDies) {
547 OS << llvm::formatv(Fmt: "\nApple accelerator entries with name = \"{0}\":\n",
548 Vals: Name);
549 for (DWARFDie Die : Dies)
550 Die.dump(OS, indent: 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...
560static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address,
561 raw_ostream &OS) {
562 auto DIEsForAddr = DICtx.getDIEsForAddress(Address: Lookup, CheckDWO: DumpNonSkeleton);
563
564 if (!DIEsForAddr)
565 return false;
566
567 DIDumpOptions DumpOpts = getDumpOpts(C&: DICtx);
568 DumpOpts.ChildRecurseDepth = 0;
569 DIEsForAddr.CompileUnit->dump(OS, DumpOpts);
570 if (DIEsForAddr.FunctionDIE) {
571 DIEsForAddr.FunctionDIE.dump(OS, indent: 2, DumpOpts);
572 if (DIEsForAddr.BlockDIE)
573 DIEsForAddr.BlockDIE.dump(OS, indent: 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 Address: {.Address: Lookup, .SectionIndex: object::SectionedAddress::UndefSection})
582 .value_or(u: 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.
591static 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(FileIndex: 0) ? 0 : 1,
597 E = LastIndex ? *LastIndex + 1 : 0;
598 I < E; ++I) {
599 std::string Path;
600 Result &= LT.getFileNameByIndex(
601 FileIndex: I, CompDir, Kind: DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
602 Result&: Path);
603 Sources.push_back(x: std::move(Path));
604 }
605 return Result;
606}
607
608static 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(U: CU.get());
620 StringRef CompDir = CU->getCompilationDir();
621 if (LT) {
622 Result &= collectLineTableSources(LT: *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(path: Name, style: sys::path::Style::posix) &&
634 sys::path::is_relative(path: Name, style: sys::path::Style::windows))
635 AbsName = CompDir;
636 sys::path::append(path&: AbsName, a: Name);
637 Sources.push_back(x: 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(Err: 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(C&: Sources);
663 Sources.erase(first: llvm::unique(R&: Sources), last: Sources.end());
664
665 for (StringRef Name : Sources)
666 OS << Name << "\n";
667 return Result;
668}
669
670static std::unique_ptr<MCRegisterInfo>
671createRegInfo(const object::ObjectFile &Obj) {
672 std::unique_ptr<MCRegisterInfo> MCRegInfo;
673 Triple TT;
674 TT.setArch(Kind: Triple::ArchType(Obj.getArch()));
675 TT.setVendor(Triple::UnknownVendor);
676 TT.setOS(Triple::UnknownOS);
677 std::string TargetLookupError;
678 const Target *TheTarget = TargetRegistry::lookupTarget(TheTriple: TT, Error&: TargetLookupError);
679 if (!TargetLookupError.empty())
680 return nullptr;
681 MCRegInfo.reset(p: TheTarget->createMCRegInfo(TT: TT.str()));
682 return MCRegInfo;
683}
684
685static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
686 const Twine &Filename, raw_ostream &OS) {
687
688 auto MCRegInfo = createRegInfo(Obj);
689 if (!MCRegInfo)
690 logAllUnhandledErrors(E: createStringError(EC: inconvertibleErrorCode(),
691 S: "Error in creating MCRegInfo"),
692 OS&: errs(), ErrorBanner: 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(RegNum: DwarfRegNum, isEH: IsEH))
699 if (const char *RegName = MCRegInfo->getName(RegNo: *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, Address: Lookup, OS);
711
712 // Handle the --name option.
713 if (!Name.empty()) {
714 StringSet<> Names;
715 for (const auto &name : Name)
716 Names.insert(key: (IgnoreCase && !UseRegex) ? StringRef(name).lower() : name);
717
718 filterByName(Names, CUs: DICtx.normal_units(), OS, GetNameForDWARFReg: GetRegName);
719 filterByName(Names, CUs: DICtx.dwo_units(), OS, GetNameForDWARFReg: GetRegName);
720 return true;
721 }
722
723 // Handle the --find option and lower it to --debug-info=<offset>.
724 if (!Find.empty()) {
725 filterByAccelName(Names: Find, DICtx, OS, GetNameForDWARFReg: 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, GetNameForDWARFReg: GetRegName);
732 return true;
733 }
734
735 // Dump the complete DWARF structure.
736 auto DumpOpts = getDumpOpts(C&: DICtx);
737 DumpOpts.GetNameForDWARFReg = GetRegName;
738 DICtx.dump(OS, DumpOpts, DumpOffsets);
739 return true;
740}
741
742static 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(OS&: stream, DumpOpts: getDumpOpts(C&: DICtx));
750 if (Result)
751 stream << "No errors.\n";
752 else
753 stream << "Errors detected.\n";
754 return Result;
755}
756
757static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
758 HandlerFn HandleObj, raw_ostream &OS);
759
760static 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(Prefix: Filename, Err: BuffOrErr.takeError());
767 auto NameOrErr = Child.getName();
768 error(Prefix: Filename, Err: NameOrErr.takeError());
769 std::string Name = (Filename + "(" + NameOrErr.get() + ")").str();
770 Result &= handleBuffer(Filename: Name, Buffer: BuffOrErr.get(), HandleObj, OS);
771 }
772 error(Prefix: Filename, Err: std::move(Err));
773
774 return Result;
775}
776
777static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer,
778 HandlerFn HandleObj, raw_ostream &OS) {
779 Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Source: Buffer);
780 error(Prefix: Filename, Err: BinOrErr.takeError());
781
782 bool Result = true;
783 auto RecoverableErrorHandler = [&](Error E) {
784 Result = false;
785 WithColor::defaultErrorHandler(Err: std::move(E));
786 };
787 if (auto *Obj = dyn_cast<ObjectFile>(Val: BinOrErr->get())) {
788 if (filterArch(Obj&: *Obj)) {
789 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(
790 Obj: *Obj, RelocAction: DWARFContext::ProcessDebugRelocations::Process, L: nullptr, DWPName: "",
791 RecoverableErrorHandler, WarningHandler: 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>(Val: 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, RelocAction: DWARFContext::ProcessDebugRelocations::Process, L: nullptr, DWPName: "",
806 RecoverableErrorHandler);
807 if (!HandleObj(Obj, *DICtx, ObjName, OS))
808 Result = false;
809 }
810 continue;
811 } else
812 consumeError(Err: MachOOrErr.takeError());
813 if (auto ArchiveOrErr = ObjForArch.getAsArchive()) {
814 error(Prefix: ObjName, Err: ArchiveOrErr.takeError());
815 if (!handleArchive(Filename: ObjName, Arch&: *ArchiveOrErr.get(), HandleObj, OS))
816 Result = false;
817 continue;
818 } else
819 consumeError(Err: ArchiveOrErr.takeError());
820 }
821 else if (auto *Arch = dyn_cast<Archive>(Val: BinOrErr->get()))
822 Result = handleArchive(Filename, Arch&: *Arch, HandleObj, OS);
823 return Result;
824}
825
826static bool handleFile(StringRef Filename, HandlerFn HandleObj,
827 raw_ostream &OS) {
828 ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
829 MemoryBuffer::getFileOrSTDIN(Filename);
830 error(Prefix: Filename, EC: BuffOrErr.getError());
831 std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get());
832 return handleBuffer(Filename, Buffer: *Buffer, HandleObj, OS);
833}
834
835int 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(TieTo: &outs());
841
842 llvm::InitializeAllTargetInfos();
843 llvm::InitializeAllTargetMCs();
844
845 HideUnrelatedOptions(
846 Categories: {&DwarfDumpCategory, &SectionCategory, &getColorCategory()});
847 cl::ParseCommandLineOptions(
848 argc, argv,
849 Overview: "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(Prefix: "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(value: "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(Path: F)) {
909 if (DsymObjectsOrErr->empty())
910 Objects.push_back(x: F);
911 else
912 llvm::append_range(C&: Objects, R&: *DsymObjectsOrErr);
913 } else {
914 error(Err: DsymObjectsOrErr.takeError());
915 }
916 }
917
918 bool Success = true;
919 if (Verify) {
920 if (!VerifyNumThreads)
921 parallel::strategy =
922 hardware_concurrency(ThreadCount: hardware_concurrency().compute_thread_count());
923 else
924 parallel::strategy = hardware_concurrency(ThreadCount: VerifyNumThreads);
925 for (StringRef Object : Objects)
926 Success &= handleFile(Filename: Object, HandleObj: verifyObjectFile, OS&: OutputFile.os());
927 } else if (Statistics) {
928 for (StringRef Object : Objects)
929 Success &= handleFile(Filename: Object, HandleObj: collectStatsForObjectFile, OS&: OutputFile.os());
930 } else if (ShowSectionSizes) {
931 for (StringRef Object : Objects)
932 Success &= handleFile(Filename: Object, HandleObj: collectObjectSectionSizes, OS&: OutputFile.os());
933 } else if (ShowSources) {
934 for (StringRef Object : Objects)
935 Success &= handleFile(Filename: Object, HandleObj: collectObjectSources, OS&: OutputFile.os());
936 } else {
937 for (StringRef Object : Objects)
938 Success &= handleFile(Filename: Object, HandleObj: dumpObjectFile, OS&: OutputFile.os());
939 }
940
941 return Success ? EXIT_SUCCESS : EXIT_FAILURE;
942}
943