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