Home | History | Annotate | Line # | Download | only in llvm-dwarfdump
      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