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