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