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/STLExtras.h" 15 #include "llvm/ADT/StringSet.h" 16 #include "llvm/ADT/Triple.h" 17 #include "llvm/DebugInfo/DIContext.h" 18 #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" 19 #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" 20 #include "llvm/DebugInfo/DWARF/DWARFContext.h" 21 #include "llvm/Object/Archive.h" 22 #include "llvm/Object/MachOUniversal.h" 23 #include "llvm/Object/ObjectFile.h" 24 #include "llvm/Support/CommandLine.h" 25 #include "llvm/Support/Debug.h" 26 #include "llvm/Support/Format.h" 27 #include "llvm/Support/InitLLVM.h" 28 #include "llvm/Support/MemoryBuffer.h" 29 #include "llvm/Support/Path.h" 30 #include "llvm/Support/Regex.h" 31 #include "llvm/Support/TargetSelect.h" 32 #include "llvm/Support/ToolOutputFile.h" 33 #include "llvm/Support/WithColor.h" 34 #include "llvm/Support/raw_ostream.h" 35 #include <cstdlib> 36 37 using namespace llvm; 38 using namespace llvm::dwarfdump; 39 using namespace llvm::object; 40 41 namespace { 42 /// Parser for options that take an optional offest argument. 43 /// @{ 44 struct OffsetOption { 45 uint64_t Val = 0; 46 bool HasValue = false; 47 bool IsRequested = false; 48 }; 49 struct BoolOption : public OffsetOption {}; 50 } // namespace 51 52 namespace llvm { 53 namespace cl { 54 template <> 55 class parser<OffsetOption> final : public basic_parser<OffsetOption> { 56 public: 57 parser(Option &O) : basic_parser(O) {} 58 59 /// Return true on error. 60 bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) { 61 if (Arg == "") { 62 Val.Val = 0; 63 Val.HasValue = false; 64 Val.IsRequested = true; 65 return false; 66 } 67 if (Arg.getAsInteger(0, Val.Val)) 68 return O.error("'" + Arg + "' value invalid for integer argument"); 69 Val.HasValue = true; 70 Val.IsRequested = true; 71 return false; 72 } 73 74 enum ValueExpected getValueExpectedFlagDefault() const { 75 return ValueOptional; 76 } 77 78 StringRef getValueName() const override { return StringRef("offset"); } 79 80 void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, 81 size_t GlobalWidth) const { 82 printOptionName(O, GlobalWidth); 83 outs() << "[=offset]"; 84 } 85 }; 86 87 template <> class parser<BoolOption> final : public basic_parser<BoolOption> { 88 public: 89 parser(Option &O) : basic_parser(O) {} 90 91 /// Return true on error. 92 bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) { 93 if (Arg != "") 94 return O.error("this is a flag and does not take a value"); 95 Val.Val = 0; 96 Val.HasValue = false; 97 Val.IsRequested = true; 98 return false; 99 } 100 101 enum ValueExpected getValueExpectedFlagDefault() const { 102 return ValueOptional; 103 } 104 105 StringRef getValueName() const override { return StringRef(); } 106 107 void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, 108 size_t GlobalWidth) const { 109 printOptionName(O, GlobalWidth); 110 } 111 }; 112 } // namespace cl 113 } // namespace llvm 114 115 /// @} 116 /// Command line options. 117 /// @{ 118 119 namespace { 120 using namespace cl; 121 122 OptionCategory DwarfDumpCategory("Specific Options"); 123 static list<std::string> 124 InputFilenames(Positional, desc("<input object files or .dSYM bundles>"), 125 cat(DwarfDumpCategory)); 126 127 cl::OptionCategory SectionCategory("Section-specific Dump Options", 128 "These control which sections are dumped. " 129 "Where applicable these parameters take an " 130 "optional =<offset> argument to dump only " 131 "the entry at the specified offset."); 132 133 static opt<bool> DumpAll("all", desc("Dump all debug info sections"), 134 cat(SectionCategory)); 135 static alias DumpAllAlias("a", desc("Alias for --all"), aliasopt(DumpAll), 136 cl::NotHidden); 137 138 // Options for dumping specific sections. 139 static unsigned DumpType = DIDT_Null; 140 static std::array<llvm::Optional<uint64_t>, (unsigned)DIDT_ID_Count> 141 DumpOffsets; 142 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ 143 static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \ 144 desc("Dump the " ELF_NAME " section"), \ 145 cat(SectionCategory)); 146 #include "llvm/BinaryFormat/Dwarf.def" 147 #undef HANDLE_DWARF_SECTION 148 149 // The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above. 150 static alias DumpDebugFrameAlias("eh-frame", desc("Alias for --debug-frame"), 151 NotHidden, cat(SectionCategory), 152 aliasopt(DumpDebugFrame)); 153 static list<std::string> 154 ArchFilters("arch", 155 desc("Dump debug information for the specified CPU " 156 "architecture only. Architectures may be specified by " 157 "name or by number. This option can be specified " 158 "multiple times, once for each desired architecture."), 159 cat(DwarfDumpCategory)); 160 static opt<bool> 161 Diff("diff", 162 desc("Emit diff-friendly output by omitting offsets and addresses."), 163 cat(DwarfDumpCategory)); 164 static list<std::string> 165 Find("find", 166 desc("Search for the exact match for <name> in the accelerator tables " 167 "and print the matching debug information entries. When no " 168 "accelerator tables are available, the slower but more complete " 169 "-name option can be used instead."), 170 value_desc("name"), cat(DwarfDumpCategory)); 171 static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find), 172 cl::NotHidden); 173 static opt<bool> IgnoreCase("ignore-case", 174 desc("Ignore case distinctions when using --name."), 175 value_desc("i"), cat(DwarfDumpCategory)); 176 static alias IgnoreCaseAlias("i", desc("Alias for --ignore-case."), 177 aliasopt(IgnoreCase), cl::NotHidden); 178 static list<std::string> Name( 179 "name", 180 desc("Find and print all debug info entries whose name (DW_AT_name " 181 "attribute) matches the exact text in <pattern>. When used with the " 182 "the -regex option <pattern> is interpreted as a regular expression."), 183 value_desc("pattern"), cat(DwarfDumpCategory)); 184 static alias NameAlias("n", desc("Alias for --name"), aliasopt(Name), 185 cl::NotHidden); 186 static opt<uint64_t> 187 Lookup("lookup", 188 desc("Lookup <address> in the debug information and print out any " 189 "available file, function, block and line table details."), 190 value_desc("address"), cat(DwarfDumpCategory)); 191 static opt<std::string> 192 OutputFilename("o", cl::init("-"), 193 cl::desc("Redirect output to the specified file."), 194 cl::value_desc("filename"), cat(DwarfDumpCategory)); 195 static alias OutputFilenameAlias("out-file", desc("Alias for -o."), 196 aliasopt(OutputFilename)); 197 static opt<bool> UseRegex( 198 "regex", 199 desc("Treat any <pattern> strings as regular " 200 "expressions when searching with --name. If --ignore-case is also " 201 "specified, the regular expression becomes case-insensitive."), 202 cat(DwarfDumpCategory)); 203 static alias RegexAlias("x", desc("Alias for --regex"), aliasopt(UseRegex), 204 cl::NotHidden); 205 static opt<bool> 206 ShowChildren("show-children", 207 desc("Show a debug info entry's children when selectively " 208 "printing entries."), 209 cat(DwarfDumpCategory)); 210 static alias ShowChildrenAlias("c", desc("Alias for --show-children."), 211 aliasopt(ShowChildren), cl::NotHidden); 212 static opt<bool> 213 ShowParents("show-parents", 214 desc("Show a debug info entry's parents when selectively " 215 "printing entries."), 216 cat(DwarfDumpCategory)); 217 static alias ShowParentsAlias("p", desc("Alias for --show-parents."), 218 aliasopt(ShowParents), cl::NotHidden); 219 static opt<bool> 220 ShowForm("show-form", 221 desc("Show DWARF form types after the DWARF attribute types."), 222 cat(DwarfDumpCategory)); 223 static alias ShowFormAlias("F", desc("Alias for --show-form."), 224 aliasopt(ShowForm), cat(DwarfDumpCategory), 225 cl::NotHidden); 226 static opt<unsigned> 227 ChildRecurseDepth("recurse-depth", 228 desc("Only recurse to a depth of N when displaying " 229 "children of debug info entries."), 230 cat(DwarfDumpCategory), init(-1U), value_desc("N")); 231 static alias ChildRecurseDepthAlias("r", desc("Alias for --recurse-depth."), 232 aliasopt(ChildRecurseDepth), cl::NotHidden); 233 static opt<unsigned> 234 ParentRecurseDepth("parent-recurse-depth", 235 desc("Only recurse to a depth of N when displaying " 236 "parents of debug info entries."), 237 cat(DwarfDumpCategory), init(-1U), value_desc("N")); 238 static opt<bool> 239 SummarizeTypes("summarize-types", 240 desc("Abbreviate the description of type unit entries."), 241 cat(DwarfDumpCategory)); 242 static cl::opt<bool> 243 Statistics("statistics", 244 cl::desc("Emit JSON-formatted debug info quality metrics."), 245 cat(DwarfDumpCategory)); 246 static cl::opt<bool> 247 ShowSectionSizes("show-section-sizes", 248 cl::desc("Show the sizes of all debug sections, " 249 "expressed in bytes."), 250 cat(DwarfDumpCategory)); 251 static cl::opt<bool> 252 ShowSources("show-sources", 253 cl::desc("Show the sources across all compilation units."), 254 cat(DwarfDumpCategory)); 255 static opt<bool> Verify("verify", desc("Verify the DWARF debug info."), 256 cat(DwarfDumpCategory)); 257 static opt<bool> Quiet("quiet", desc("Use with -verify to not emit to STDOUT."), 258 cat(DwarfDumpCategory)); 259 static opt<bool> DumpUUID("uuid", desc("Show the UUID for each architecture."), 260 cat(DwarfDumpCategory)); 261 static alias DumpUUIDAlias("u", desc("Alias for --uuid."), aliasopt(DumpUUID), 262 cl::NotHidden); 263 static opt<bool> Verbose("verbose", 264 desc("Print more low-level encoding details."), 265 cat(DwarfDumpCategory)); 266 static alias VerboseAlias("v", desc("Alias for --verbose."), aliasopt(Verbose), 267 cat(DwarfDumpCategory), cl::NotHidden); 268 static cl::extrahelp 269 HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); 270 } // namespace 271 /// @} 272 //===----------------------------------------------------------------------===// 273 274 static void error(Error Err) { 275 if (!Err) 276 return; 277 WithColor::error() << toString(std::move(Err)) << "\n"; 278 exit(1); 279 } 280 281 static void error(StringRef Prefix, Error Err) { 282 if (!Err) 283 return; 284 WithColor::error() << Prefix << ": " << toString(std::move(Err)) << "\n"; 285 exit(1); 286 } 287 288 static void error(StringRef Prefix, std::error_code EC) { 289 error(Prefix, errorCodeToError(EC)); 290 } 291 292 static DIDumpOptions getDumpOpts(DWARFContext &C) { 293 DIDumpOptions DumpOpts; 294 DumpOpts.DumpType = DumpType; 295 DumpOpts.ChildRecurseDepth = ChildRecurseDepth; 296 DumpOpts.ParentRecurseDepth = ParentRecurseDepth; 297 DumpOpts.ShowAddresses = !Diff; 298 DumpOpts.ShowChildren = ShowChildren; 299 DumpOpts.ShowParents = ShowParents; 300 DumpOpts.ShowForm = ShowForm; 301 DumpOpts.SummarizeTypes = SummarizeTypes; 302 DumpOpts.Verbose = Verbose; 303 DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler(); 304 // In -verify mode, print DIEs without children in error messages. 305 if (Verify) { 306 DumpOpts.Verbose = true; 307 return DumpOpts.noImplicitRecursion(); 308 } 309 return DumpOpts; 310 } 311 312 static uint32_t getCPUType(MachOObjectFile &MachO) { 313 if (MachO.is64Bit()) 314 return MachO.getHeader64().cputype; 315 else 316 return MachO.getHeader().cputype; 317 } 318 319 /// Return true if the object file has not been filtered by an --arch option. 320 static bool filterArch(ObjectFile &Obj) { 321 if (ArchFilters.empty()) 322 return true; 323 324 if (auto *MachO = dyn_cast<MachOObjectFile>(&Obj)) { 325 for (auto Arch : ArchFilters) { 326 // Match architecture number. 327 unsigned Value; 328 if (!StringRef(Arch).getAsInteger(0, Value)) 329 if (Value == getCPUType(*MachO)) 330 return true; 331 332 // Match as name. 333 if (MachO->getArchTriple().getArchName() == Triple(Arch).getArchName()) 334 return true; 335 } 336 } 337 return false; 338 } 339 340 using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, 341 const Twine &, raw_ostream &)>; 342 343 /// Print only DIEs that have a certain name. 344 static bool filterByName(const StringSet<> &Names, DWARFDie Die, 345 StringRef NameRef, raw_ostream &OS) { 346 DIDumpOptions DumpOpts = getDumpOpts(Die.getDwarfUnit()->getContext()); 347 std::string Name = 348 (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str(); 349 if (UseRegex) { 350 // Match regular expression. 351 for (auto Pattern : Names.keys()) { 352 Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags); 353 std::string Error; 354 if (!RE.isValid(Error)) { 355 errs() << "error in regular expression: " << Error << "\n"; 356 exit(1); 357 } 358 if (RE.match(Name)) { 359 Die.dump(OS, 0, DumpOpts); 360 return true; 361 } 362 } 363 } else if (Names.count(Name)) { 364 // Match full text. 365 Die.dump(OS, 0, DumpOpts); 366 return true; 367 } 368 return false; 369 } 370 371 /// Print only DIEs that have a certain name. 372 static void filterByName(const StringSet<> &Names, 373 DWARFContext::unit_iterator_range CUs, 374 raw_ostream &OS) { 375 for (const auto &CU : CUs) 376 for (const auto &Entry : CU->dies()) { 377 DWARFDie Die = {CU.get(), &Entry}; 378 if (const char *Name = Die.getName(DINameKind::ShortName)) 379 if (filterByName(Names, Die, Name, OS)) 380 continue; 381 if (const char *Name = Die.getName(DINameKind::LinkageName)) 382 filterByName(Names, Die, Name, OS); 383 } 384 } 385 386 static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel, 387 StringRef Name, SmallVectorImpl<DWARFDie> &Dies) { 388 for (const auto &Entry : Accel.equal_range(Name)) { 389 if (llvm::Optional<uint64_t> Off = Entry.getDIESectionOffset()) { 390 if (DWARFDie Die = DICtx.getDIEForOffset(*Off)) 391 Dies.push_back(Die); 392 } 393 } 394 } 395 396 static DWARFDie toDie(const DWARFDebugNames::Entry &Entry, 397 DWARFContext &DICtx) { 398 llvm::Optional<uint64_t> CUOff = Entry.getCUOffset(); 399 llvm::Optional<uint64_t> Off = Entry.getDIEUnitOffset(); 400 if (!CUOff || !Off) 401 return DWARFDie(); 402 403 DWARFCompileUnit *CU = DICtx.getCompileUnitForOffset(*CUOff); 404 if (!CU) 405 return DWARFDie(); 406 407 if (llvm::Optional<uint64_t> DWOId = CU->getDWOId()) { 408 // This is a skeleton unit. Look up the DIE in the DWO unit. 409 CU = DICtx.getDWOCompileUnitForHash(*DWOId); 410 if (!CU) 411 return DWARFDie(); 412 } 413 414 return CU->getDIEForOffset(CU->getOffset() + *Off); 415 } 416 417 static void getDies(DWARFContext &DICtx, const DWARFDebugNames &Accel, 418 StringRef Name, SmallVectorImpl<DWARFDie> &Dies) { 419 for (const auto &Entry : Accel.equal_range(Name)) { 420 if (DWARFDie Die = toDie(Entry, DICtx)) 421 Dies.push_back(Die); 422 } 423 } 424 425 /// Print only DIEs that have a certain name. 426 static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx, 427 raw_ostream &OS) { 428 SmallVector<DWARFDie, 4> Dies; 429 for (const auto &Name : Names) { 430 getDies(DICtx, DICtx.getAppleNames(), Name, Dies); 431 getDies(DICtx, DICtx.getAppleTypes(), Name, Dies); 432 getDies(DICtx, DICtx.getAppleNamespaces(), Name, Dies); 433 getDies(DICtx, DICtx.getDebugNames(), Name, Dies); 434 } 435 llvm::sort(Dies); 436 Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end()); 437 438 DIDumpOptions DumpOpts = getDumpOpts(DICtx); 439 for (DWARFDie Die : Dies) 440 Die.dump(OS, 0, DumpOpts); 441 } 442 443 /// Handle the --lookup option and dump the DIEs and line info for the given 444 /// address. 445 /// TODO: specified Address for --lookup option could relate for several 446 /// different sections(in case not-linked object file). llvm-dwarfdump 447 /// need to do something with this: extend lookup option with section 448 /// information or probably display all matched entries, or something else... 449 static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address, 450 raw_ostream &OS) { 451 auto DIEsForAddr = DICtx.getDIEsForAddress(Lookup); 452 453 if (!DIEsForAddr) 454 return false; 455 456 DIDumpOptions DumpOpts = getDumpOpts(DICtx); 457 DumpOpts.ChildRecurseDepth = 0; 458 DIEsForAddr.CompileUnit->dump(OS, DumpOpts); 459 if (DIEsForAddr.FunctionDIE) { 460 DIEsForAddr.FunctionDIE.dump(OS, 2, DumpOpts); 461 if (DIEsForAddr.BlockDIE) 462 DIEsForAddr.BlockDIE.dump(OS, 4, DumpOpts); 463 } 464 465 // TODO: it is neccessary to set proper SectionIndex here. 466 // object::SectionedAddress::UndefSection works for only absolute addresses. 467 if (DILineInfo LineInfo = DICtx.getLineInfoForAddress( 468 {Lookup, object::SectionedAddress::UndefSection})) 469 LineInfo.dump(OS); 470 471 return true; 472 } 473 474 // Collect all sources referenced from the given line table, scoped to the given 475 // CU compilation directory. 476 static bool collectLineTableSources(const DWARFDebugLine::LineTable <, 477 StringRef CompDir, 478 std::vector<std::string> &Sources) { 479 bool Result = true; 480 llvm::Optional<uint64_t> LastIndex = LT.getLastValidFileIndex(); 481 for (uint64_t I = LT.hasFileAtIndex(0) ? 0 : 1, 482 E = LastIndex ? *LastIndex + 1 : 0; 483 I < E; ++I) { 484 std::string Path; 485 Result &= LT.getFileNameByIndex( 486 I, CompDir, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, 487 Path); 488 Sources.push_back(std::move(Path)); 489 } 490 return Result; 491 } 492 493 static bool collectObjectSources(ObjectFile &Obj, DWARFContext &DICtx, 494 const Twine &Filename, raw_ostream &OS) { 495 bool Result = true; 496 std::vector<std::string> Sources; 497 498 bool HasCompileUnits = false; 499 for (const auto &CU : DICtx.compile_units()) { 500 HasCompileUnits = true; 501 // Extract paths from the line table for this CU. This allows combining the 502 // compilation directory with the line information, in case both the include 503 // directory and file names in the line table are relative. 504 const DWARFDebugLine::LineTable *LT = DICtx.getLineTableForUnit(CU.get()); 505 StringRef CompDir = CU->getCompilationDir(); 506 if (LT) { 507 Result &= collectLineTableSources(*LT, CompDir, Sources); 508 } else { 509 // Since there's no line table for this CU, collect the name from the CU 510 // itself. 511 const char *Name = CU->getUnitDIE().getShortName(); 512 if (!Name) { 513 WithColor::warning() 514 << Filename << ": missing name for compilation unit\n"; 515 continue; 516 } 517 SmallString<64> AbsName; 518 if (sys::path::is_relative(Name, sys::path::Style::posix) && 519 sys::path::is_relative(Name, sys::path::Style::windows)) 520 AbsName = CompDir; 521 sys::path::append(AbsName, Name); 522 Sources.push_back(std::string(AbsName)); 523 } 524 } 525 526 if (!HasCompileUnits) { 527 // Since there's no compile units available, walk the line tables and 528 // extract out any referenced paths. 529 DWARFDataExtractor LineData(DICtx.getDWARFObj(), 530 DICtx.getDWARFObj().getLineSection(), 531 DICtx.isLittleEndian(), 0); 532 DWARFDebugLine::SectionParser Parser(LineData, DICtx, DICtx.normal_units()); 533 while (!Parser.done()) { 534 const auto RecoverableErrorHandler = [&](Error Err) { 535 Result = false; 536 WithColor::defaultErrorHandler(std::move(Err)); 537 }; 538 void (*UnrecoverableErrorHandler)(Error Err) = error; 539 540 DWARFDebugLine::LineTable LT = 541 Parser.parseNext(RecoverableErrorHandler, UnrecoverableErrorHandler); 542 Result &= collectLineTableSources(LT, /*CompDir=*/"", Sources); 543 } 544 } 545 546 // Dedup and order the sources. 547 llvm::sort(Sources); 548 Sources.erase(std::unique(Sources.begin(), Sources.end()), Sources.end()); 549 550 for (StringRef Name : Sources) 551 OS << Name << "\n"; 552 return Result; 553 } 554 555 static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 556 const Twine &Filename, raw_ostream &OS) { 557 logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(), 558 Filename.str() + ": "); 559 // The UUID dump already contains all the same information. 560 if (!(DumpType & DIDT_UUID) || DumpType == DIDT_All) 561 OS << Filename << ":\tfile format " << Obj.getFileFormatName() << '\n'; 562 563 // Handle the --lookup option. 564 if (Lookup) 565 return lookup(Obj, DICtx, Lookup, OS); 566 567 // Handle the --name option. 568 if (!Name.empty()) { 569 StringSet<> Names; 570 for (auto name : Name) 571 Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name); 572 573 filterByName(Names, DICtx.normal_units(), OS); 574 filterByName(Names, DICtx.dwo_units(), OS); 575 return true; 576 } 577 578 // Handle the --find option and lower it to --debug-info=<offset>. 579 if (!Find.empty()) { 580 filterByAccelName(Find, DICtx, OS); 581 return true; 582 } 583 584 // Dump the complete DWARF structure. 585 DICtx.dump(OS, getDumpOpts(DICtx), DumpOffsets); 586 return true; 587 } 588 589 static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx, 590 const Twine &Filename, raw_ostream &OS) { 591 // Verify the DWARF and exit with non-zero exit status if verification 592 // fails. 593 raw_ostream &stream = Quiet ? nulls() : OS; 594 stream << "Verifying " << Filename.str() << ":\tfile format " 595 << Obj.getFileFormatName() << "\n"; 596 bool Result = DICtx.verify(stream, getDumpOpts(DICtx)); 597 if (Result) 598 stream << "No errors.\n"; 599 else 600 stream << "Errors detected.\n"; 601 return Result; 602 } 603 604 static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, 605 HandlerFn HandleObj, raw_ostream &OS); 606 607 static bool handleArchive(StringRef Filename, Archive &Arch, 608 HandlerFn HandleObj, raw_ostream &OS) { 609 bool Result = true; 610 Error Err = Error::success(); 611 for (auto Child : Arch.children(Err)) { 612 auto BuffOrErr = Child.getMemoryBufferRef(); 613 error(Filename, BuffOrErr.takeError()); 614 auto NameOrErr = Child.getName(); 615 error(Filename, NameOrErr.takeError()); 616 std::string Name = (Filename + "(" + NameOrErr.get() + ")").str(); 617 Result &= handleBuffer(Name, BuffOrErr.get(), HandleObj, OS); 618 } 619 error(Filename, std::move(Err)); 620 621 return Result; 622 } 623 624 static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, 625 HandlerFn HandleObj, raw_ostream &OS) { 626 Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Buffer); 627 error(Filename, BinOrErr.takeError()); 628 629 bool Result = true; 630 auto RecoverableErrorHandler = [&](Error E) { 631 Result = false; 632 WithColor::defaultErrorHandler(std::move(E)); 633 }; 634 if (auto *Obj = dyn_cast<ObjectFile>(BinOrErr->get())) { 635 if (filterArch(*Obj)) { 636 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create( 637 *Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "", 638 RecoverableErrorHandler); 639 if (!HandleObj(*Obj, *DICtx, Filename, OS)) 640 Result = false; 641 } 642 } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) 643 for (auto &ObjForArch : Fat->objects()) { 644 std::string ObjName = 645 (Filename + "(" + ObjForArch.getArchFlagName() + ")").str(); 646 if (auto MachOOrErr = ObjForArch.getAsObjectFile()) { 647 auto &Obj = **MachOOrErr; 648 if (filterArch(Obj)) { 649 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create( 650 Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "", 651 RecoverableErrorHandler); 652 if (!HandleObj(Obj, *DICtx, ObjName, OS)) 653 Result = false; 654 } 655 continue; 656 } else 657 consumeError(MachOOrErr.takeError()); 658 if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { 659 error(ObjName, ArchiveOrErr.takeError()); 660 if (!handleArchive(ObjName, *ArchiveOrErr.get(), HandleObj, OS)) 661 Result = false; 662 continue; 663 } else 664 consumeError(ArchiveOrErr.takeError()); 665 } 666 else if (auto *Arch = dyn_cast<Archive>(BinOrErr->get())) 667 Result = handleArchive(Filename, *Arch, HandleObj, OS); 668 return Result; 669 } 670 671 static bool handleFile(StringRef Filename, HandlerFn HandleObj, 672 raw_ostream &OS) { 673 ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = 674 MemoryBuffer::getFileOrSTDIN(Filename); 675 error(Filename, BuffOrErr.getError()); 676 std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get()); 677 return handleBuffer(Filename, *Buffer, HandleObj, OS); 678 } 679 680 int main(int argc, char **argv) { 681 InitLLVM X(argc, argv); 682 683 // Flush outs() when printing to errs(). This avoids interleaving output 684 // between the two. 685 errs().tie(&outs()); 686 687 llvm::InitializeAllTargetInfos(); 688 llvm::InitializeAllTargetMCs(); 689 690 HideUnrelatedOptions( 691 {&DwarfDumpCategory, &SectionCategory, &getColorCategory()}); 692 cl::ParseCommandLineOptions( 693 argc, argv, 694 "pretty-print DWARF debug information in object files" 695 " and debug info archives.\n"); 696 697 // FIXME: Audit interactions between these two options and make them 698 // compatible. 699 if (Diff && Verbose) { 700 WithColor::error() << "incompatible arguments: specifying both -diff and " 701 "-verbose is currently not supported"; 702 return 1; 703 } 704 705 std::error_code EC; 706 ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF); 707 error("unable to open output file " + OutputFilename, EC); 708 // Don't remove output file if we exit with an error. 709 OutputFile.keep(); 710 711 bool OffsetRequested = false; 712 713 // Defaults to dumping all sections, unless brief mode is specified in which 714 // case only the .debug_info section in dumped. 715 #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ 716 if (Dump##ENUM_NAME.IsRequested) { \ 717 DumpType |= DIDT_##ENUM_NAME; \ 718 if (Dump##ENUM_NAME.HasValue) { \ 719 DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val; \ 720 OffsetRequested = true; \ 721 } \ 722 } 723 #include "llvm/BinaryFormat/Dwarf.def" 724 #undef HANDLE_DWARF_SECTION 725 if (DumpUUID) 726 DumpType |= DIDT_UUID; 727 if (DumpAll) 728 DumpType = DIDT_All; 729 if (DumpType == DIDT_Null) { 730 if (Verbose) 731 DumpType = DIDT_All; 732 else 733 DumpType = DIDT_DebugInfo; 734 } 735 736 // Unless dumping a specific DIE, default to --show-children. 737 if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && 738 Find.empty()) 739 ShowChildren = true; 740 741 // Defaults to a.out if no filenames specified. 742 if (InputFilenames.empty()) 743 InputFilenames.push_back("a.out"); 744 745 // Expand any .dSYM bundles to the individual object files contained therein. 746 std::vector<std::string> Objects; 747 for (const auto &F : InputFilenames) { 748 if (auto DsymObjectsOrErr = MachOObjectFile::findDsymObjectMembers(F)) { 749 if (DsymObjectsOrErr->empty()) 750 Objects.push_back(F); 751 else 752 llvm::append_range(Objects, *DsymObjectsOrErr); 753 } else { 754 error(DsymObjectsOrErr.takeError()); 755 } 756 } 757 758 bool Success = true; 759 if (Verify) { 760 for (auto Object : Objects) 761 Success &= handleFile(Object, verifyObjectFile, OutputFile.os()); 762 } else if (Statistics) { 763 for (auto Object : Objects) 764 Success &= handleFile(Object, collectStatsForObjectFile, OutputFile.os()); 765 } else if (ShowSectionSizes) { 766 for (auto Object : Objects) 767 Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os()); 768 } else if (ShowSources) { 769 for (auto Object : Objects) 770 Success &= handleFile(Object, collectObjectSources, OutputFile.os()); 771 } else { 772 for (auto Object : Objects) 773 Success &= handleFile(Object, dumpObjectFile, OutputFile.os()); 774 } 775 776 return Success ? EXIT_SUCCESS : EXIT_FAILURE; 777 } 778