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