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