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/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" 40 #include <cstdlib> 41 42 using namespace llvm; 43 using namespace llvm::dwarfdump; 44 using namespace llvm::object; 45 46 namespace { 47 /// Parser for options that take an optional offest argument. 48 /// @{ 49 struct OffsetOption { 50 uint64_t Val = 0; 51 bool HasValue = false; 52 bool IsRequested = false; 53 }; 54 struct BoolOption : public OffsetOption {}; 55 } // namespace 56 57 namespace llvm { 58 namespace cl { 59 template <> 60 class parser<OffsetOption> final : public basic_parser<OffsetOption> { 61 public: 62 parser(Option &O) : basic_parser(O) {} 63 64 /// Return true on error. 65 bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) { 66 if (Arg == "") { 67 Val.Val = 0; 68 Val.HasValue = false; 69 Val.IsRequested = true; 70 return false; 71 } 72 if (Arg.getAsInteger(0, Val.Val)) 73 return O.error("'" + Arg + "' value invalid for integer argument"); 74 Val.HasValue = true; 75 Val.IsRequested = true; 76 return false; 77 } 78 79 enum ValueExpected getValueExpectedFlagDefault() const { 80 return ValueOptional; 81 } 82 83 StringRef getValueName() const override { return StringRef("offset"); } 84 85 void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, 86 size_t GlobalWidth) const { 87 printOptionName(O, GlobalWidth); 88 outs() << "[=offset]"; 89 } 90 }; 91 92 template <> class parser<BoolOption> final : public basic_parser<BoolOption> { 93 public: 94 parser(Option &O) : basic_parser(O) {} 95 96 /// Return true on error. 97 bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) { 98 if (Arg != "") 99 return O.error("this is a flag and does not take a value"); 100 Val.Val = 0; 101 Val.HasValue = false; 102 Val.IsRequested = true; 103 return false; 104 } 105 106 enum ValueExpected getValueExpectedFlagDefault() const { 107 return ValueOptional; 108 } 109 110 StringRef getValueName() const override { return StringRef(); } 111 112 void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, 113 size_t GlobalWidth) const { 114 printOptionName(O, GlobalWidth); 115 } 116 }; 117 } // namespace cl 118 } // namespace llvm 119 120 /// @} 121 /// Command line options. 122 /// @{ 123 124 namespace { 125 using namespace cl; 126 127 enum ErrorDetailLevel { 128 OnlyDetailsNoSummary, 129 NoDetailsOnlySummary, 130 NoDetailsOrSummary, 131 BothDetailsAndSummary, 132 Unspecified 133 }; 134 135 OptionCategory DwarfDumpCategory("Specific Options"); 136 static list<std::string> 137 InputFilenames(Positional, desc("<input object files or .dSYM bundles>"), 138 cat(DwarfDumpCategory)); 139 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."); 145 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), 149 cl::NotHidden); 150 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 160 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> 166 ArchFilters("arch", 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)); 172 static opt<bool> 173 Diff("diff", 174 desc("Emit diff-friendly output by omitting offsets and addresses."), 175 cat(DwarfDumpCategory)); 176 static list<std::string> 177 Find("find", 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), 184 cl::NotHidden); 185 static opt<bool> FindAllApple( 186 "find-all-apple", 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( 193 "dwo", 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)); 198 199 static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."), 200 aliasopt(IgnoreCase), cl::NotHidden); 201 static list<std::string> Name( 202 "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), 208 cl::NotHidden); 209 static opt<uint64_t> 210 Lookup("lookup", 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( 221 "regex", 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), 227 cl::NotHidden); 228 static opt<bool> 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); 235 static opt<bool> 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); 242 static opt<bool> 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), 248 cl::NotHidden); 249 static opt<unsigned> 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); 256 static opt<unsigned> 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")); 261 static opt<bool> 262 SummarizeTypes("summarize-types", 263 desc("Abbreviate the description of type unit entries."), 264 cat(DwarfDumpCategory)); 265 static cl::opt<bool> 266 Statistics("statistics", 267 cl::desc("Emit JSON-formatted debug info quality metrics."), 268 cat(DwarfDumpCategory)); 269 static cl::opt<bool> 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 "manaully-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)); 281 static cl::opt<bool> 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), 310 cl::NotHidden); 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); 316 static cl::extrahelp 317 HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); 318 } // namespace 319 /// @} 320 //===----------------------------------------------------------------------===// 321 322 static void error(Error Err) { 323 if (!Err) 324 return; 325 WithColor::error() << toString(std::move(Err)) << "\n"; 326 exit(1); 327 } 328 329 static void error(StringRef Prefix, Error Err) { 330 if (!Err) 331 return; 332 WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n"; 333 exit(1); 334 } 335 336 static void error(StringRef Prefix, std::error_code EC) { 337 error(Prefix, errorCodeToError(EC)); 338 } 339 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. 354 if (Verify) { 355 DumpOpts.Verbose = ErrorDetails != NoDetailsOnlySummary && 356 ErrorDetails != NoDetailsOrSummary; 357 DumpOpts.ShowAggregateErrors = ErrorDetails != OnlyDetailsNoSummary && 358 ErrorDetails != NoDetailsOnlySummary; 359 DumpOpts.JsonErrSummaryFile = JsonErrSummaryFile; 360 return DumpOpts.noImplicitRecursion(); 361 } 362 return DumpOpts; 363 } 364 365 static uint32_t getCPUType(MachOObjectFile &MachO) { 366 if (MachO.is64Bit()) 367 return MachO.getHeader64().cputype; 368 else 369 return MachO.getHeader().cputype; 370 } 371 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()) 375 return true; 376 377 if (auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) { 378 for (const StringRef Arch : ArchFilters) { 379 // Match architecture number. 380 unsigned Value; 381 if (!Arch.getAsInteger(0, Value)) 382 if (Value == getCPUType(*MachO)) 383 return true; 384 385 // Match as name. 386 if (MachO->getArchTriple().getArchName() == Triple(Arch).getArchName()) 387 return true; 388 } 389 } 390 return false; 391 } 392 393 using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, 394 const Twine &, raw_ostream &)>; 395 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; 402 std::string Name = 403 (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str(); 404 if (UseRegex) { 405 // Match regular expression. 406 for (auto Pattern : Names.keys()) { 407 Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags); 408 std::string Error; 409 if (!RE.isValid(Error)) { 410 errs() << "error in regular expression: " << Error << "\n"; 411 exit(1); 412 } 413 if (RE.match(Name)) { 414 Die.dump(OS, 0, DumpOpts); 415 return true; 416 } 417 } 418 } else if (Names.count(Name)) { 419 // Match full text. 420 Die.dump(OS, 0, DumpOpts); 421 return true; 422 } 423 return false; 424 } 425 426 /// Print only DIEs that have a certain name. 427 static void filterByName( 428 const StringSet<> &Names, DWARFContext::unit_iterator_range CUs, 429 raw_ostream &OS, 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)) 436 continue; 437 if (const char *Name = Die.getName(DINameKind::LinkageName)) 438 filterByName(Names, Die, Name, OS, GetNameForDWARFReg); 439 } 440 }; 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()); 450 } 451 } 452 } 453 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)) 459 Dies.push_back(Die); 460 } 461 } 462 } 463 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(); 468 if (!CUOff || !Off) 469 return DWARFDie(); 470 471 DWARFCompileUnit *CU = DICtx.getCompileUnitForOffset(*CUOff); 472 if (!CU) 473 return DWARFDie(); 474 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); 478 if (!CU) 479 return DWARFDie(); 480 } 481 482 return CU->getDIEForOffset(CU->getOffset() + *Off); 483 } 484 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)) 489 Dies.push_back(Die); 490 } 491 } 492 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); 503 } 504 llvm::sort(Dies); 505 Dies.erase(llvm::unique(Dies), Dies.end()); 506 507 DIDumpOptions DumpOpts = getDumpOpts(DICtx); 508 DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg; 509 for (DWARFDie Die : Dies) 510 Die.dump(OS, 0, DumpOpts); 511 } 512 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; 518 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); 526 } 527 } 528 }; 529 530 PushDIEs(DICtx.getAppleNames()); 531 PushDIEs(DICtx.getAppleNamespaces()); 532 PushDIEs(DICtx.getAppleTypes()); 533 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", 538 Name); 539 for (DWARFDie Die : Dies) 540 Die.dump(OS, 0, DumpOpts); 541 } 542 } 543 544 /// Handle the --lookup option and dump the DIEs and line info for the given 545 /// address. 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, 551 raw_ostream &OS) { 552 auto DIEsForAddr = DICtx.getDIEsForAddress(Lookup, DumpNonSkeleton); 553 554 if (!DIEsForAddr) 555 return false; 556 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); 564 } 565 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})) 570 LineInfo.dump(OS); 571 572 return true; 573 } 574 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 <, 578 StringRef CompDir, 579 std::vector<std::string> &Sources) { 580 bool Result = true; 581 std::optional<uint64_t> LastIndex = LT.getLastValidFileIndex(); 582 for (uint64_t I = LT.hasFileAtIndex(0) ? 0 : 1, 583 E = LastIndex ? *LastIndex + 1 : 0; 584 I < E; ++I) { 585 std::string Path; 586 Result &= LT.getFileNameByIndex( 587 I, CompDir, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, 588 Path); 589 Sources.push_back(std::move(Path)); 590 } 591 return Result; 592 } 593 594 static bool collectObjectSources(ObjectFile &Obj, DWARFContext &DICtx, 595 const Twine &Filename, raw_ostream &OS) { 596 bool Result = true; 597 std::vector<std::string> Sources; 598 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(); 607 if (LT) { 608 Result &= collectLineTableSources(*LT, CompDir, Sources); 609 } else { 610 // Since there's no line table for this CU, collect the name from the CU 611 // itself. 612 const char *Name = CU->getUnitDIE().getShortName(); 613 if (!Name) { 614 WithColor::warning() 615 << Filename << ": missing name for compilation unit\n"; 616 continue; 617 } 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)) 621 AbsName = CompDir; 622 sys::path::append(AbsName, Name); 623 Sources.push_back(std::string(AbsName)); 624 } 625 } 626 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) { 636 Result = false; 637 WithColor::defaultErrorHandler(std::move(Err)); 638 }; 639 void (*UnrecoverableErrorHandler)(Error Err) = error; 640 641 DWARFDebugLine::LineTable LT = 642 Parser.parseNext(RecoverableErrorHandler, UnrecoverableErrorHandler); 643 Result &= collectLineTableSources(LT, /*CompDir=*/"", Sources); 644 } 645 } 646 647 // Dedup and order the sources. 648 llvm::sort(Sources); 649 Sources.erase(llvm::unique(Sources), Sources.end()); 650 651 for (StringRef Name : Sources) 652 OS << Name << "\n"; 653 return Result; 654 } 655 656 static std::unique_ptr<MCRegisterInfo> 657 createRegInfo(const object::ObjectFile &Obj) { 658 std::unique_ptr<MCRegisterInfo> MCRegInfo; 659 Triple TT; 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()) 667 return nullptr; 668 MCRegInfo.reset(TheTarget->createMCRegInfo(TT.str())); 669 return MCRegInfo; 670 } 671 672 static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 673 const Twine &Filename, raw_ostream &OS) { 674 675 auto MCRegInfo = createRegInfo(Obj); 676 if (!MCRegInfo) 677 logAllUnhandledErrors(createStringError(inconvertibleErrorCode(), 678 "Error in creating MCRegInfo"), 679 errs(), Filename.str() + ": "); 680 681 auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> StringRef { 682 if (!MCRegInfo) 683 return {}; 684 if (std::optional<unsigned> LLVMRegNum = 685 MCRegInfo->getLLVMRegNum(DwarfRegNum, IsEH)) 686 if (const char *RegName = MCRegInfo->getName(*LLVMRegNum)) 687 return StringRef(RegName); 688 return {}; 689 }; 690 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'; 694 695 // Handle the --lookup option. 696 if (Lookup) 697 return lookup(Obj, DICtx, Lookup, OS); 698 699 // Handle the --name option. 700 if (!Name.empty()) { 701 StringSet<> Names; 702 for (const auto &name : Name) 703 Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name); 704 705 filterByName(Names, DICtx.normal_units(), OS, GetRegName); 706 filterByName(Names, DICtx.dwo_units(), OS, GetRegName); 707 return true; 708 } 709 710 // Handle the --find option and lower it to --debug-info=<offset>. 711 if (!Find.empty()) { 712 filterByAccelName(Find, DICtx, OS, GetRegName); 713 return true; 714 } 715 716 // Handle the --find-all-apple option and lower it to --debug-info=<offset>. 717 if (FindAllApple) { 718 findAllApple(DICtx, OS, GetRegName); 719 return true; 720 } 721 722 // Dump the complete DWARF structure. 723 auto DumpOpts = getDumpOpts(DICtx); 724 DumpOpts.GetNameForDWARFReg = GetRegName; 725 DICtx.dump(OS, DumpOpts, DumpOffsets); 726 return true; 727 } 728 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 732 // fails. 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)); 737 if (Result) 738 stream << "No errors.\n"; 739 else 740 stream << "Errors detected.\n"; 741 return Result; 742 } 743 744 static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, 745 HandlerFn HandleObj, raw_ostream &OS); 746 747 static bool handleArchive(StringRef Filename, Archive &Arch, 748 HandlerFn HandleObj, raw_ostream &OS) { 749 bool Result = true; 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); 758 } 759 error(Filename, std::move(Err)); 760 761 return Result; 762 } 763 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()); 768 769 bool Result = true; 770 auto RecoverableErrorHandler = [&](Error E) { 771 Result = false; 772 WithColor::defaultErrorHandler(std::move(E)); 773 }; 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)) 781 Result = false; 782 } 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)) 794 Result = false; 795 } 796 continue; 797 } else 798 consumeError(MachOOrErr.takeError()); 799 if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { 800 error(ObjName, ArchiveOrErr.takeError()); 801 if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS)) 802 Result = false; 803 continue; 804 } else 805 consumeError(ArchiveOrErr.takeError()); 806 } 807 else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get())) 808 Result = handleArchive(Filename, *Arch, HandleObj, OS); 809 return Result; 810 } 811 812 static bool handleFile(StringRef Filename, HandlerFn HandleObj, 813 raw_ostream &OS) { 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); 819 } 820 821 int main(int argc, char **argv) { 822 InitLLVM X(argc, argv); 823 824 // Flush outs() when printing to errs(). This avoids interleaving output 825 // between the two. 826 errs().tie(&outs()); 827 828 llvm::InitializeAllTargetInfos(); 829 llvm::InitializeAllTargetMCs(); 830 831 HideUnrelatedOptions( 832 {&DwarfDumpCategory, &SectionCategory, &getColorCategory()}); 833 cl::ParseCommandLineOptions( 834 argc, argv, 835 "pretty-print DWARF debug information in object files" 836 " and debug info archives.\n"); 837 838 // FIXME: Audit interactions between these two options and make them 839 // compatible. 840 if (Diff && Verbose) { 841 WithColor::error() << "incompatible arguments: specifying both -diff and " 842 "-verbose is currently not supported"; 843 return 1; 844 } 845 // -error-detail and -json-summary-file both imply -verify 846 if (ErrorDetails != Unspecified || !JsonErrSummaryFile.empty()) { 847 Verify = true; 848 } 849 850 std::error_code EC; 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. 854 OutputFile.keep(); 855 856 bool OffsetRequested = false; 857 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 860 // requested. 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; \ 867 } \ 868 } 869 #include "llvm/BinaryFormat/Dwarf.def" 870 #undef HANDLE_DWARF_SECTION 871 if (DumpUUID) 872 DumpType |= DIDT_UUID; 873 if (DumpAll) 874 DumpType = DIDT_All; 875 if (DumpType == DIDT_Null) { 876 if (Verbose || Verify) 877 DumpType = DIDT_All; 878 else 879 DumpType = DIDT_DebugInfo; 880 } 881 882 // Unless dumping a specific DIE, default to --show-children. 883 if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && 884 Find.empty() && !FindAllApple) 885 ShowChildren = true; 886 887 // Defaults to a.out if no filenames specified. 888 if (InputFilenames.empty()) 889 InputFilenames.push_back("a.out"); 890 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); 897 else 898 llvm::append_range(Objects, *DsymObjectsOrErr); 899 } else { 900 error(DsymObjectsOrErr.takeError()); 901 } 902 } 903 904 bool Success = true; 905 if (Verify) { 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()); 917 } else { 918 for (StringRef Object : Objects) 919 Success &= handleFile(Object, dumpObjectFile, OutputFile.os()); 920 } 921 922 return Success ? EXIT_SUCCESS : EXIT_FAILURE; 923 } 924