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