1 //===-- llvm-dwarfdump.cpp - Debug info dumping utility for llvm ----------===//
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
7 //===----------------------------------------------------------------------===//
9 // This program is a utility that works like "dwarfdump".
11 //===----------------------------------------------------------------------===//
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/Path.h"
34 #include "llvm/Support/Regex.h"
35 #include "llvm/Support/TargetSelect.h"
36 #include "llvm/Support/ToolOutputFile.h"
37 #include "llvm/Support/WithColor.h"
38 #include "llvm/Support/raw_ostream.h"
39 #include "llvm/TargetParser/Triple.h"
43 using namespace llvm::dwarfdump
;
44 using namespace llvm::object
;
47 /// Parser for options that take an optional offest argument.
51 bool HasValue
= false;
52 bool IsRequested
= false;
54 struct BoolOption
: public OffsetOption
{};
60 class parser
<OffsetOption
> final
: public basic_parser
<OffsetOption
> {
62 parser(Option
&O
) : basic_parser(O
) {}
64 /// Return true on error.
65 bool parse(Option
&O
, StringRef ArgName
, StringRef Arg
, OffsetOption
&Val
) {
69 Val
.IsRequested
= true;
72 if (Arg
.getAsInteger(0, Val
.Val
))
73 return O
.error("'" + Arg
+ "' value invalid for integer argument");
75 Val
.IsRequested
= true;
79 enum ValueExpected
getValueExpectedFlagDefault() const {
83 StringRef
getValueName() const override
{ return StringRef("offset"); }
85 void printOptionDiff(const Option
&O
, OffsetOption V
, OptVal Default
,
86 size_t GlobalWidth
) const {
87 printOptionName(O
, GlobalWidth
);
88 outs() << "[=offset]";
92 template <> class parser
<BoolOption
> final
: public basic_parser
<BoolOption
> {
94 parser(Option
&O
) : basic_parser(O
) {}
96 /// Return true on error.
97 bool parse(Option
&O
, StringRef ArgName
, StringRef Arg
, BoolOption
&Val
) {
99 return O
.error("this is a flag and does not take a value");
101 Val
.HasValue
= false;
102 Val
.IsRequested
= true;
106 enum ValueExpected
getValueExpectedFlagDefault() const {
107 return ValueOptional
;
110 StringRef
getValueName() const override
{ return StringRef(); }
112 void printOptionDiff(const Option
&O
, OffsetOption V
, OptVal Default
,
113 size_t GlobalWidth
) const {
114 printOptionName(O
, GlobalWidth
);
121 /// Command line options.
127 enum ErrorDetailLevel
{
128 OnlyDetailsNoSummary
,
129 NoDetailsOnlySummary
,
131 BothDetailsAndSummary
,
135 OptionCategory
DwarfDumpCategory("Specific Options");
136 static list
<std::string
>
137 InputFilenames(Positional
, desc("<input object files or .dSYM bundles>"),
138 cat(DwarfDumpCategory
));
140 cl::OptionCategory
SectionCategory("Section-specific Dump Options",
141 "These control which sections are dumped. "
142 "Where applicable these parameters take an "
143 "optional =<offset> argument to dump only "
144 "the entry at the specified offset.");
146 static opt
<bool> DumpAll("all", desc("Dump all debug info sections"),
147 cat(SectionCategory
));
148 static alias
DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll
),
151 // Options for dumping specific sections.
152 static unsigned DumpType
= DIDT_Null
;
153 static std::array
<std::optional
<uint64_t>, (unsigned)DIDT_ID_Count
> DumpOffsets
;
154 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
155 static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \
156 desc("Dump the " ELF_NAME " section"), \
157 cat(SectionCategory));
158 #include "llvm/BinaryFormat/Dwarf.def"
159 #undef HANDLE_DWARF_SECTION
161 // The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above.
162 static alias
DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"),
163 NotHidden
, cat(SectionCategory
),
164 aliasopt(DumpDebugFrame
));
165 static list
<std::string
>
167 desc("Dump debug information for the specified CPU "
168 "architecture only. Architectures may be specified by "
169 "name or by number. This option can be specified "
170 "multiple times, once for each desired architecture."),
171 cat(DwarfDumpCategory
));
174 desc("Emit diff-friendly output by omitting offsets and addresses."),
175 cat(DwarfDumpCategory
));
176 static list
<std::string
>
178 desc("Search for the exact match for <name> in the accelerator tables "
179 "and print the matching debug information entries. When no "
180 "accelerator tables are available, the slower but more complete "
181 "-name option can be used instead."),
182 value_desc("name"), cat(DwarfDumpCategory
));
183 static alias
FindAlias("f", desc("Alias for --find."), aliasopt(Find
),
185 static opt
<bool> FindAllApple(
187 desc("Print every debug information entry in the accelerator tables."),
188 cat(DwarfDumpCategory
));
189 static opt
<bool> IgnoreCase("ignore-case",
190 desc("Ignore case distinctions when using --name."),
191 value_desc("i"), cat(DwarfDumpCategory
));
192 static opt
<bool> DumpNonSkeleton(
194 desc("Dump the non skeleton DIE in the .dwo or .dwp file after dumping the "
195 "skeleton DIE from the main executable. This allows dumping the .dwo "
196 "files with resolved addresses."),
197 value_desc("d"), cat(DwarfDumpCategory
));
199 static alias
IgnoreCaseAlias("i", desc("Alias for --ignore-case."),
200 aliasopt(IgnoreCase
), cl::NotHidden
);
201 static list
<std::string
> Name(
203 desc("Find and print all debug info entries whose name (DW_AT_name "
204 "attribute) matches the exact text in <pattern>. When used with the "
205 "the -regex option <pattern> is interpreted as a regular expression."),
206 value_desc("pattern"), cat(DwarfDumpCategory
));
207 static alias
NameAlias("n", desc("Alias for --name"), aliasopt(Name
),
211 desc("Lookup <address> in the debug information and print out any "
212 "available file, function, block and line table details."),
213 value_desc("address"), cat(DwarfDumpCategory
));
214 static opt
<std::string
>
215 OutputFilename("o", cl::init("-"),
216 cl::desc("Redirect output to the specified file."),
217 cl::value_desc("filename"), cat(DwarfDumpCategory
));
218 static alias
OutputFilenameAlias("out-file", desc("Alias for -o."),
219 aliasopt(OutputFilename
));
220 static opt
<bool> UseRegex(
222 desc("Treat any <pattern> strings as regular "
223 "expressions when searching with --name. If --ignore-case is also "
224 "specified, the regular expression becomes case-insensitive."),
225 cat(DwarfDumpCategory
));
226 static alias
RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex
),
229 ShowChildren("show-children",
230 desc("Show a debug info entry's children when selectively "
231 "printing entries."),
232 cat(DwarfDumpCategory
));
233 static alias
ShowChildrenAlias("c", desc("Alias for --show-children."),
234 aliasopt(ShowChildren
), cl::NotHidden
);
236 ShowParents("show-parents",
237 desc("Show a debug info entry's parents when selectively "
238 "printing entries."),
239 cat(DwarfDumpCategory
));
240 static alias
ShowParentsAlias("p", desc("Alias for --show-parents."),
241 aliasopt(ShowParents
), cl::NotHidden
);
243 ShowForm("show-form",
244 desc("Show DWARF form types after the DWARF attribute types."),
245 cat(DwarfDumpCategory
));
246 static alias
ShowFormAlias("F", desc("Alias for --show-form."),
247 aliasopt(ShowForm
), cat(DwarfDumpCategory
),
250 ChildRecurseDepth("recurse-depth",
251 desc("Only recurse to a depth of N when displaying "
252 "children of debug info entries."),
253 cat(DwarfDumpCategory
), init(-1U), value_desc("N"));
254 static alias
ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."),
255 aliasopt(ChildRecurseDepth
), cl::NotHidden
);
257 ParentRecurseDepth("parent-recurse-depth",
258 desc("Only recurse to a depth of N when displaying "
259 "parents of debug info entries."),
260 cat(DwarfDumpCategory
), init(-1U), value_desc("N"));
262 SummarizeTypes("summarize-types",
263 desc("Abbreviate the description of type unit entries."),
264 cat(DwarfDumpCategory
));
266 Statistics("statistics",
267 cl::desc("Emit JSON-formatted debug info quality metrics."),
268 cat(DwarfDumpCategory
));
270 ShowSectionSizes("show-section-sizes",
271 cl::desc("Show the sizes of all debug sections, "
272 "expressed in bytes."),
273 cat(DwarfDumpCategory
));
274 static cl::opt
<bool> ManuallyGenerateUnitIndex(
275 "manually-generate-unit-index",
276 cl::desc("if the input is dwp file, parse .debug_info "
277 "section and use it to populate "
278 "DW_SECT_INFO contributions in cu-index. "
279 "For DWARF5 it also populated TU Index."),
280 cl::init(false), cl::Hidden
, cl::cat(DwarfDumpCategory
));
282 ShowSources("show-sources",
283 cl::desc("Show the sources across all compilation units."),
284 cat(DwarfDumpCategory
));
285 static opt
<bool> Verify("verify", desc("Verify the DWARF debug info."),
286 cat(DwarfDumpCategory
));
287 static opt
<ErrorDetailLevel
> ErrorDetails(
288 "error-display", init(Unspecified
),
289 desc("Set the level of detail and summary to display when verifying "
290 "(implies --verify)"),
291 values(clEnumValN(NoDetailsOrSummary
, "quiet",
292 "Only display whether errors occurred."),
293 clEnumValN(NoDetailsOnlySummary
, "summary",
294 "Display only a summary of the errors found."),
295 clEnumValN(OnlyDetailsNoSummary
, "details",
296 "Display each error in detail but no summary."),
297 clEnumValN(BothDetailsAndSummary
, "full",
298 "Display each error as well as a summary. [default]")),
299 cat(DwarfDumpCategory
));
300 static opt
<std::string
> JsonErrSummaryFile(
301 "verify-json", init(""),
302 desc("Output JSON-formatted error summary to the specified file. "
303 "(Implies --verify)"),
304 value_desc("filename.json"), cat(DwarfDumpCategory
));
305 static opt
<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."),
306 cat(DwarfDumpCategory
));
307 static opt
<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."),
308 cat(DwarfDumpCategory
));
309 static alias
DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID
),
311 static opt
<bool> Verbose("verbose",
312 desc("Print more low-level encoding details."),
313 cat(DwarfDumpCategory
));
314 static alias
VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose
),
315 cat(DwarfDumpCategory
), cl::NotHidden
);
317 HelpResponse("\nPass @FILE as argument to read options from FILE.\n");
320 //===----------------------------------------------------------------------===//
322 static void error(Error Err
) {
325 WithColor::error() << toString(std::move(Err
)) << "\n";
329 static void error(StringRef Prefix
, Error Err
) {
332 WithColor::error() << Prefix
<< ": " << toString(std::move(Err
)) << "\n";
336 static void error(StringRef Prefix
, std::error_code EC
) {
337 error(Prefix
, errorCodeToError(EC
));
340 static DIDumpOptions
getDumpOpts(DWARFContext
&C
) {
341 DIDumpOptions DumpOpts
;
342 DumpOpts
.DumpType
= DumpType
;
343 DumpOpts
.ChildRecurseDepth
= ChildRecurseDepth
;
344 DumpOpts
.ParentRecurseDepth
= ParentRecurseDepth
;
345 DumpOpts
.ShowAddresses
= !Diff
;
346 DumpOpts
.ShowChildren
= ShowChildren
;
347 DumpOpts
.ShowParents
= ShowParents
;
348 DumpOpts
.ShowForm
= ShowForm
;
349 DumpOpts
.SummarizeTypes
= SummarizeTypes
;
350 DumpOpts
.Verbose
= Verbose
;
351 DumpOpts
.DumpNonSkeleton
= DumpNonSkeleton
;
352 DumpOpts
.RecoverableErrorHandler
= C
.getRecoverableErrorHandler();
353 // In -verify mode, print DIEs without children in error messages.
355 DumpOpts
.Verbose
= ErrorDetails
!= NoDetailsOnlySummary
&&
356 ErrorDetails
!= NoDetailsOrSummary
;
357 DumpOpts
.ShowAggregateErrors
= ErrorDetails
!= OnlyDetailsNoSummary
&&
358 ErrorDetails
!= NoDetailsOnlySummary
;
359 DumpOpts
.JsonErrSummaryFile
= JsonErrSummaryFile
;
360 return DumpOpts
.noImplicitRecursion();
365 static uint32_t getCPUType(MachOObjectFile
&MachO
) {
367 return MachO
.getHeader64().cputype
;
369 return MachO
.getHeader().cputype
;
372 /// Return true if the object file has not been filtered by an --arch option.
373 static bool filterArch(ObjectFile
&Obj
) {
374 if (ArchFilters
.empty())
377 if (auto *MachO
= dyn_cast
<MachOObjectFile
>(&Obj
)) {
378 for (const StringRef Arch
: ArchFilters
) {
379 // Match architecture number.
381 if (!Arch
.getAsInteger(0, Value
))
382 if (Value
== getCPUType(*MachO
))
386 if (MachO
->getArchTriple().getArchName() == Triple(Arch
).getArchName())
393 using HandlerFn
= std::function
<bool(ObjectFile
&, DWARFContext
&DICtx
,
394 const Twine
&, raw_ostream
&)>;
396 /// Print only DIEs that have a certain name.
397 static bool filterByName(
398 const StringSet
<> &Names
, DWARFDie Die
, StringRef NameRef
, raw_ostream
&OS
,
399 std::function
<StringRef(uint64_t RegNum
, bool IsEH
)> GetNameForDWARFReg
) {
400 DIDumpOptions DumpOpts
= getDumpOpts(Die
.getDwarfUnit()->getContext());
401 DumpOpts
.GetNameForDWARFReg
= GetNameForDWARFReg
;
403 (IgnoreCase
&& !UseRegex
) ? NameRef
.lower() : NameRef
.str();
405 // Match regular expression.
406 for (auto Pattern
: Names
.keys()) {
407 Regex
RE(Pattern
, IgnoreCase
? Regex::IgnoreCase
: Regex::NoFlags
);
409 if (!RE
.isValid(Error
)) {
410 errs() << "error in regular expression: " << Error
<< "\n";
413 if (RE
.match(Name
)) {
414 Die
.dump(OS
, 0, DumpOpts
);
418 } else if (Names
.count(Name
)) {
420 Die
.dump(OS
, 0, DumpOpts
);
426 /// Print only DIEs that have a certain name.
427 static void filterByName(
428 const StringSet
<> &Names
, DWARFContext::unit_iterator_range CUs
,
430 std::function
<StringRef(uint64_t RegNum
, bool IsEH
)> GetNameForDWARFReg
) {
431 auto filterDieNames
= [&](DWARFUnit
*Unit
) {
432 for (const auto &Entry
: Unit
->dies()) {
433 DWARFDie Die
= {Unit
, &Entry
};
434 if (const char *Name
= Die
.getName(DINameKind::ShortName
))
435 if (filterByName(Names
, Die
, Name
, OS
, GetNameForDWARFReg
))
437 if (const char *Name
= Die
.getName(DINameKind::LinkageName
))
438 filterByName(Names
, Die
, Name
, OS
, GetNameForDWARFReg
);
441 for (const auto &CU
: CUs
) {
442 filterDieNames(CU
.get());
443 if (DumpNonSkeleton
) {
444 // If we have split DWARF, then recurse down into the .dwo files as well.
445 DWARFDie CUDie
= CU
->getUnitDIE(false);
446 DWARFDie CUNonSkeletonDie
= CU
->getNonSkeletonUnitDIE(false);
447 // If we have a DWO file, we need to search it as well
448 if (CUNonSkeletonDie
&& CUDie
!= CUNonSkeletonDie
)
449 filterDieNames(CUNonSkeletonDie
.getDwarfUnit());
454 static void getDies(DWARFContext
&DICtx
, const AppleAcceleratorTable
&Accel
,
455 StringRef Name
, SmallVectorImpl
<DWARFDie
> &Dies
) {
456 for (const auto &Entry
: Accel
.equal_range(Name
)) {
457 if (std::optional
<uint64_t> Off
= Entry
.getDIESectionOffset()) {
458 if (DWARFDie Die
= DICtx
.getDIEForOffset(*Off
))
464 static DWARFDie
toDie(const DWARFDebugNames::Entry
&Entry
,
465 DWARFContext
&DICtx
) {
466 std::optional
<uint64_t> CUOff
= Entry
.getCUOffset();
467 std::optional
<uint64_t> Off
= Entry
.getDIEUnitOffset();
471 DWARFCompileUnit
*CU
= DICtx
.getCompileUnitForOffset(*CUOff
);
475 if (std::optional
<uint64_t> DWOId
= CU
->getDWOId()) {
476 // This is a skeleton unit. Look up the DIE in the DWO unit.
477 CU
= DICtx
.getDWOCompileUnitForHash(*DWOId
);
482 return CU
->getDIEForOffset(CU
->getOffset() + *Off
);
485 static void getDies(DWARFContext
&DICtx
, const DWARFDebugNames
&Accel
,
486 StringRef Name
, SmallVectorImpl
<DWARFDie
> &Dies
) {
487 for (const auto &Entry
: Accel
.equal_range(Name
)) {
488 if (DWARFDie Die
= toDie(Entry
, DICtx
))
493 /// Print only DIEs that have a certain name.
494 static void filterByAccelName(
495 ArrayRef
<std::string
> Names
, DWARFContext
&DICtx
, raw_ostream
&OS
,
496 std::function
<StringRef(uint64_t RegNum
, bool IsEH
)> GetNameForDWARFReg
) {
497 SmallVector
<DWARFDie
, 4> Dies
;
498 for (const auto &Name
: Names
) {
499 getDies(DICtx
, DICtx
.getAppleNames(), Name
, Dies
);
500 getDies(DICtx
, DICtx
.getAppleTypes(), Name
, Dies
);
501 getDies(DICtx
, DICtx
.getAppleNamespaces(), Name
, Dies
);
502 getDies(DICtx
, DICtx
.getDebugNames(), Name
, Dies
);
505 Dies
.erase(llvm::unique(Dies
), Dies
.end());
507 DIDumpOptions DumpOpts
= getDumpOpts(DICtx
);
508 DumpOpts
.GetNameForDWARFReg
= GetNameForDWARFReg
;
509 for (DWARFDie Die
: Dies
)
510 Die
.dump(OS
, 0, DumpOpts
);
513 /// Print all DIEs in apple accelerator tables
514 static void findAllApple(
515 DWARFContext
&DICtx
, raw_ostream
&OS
,
516 std::function
<StringRef(uint64_t RegNum
, bool IsEH
)> GetNameForDWARFReg
) {
517 MapVector
<StringRef
, llvm::SmallSet
<DWARFDie
, 2>> NameToDies
;
519 auto PushDIEs
= [&](const AppleAcceleratorTable
&Accel
) {
520 for (const auto &Entry
: Accel
.entries()) {
521 if (std::optional
<uint64_t> Off
= Entry
.BaseEntry
.getDIESectionOffset()) {
522 std::optional
<StringRef
> MaybeName
= Entry
.readName();
523 DWARFDie Die
= DICtx
.getDIEForOffset(*Off
);
524 if (Die
&& MaybeName
)
525 NameToDies
[*MaybeName
].insert(Die
);
530 PushDIEs(DICtx
.getAppleNames());
531 PushDIEs(DICtx
.getAppleNamespaces());
532 PushDIEs(DICtx
.getAppleTypes());
534 DIDumpOptions DumpOpts
= getDumpOpts(DICtx
);
535 DumpOpts
.GetNameForDWARFReg
= GetNameForDWARFReg
;
536 for (const auto &[Name
, Dies
] : NameToDies
) {
537 OS
<< llvm::formatv("\nApple accelerator entries with name = \"{0}\":\n",
539 for (DWARFDie Die
: Dies
)
540 Die
.dump(OS
, 0, DumpOpts
);
544 /// Handle the --lookup option and dump the DIEs and line info for the given
546 /// TODO: specified Address for --lookup option could relate for several
547 /// different sections(in case not-linked object file). llvm-dwarfdump
548 /// need to do something with this: extend lookup option with section
549 /// information or probably display all matched entries, or something else...
550 static bool lookup(ObjectFile
&Obj
, DWARFContext
&DICtx
, uint64_t Address
,
552 auto DIEsForAddr
= DICtx
.getDIEsForAddress(Lookup
, DumpNonSkeleton
);
557 DIDumpOptions DumpOpts
= getDumpOpts(DICtx
);
558 DumpOpts
.ChildRecurseDepth
= 0;
559 DIEsForAddr
.CompileUnit
->dump(OS
, DumpOpts
);
560 if (DIEsForAddr
.FunctionDIE
) {
561 DIEsForAddr
.FunctionDIE
.dump(OS
, 2, DumpOpts
);
562 if (DIEsForAddr
.BlockDIE
)
563 DIEsForAddr
.BlockDIE
.dump(OS
, 4, DumpOpts
);
566 // TODO: it is neccessary to set proper SectionIndex here.
567 // object::SectionedAddress::UndefSection works for only absolute addresses.
568 if (DILineInfo LineInfo
= DICtx
.getLineInfoForAddress(
569 {Lookup
, object::SectionedAddress::UndefSection
}))
575 // Collect all sources referenced from the given line table, scoped to the given
576 // CU compilation directory.
577 static bool collectLineTableSources(const DWARFDebugLine::LineTable
<
,
579 std::vector
<std::string
> &Sources
) {
581 std::optional
<uint64_t> LastIndex
= LT
.getLastValidFileIndex();
582 for (uint64_t I
= LT
.hasFileAtIndex(0) ? 0 : 1,
583 E
= LastIndex
? *LastIndex
+ 1 : 0;
586 Result
&= LT
.getFileNameByIndex(
587 I
, CompDir
, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath
,
589 Sources
.push_back(std::move(Path
));
594 static bool collectObjectSources(ObjectFile
&Obj
, DWARFContext
&DICtx
,
595 const Twine
&Filename
, raw_ostream
&OS
) {
597 std::vector
<std::string
> Sources
;
599 bool HasCompileUnits
= false;
600 for (const auto &CU
: DICtx
.compile_units()) {
601 HasCompileUnits
= true;
602 // Extract paths from the line table for this CU. This allows combining the
603 // compilation directory with the line information, in case both the include
604 // directory and file names in the line table are relative.
605 const DWARFDebugLine::LineTable
*LT
= DICtx
.getLineTableForUnit(CU
.get());
606 StringRef CompDir
= CU
->getCompilationDir();
608 Result
&= collectLineTableSources(*LT
, CompDir
, Sources
);
610 // Since there's no line table for this CU, collect the name from the CU
612 const char *Name
= CU
->getUnitDIE().getShortName();
615 << Filename
<< ": missing name for compilation unit\n";
618 SmallString
<64> AbsName
;
619 if (sys::path::is_relative(Name
, sys::path::Style::posix
) &&
620 sys::path::is_relative(Name
, sys::path::Style::windows
))
622 sys::path::append(AbsName
, Name
);
623 Sources
.push_back(std::string(AbsName
));
627 if (!HasCompileUnits
) {
628 // Since there's no compile units available, walk the line tables and
629 // extract out any referenced paths.
630 DWARFDataExtractor
LineData(DICtx
.getDWARFObj(),
631 DICtx
.getDWARFObj().getLineSection(),
632 DICtx
.isLittleEndian(), 0);
633 DWARFDebugLine::SectionParser
Parser(LineData
, DICtx
, DICtx
.normal_units());
634 while (!Parser
.done()) {
635 const auto RecoverableErrorHandler
= [&](Error Err
) {
637 WithColor::defaultErrorHandler(std::move(Err
));
639 void (*UnrecoverableErrorHandler
)(Error Err
) = error
;
641 DWARFDebugLine::LineTable LT
=
642 Parser
.parseNext(RecoverableErrorHandler
, UnrecoverableErrorHandler
);
643 Result
&= collectLineTableSources(LT
, /*CompDir=*/"", Sources
);
647 // Dedup and order the sources.
649 Sources
.erase(llvm::unique(Sources
), Sources
.end());
651 for (StringRef Name
: Sources
)
656 static std::unique_ptr
<MCRegisterInfo
>
657 createRegInfo(const object::ObjectFile
&Obj
) {
658 std::unique_ptr
<MCRegisterInfo
> MCRegInfo
;
660 TT
.setArch(Triple::ArchType(Obj
.getArch()));
661 TT
.setVendor(Triple::UnknownVendor
);
662 TT
.setOS(Triple::UnknownOS
);
663 std::string TargetLookupError
;
664 const Target
*TheTarget
=
665 TargetRegistry::lookupTarget(TT
.str(), TargetLookupError
);
666 if (!TargetLookupError
.empty())
668 MCRegInfo
.reset(TheTarget
->createMCRegInfo(TT
.str()));
672 static bool dumpObjectFile(ObjectFile
&Obj
, DWARFContext
&DICtx
,
673 const Twine
&Filename
, raw_ostream
&OS
) {
675 auto MCRegInfo
= createRegInfo(Obj
);
677 logAllUnhandledErrors(createStringError(inconvertibleErrorCode(),
678 "Error in creating MCRegInfo"),
679 errs(), Filename
.str() + ": ");
681 auto GetRegName
= [&MCRegInfo
](uint64_t DwarfRegNum
, bool IsEH
) -> StringRef
{
684 if (std::optional
<MCRegister
> LLVMRegNum
=
685 MCRegInfo
->getLLVMRegNum(DwarfRegNum
, IsEH
))
686 if (const char *RegName
= MCRegInfo
->getName(*LLVMRegNum
))
687 return StringRef(RegName
);
691 // The UUID dump already contains all the same information.
692 if (!(DumpType
& DIDT_UUID
) || DumpType
== DIDT_All
)
693 OS
<< Filename
<< ":\tfile format " << Obj
.getFileFormatName() << '\n';
695 // Handle the --lookup option.
697 return lookup(Obj
, DICtx
, Lookup
, OS
);
699 // Handle the --name option.
702 for (const auto &name
: Name
)
703 Names
.insert((IgnoreCase
&& !UseRegex
) ? StringRef(name
).lower() : name
);
705 filterByName(Names
, DICtx
.normal_units(), OS
, GetRegName
);
706 filterByName(Names
, DICtx
.dwo_units(), OS
, GetRegName
);
710 // Handle the --find option and lower it to --debug-info=<offset>.
712 filterByAccelName(Find
, DICtx
, OS
, GetRegName
);
716 // Handle the --find-all-apple option and lower it to --debug-info=<offset>.
718 findAllApple(DICtx
, OS
, GetRegName
);
722 // Dump the complete DWARF structure.
723 auto DumpOpts
= getDumpOpts(DICtx
);
724 DumpOpts
.GetNameForDWARFReg
= GetRegName
;
725 DICtx
.dump(OS
, DumpOpts
, DumpOffsets
);
729 static bool verifyObjectFile(ObjectFile
&Obj
, DWARFContext
&DICtx
,
730 const Twine
&Filename
, raw_ostream
&OS
) {
731 // Verify the DWARF and exit with non-zero exit status if verification
733 raw_ostream
&stream
= Quiet
? nulls() : OS
;
734 stream
<< "Verifying " << Filename
.str() << ":\tfile format "
735 << Obj
.getFileFormatName() << "\n";
736 bool Result
= DICtx
.verify(stream
, getDumpOpts(DICtx
));
738 stream
<< "No errors.\n";
740 stream
<< "Errors detected.\n";
744 static bool handleBuffer(StringRef Filename
, MemoryBufferRef Buffer
,
745 HandlerFn HandleObj
, raw_ostream
&OS
);
747 static bool handleArchive(StringRef Filename
, Archive
&Arch
,
748 HandlerFn HandleObj
, raw_ostream
&OS
) {
750 Error Err
= Error::success();
751 for (const auto &Child
: Arch
.children(Err
)) {
752 auto BuffOrErr
= Child
.getMemoryBufferRef();
753 error(Filename
, BuffOrErr
.takeError());
754 auto NameOrErr
= Child
.getName();
755 error(Filename
, NameOrErr
.takeError());
756 std::string Name
= (Filename
+ "(" + NameOrErr
.get() + ")").str();
757 Result
&= handleBuffer(Name
, BuffOrErr
.get(), HandleObj
, OS
);
759 error(Filename
, std::move(Err
));
764 static bool handleBuffer(StringRef Filename
, MemoryBufferRef Buffer
,
765 HandlerFn HandleObj
, raw_ostream
&OS
) {
766 Expected
<std::unique_ptr
<Binary
>> BinOrErr
= object::createBinary(Buffer
);
767 error(Filename
, BinOrErr
.takeError());
770 auto RecoverableErrorHandler
= [&](Error E
) {
772 WithColor::defaultErrorHandler(std::move(E
));
774 if (auto *Obj
= dyn_cast
<ObjectFile
>(BinOrErr
->get())) {
775 if (filterArch(*Obj
)) {
776 std::unique_ptr
<DWARFContext
> DICtx
= DWARFContext::create(
777 *Obj
, DWARFContext::ProcessDebugRelocations::Process
, nullptr, "",
778 RecoverableErrorHandler
);
779 DICtx
->setParseCUTUIndexManually(ManuallyGenerateUnitIndex
);
780 if (!HandleObj(*Obj
, *DICtx
, Filename
, OS
))
783 } else if (auto *Fat
= dyn_cast
<MachOUniversalBinary
>(BinOrErr
->get()))
784 for (auto &ObjForArch
: Fat
->objects()) {
785 std::string ObjName
=
786 (Filename
+ "(" + ObjForArch
.getArchFlagName() + ")").str();
787 if (auto MachOOrErr
= ObjForArch
.getAsObjectFile()) {
788 auto &Obj
= **MachOOrErr
;
789 if (filterArch(Obj
)) {
790 std::unique_ptr
<DWARFContext
> DICtx
= DWARFContext::create(
791 Obj
, DWARFContext::ProcessDebugRelocations::Process
, nullptr, "",
792 RecoverableErrorHandler
);
793 if (!HandleObj(Obj
, *DICtx
, ObjName
, OS
))
798 consumeError(MachOOrErr
.takeError());
799 if (auto ArchiveOrErr
= ObjForArch
.getAsArchive()) {
800 error(ObjName
, ArchiveOrErr
.takeError());
801 if (!handleArchive(ObjName
, *ArchiveOrErr
.get(), HandleObj
, OS
))
805 consumeError(ArchiveOrErr
.takeError());
807 else if (auto *Arch
= dyn_cast
<Archive
>(BinOrErr
->get()))
808 Result
= handleArchive(Filename
, *Arch
, HandleObj
, OS
);
812 static bool handleFile(StringRef Filename
, HandlerFn HandleObj
,
814 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BuffOrErr
=
815 MemoryBuffer::getFileOrSTDIN(Filename
);
816 error(Filename
, BuffOrErr
.getError());
817 std::unique_ptr
<MemoryBuffer
> Buffer
= std::move(BuffOrErr
.get());
818 return handleBuffer(Filename
, *Buffer
, HandleObj
, OS
);
821 int main(int argc
, char **argv
) {
822 InitLLVM
X(argc
, argv
);
824 // Flush outs() when printing to errs(). This avoids interleaving output
828 llvm::InitializeAllTargetInfos();
829 llvm::InitializeAllTargetMCs();
831 HideUnrelatedOptions(
832 {&DwarfDumpCategory
, &SectionCategory
, &getColorCategory()});
833 cl::ParseCommandLineOptions(
835 "pretty-print DWARF debug information in object files"
836 " and debug info archives.\n");
838 // FIXME: Audit interactions between these two options and make them
840 if (Diff
&& Verbose
) {
841 WithColor::error() << "incompatible arguments: specifying both -diff and "
842 "-verbose is currently not supported";
845 // -error-detail and -json-summary-file both imply -verify
846 if (ErrorDetails
!= Unspecified
|| !JsonErrSummaryFile
.empty()) {
851 ToolOutputFile
OutputFile(OutputFilename
, EC
, sys::fs::OF_TextWithCRLF
);
852 error("unable to open output file " + OutputFilename
, EC
);
853 // Don't remove output file if we exit with an error.
856 bool OffsetRequested
= false;
858 // Defaults to dumping only debug_info, unless: A) verbose mode is specified,
859 // in which case all sections are dumped, or B) a specific section is
861 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \
862 if (Dump##ENUM_NAME.IsRequested) { \
863 DumpType |= DIDT_##ENUM_NAME; \
864 if (Dump##ENUM_NAME.HasValue) { \
865 DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val; \
866 OffsetRequested = true; \
869 #include "llvm/BinaryFormat/Dwarf.def"
870 #undef HANDLE_DWARF_SECTION
872 DumpType
|= DIDT_UUID
;
875 if (DumpType
== DIDT_Null
) {
876 if (Verbose
|| Verify
)
879 DumpType
= DIDT_DebugInfo
;
882 // Unless dumping a specific DIE, default to --show-children.
883 if (!ShowChildren
&& !Verify
&& !OffsetRequested
&& Name
.empty() &&
884 Find
.empty() && !FindAllApple
)
887 // Defaults to a.out if no filenames specified.
888 if (InputFilenames
.empty())
889 InputFilenames
.push_back("a.out");
891 // Expand any .dSYM bundles to the individual object files contained therein.
892 std::vector
<std::string
> Objects
;
893 for (const auto &F
: InputFilenames
) {
894 if (auto DsymObjectsOrErr
= MachOObjectFile::findDsymObjectMembers(F
)) {
895 if (DsymObjectsOrErr
->empty())
896 Objects
.push_back(F
);
898 llvm::append_range(Objects
, *DsymObjectsOrErr
);
900 error(DsymObjectsOrErr
.takeError());
906 for (StringRef Object
: Objects
)
907 Success
&= handleFile(Object
, verifyObjectFile
, OutputFile
.os());
908 } else if (Statistics
) {
909 for (StringRef Object
: Objects
)
910 Success
&= handleFile(Object
, collectStatsForObjectFile
, OutputFile
.os());
911 } else if (ShowSectionSizes
) {
912 for (StringRef Object
: Objects
)
913 Success
&= handleFile(Object
, collectObjectSectionSizes
, OutputFile
.os());
914 } else if (ShowSources
) {
915 for (StringRef Object
: Objects
)
916 Success
&= handleFile(Object
, collectObjectSources
, OutputFile
.os());
918 for (StringRef Object
: Objects
)
919 Success
&= handleFile(Object
, dumpObjectFile
, OutputFile
.os());
922 return Success
? EXIT_SUCCESS
: EXIT_FAILURE
;