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/MapVector.h" |
15 | #include "llvm/ADT/STLExtras.h" |
16 | #include "llvm/ADT/SmallSet.h" |
17 | #include "llvm/ADT/StringSet.h" |
18 | #include "llvm/DebugInfo/DIContext.h" |
19 | #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" |
20 | #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" |
21 | #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
22 | #include "llvm/MC/MCRegisterInfo.h" |
23 | #include "llvm/MC/TargetRegistry.h" |
24 | #include "llvm/Object/Archive.h" |
25 | #include "llvm/Object/MachOUniversal.h" |
26 | #include "llvm/Object/ObjectFile.h" |
27 | #include "llvm/Support/CommandLine.h" |
28 | #include "llvm/Support/Debug.h" |
29 | #include "llvm/Support/Format.h" |
30 | #include "llvm/Support/FormatVariadic.h" |
31 | #include "llvm/Support/InitLLVM.h" |
32 | #include "llvm/Support/MemoryBuffer.h" |
33 | #include "llvm/Support/Path.h" |
34 | #include "llvm/Support/Regex.h" |
35 | #include "llvm/Support/TargetSelect.h" |
36 | #include "llvm/Support/ToolOutputFile.h" |
37 | #include "llvm/Support/WithColor.h" |
38 | #include "llvm/Support/raw_ostream.h" |
39 | #include "llvm/TargetParser/Triple.h" |
40 | #include <cstdlib> |
41 | |
42 | using namespace llvm; |
43 | using namespace llvm::dwarfdump; |
44 | using namespace llvm::object; |
45 | |
46 | namespace { |
47 | /// Parser for options that take an optional offest argument. |
48 | /// @{ |
49 | struct OffsetOption { |
50 | uint64_t Val = 0; |
51 | bool HasValue = false; |
52 | bool IsRequested = false; |
53 | }; |
54 | struct BoolOption : public OffsetOption {}; |
55 | } // namespace |
56 | |
57 | namespace llvm { |
58 | namespace cl { |
59 | template <> |
60 | class parser<OffsetOption> final : public basic_parser<OffsetOption> { |
61 | public: |
62 | parser(Option &O) : basic_parser(O) {} |
63 | |
64 | /// Return true on error. |
65 | bool parse(Option &O, StringRef ArgName, StringRef Arg, OffsetOption &Val) { |
66 | if (Arg == "" ) { |
67 | Val.Val = 0; |
68 | Val.HasValue = false; |
69 | Val.IsRequested = true; |
70 | return false; |
71 | } |
72 | if (Arg.getAsInteger(Radix: 0, Result&: Val.Val)) |
73 | return O.error(Message: "'" + Arg + "' value invalid for integer argument" ); |
74 | Val.HasValue = true; |
75 | Val.IsRequested = true; |
76 | return false; |
77 | } |
78 | |
79 | enum ValueExpected getValueExpectedFlagDefault() const { |
80 | return ValueOptional; |
81 | } |
82 | |
83 | StringRef getValueName() const override { return StringRef("offset" ); } |
84 | |
85 | void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, |
86 | size_t GlobalWidth) const { |
87 | printOptionName(O, GlobalWidth); |
88 | outs() << "[=offset]" ; |
89 | } |
90 | }; |
91 | |
92 | template <> class parser<BoolOption> final : public basic_parser<BoolOption> { |
93 | public: |
94 | parser(Option &O) : basic_parser(O) {} |
95 | |
96 | /// Return true on error. |
97 | bool parse(Option &O, StringRef ArgName, StringRef Arg, BoolOption &Val) { |
98 | if (Arg != "" ) |
99 | return O.error(Message: "this is a flag and does not take a value" ); |
100 | Val.Val = 0; |
101 | Val.HasValue = false; |
102 | Val.IsRequested = true; |
103 | return false; |
104 | } |
105 | |
106 | enum ValueExpected getValueExpectedFlagDefault() const { |
107 | return ValueOptional; |
108 | } |
109 | |
110 | StringRef getValueName() const override { return StringRef(); } |
111 | |
112 | void printOptionDiff(const Option &O, OffsetOption V, OptVal Default, |
113 | size_t GlobalWidth) const { |
114 | printOptionName(O, GlobalWidth); |
115 | } |
116 | }; |
117 | } // namespace cl |
118 | } // namespace llvm |
119 | |
120 | /// @} |
121 | /// Command line options. |
122 | /// @{ |
123 | |
124 | namespace { |
125 | using namespace cl; |
126 | |
127 | enum ErrorDetailLevel { |
128 | OnlyDetailsNoSummary, |
129 | NoDetailsOnlySummary, |
130 | NoDetailsOrSummary, |
131 | BothDetailsAndSummary, |
132 | Unspecified |
133 | }; |
134 | |
135 | OptionCategory DwarfDumpCategory("Specific Options" ); |
136 | static list<std::string> |
137 | InputFilenames(Positional, desc("<input object files or .dSYM bundles>" ), |
138 | cat(DwarfDumpCategory)); |
139 | |
140 | cl::OptionCategory SectionCategory("Section-specific Dump Options" , |
141 | "These control which sections are dumped. " |
142 | "Where applicable these parameters take an " |
143 | "optional =<offset> argument to dump only " |
144 | "the entry at the specified offset." ); |
145 | |
146 | static opt<bool> DumpAll("all" , desc("Dump all debug info sections" ), |
147 | cat(SectionCategory)); |
148 | static alias DumpAllAlias("a" , desc("Alias for --all" ), aliasopt(DumpAll), |
149 | cl::NotHidden); |
150 | |
151 | // Options for dumping specific sections. |
152 | static unsigned DumpType = DIDT_Null; |
153 | static std::array<std::optional<uint64_t>, (unsigned)DIDT_ID_Count> DumpOffsets; |
154 | #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ |
155 | static opt<OPTION> Dump##ENUM_NAME(CMDLINE_NAME, \ |
156 | desc("Dump the " ELF_NAME " section"), \ |
157 | cat(SectionCategory)); |
158 | #include "llvm/BinaryFormat/Dwarf.def" |
159 | #undef HANDLE_DWARF_SECTION |
160 | |
161 | // The aliased DumpDebugFrame is created by the Dwarf.def x-macro just above. |
162 | static alias DumpDebugFrameAlias("eh-frame" , desc("Alias for --debug-frame" ), |
163 | NotHidden, cat(SectionCategory), |
164 | aliasopt(DumpDebugFrame)); |
165 | static list<std::string> |
166 | ArchFilters("arch" , |
167 | desc("Dump debug information for the specified CPU " |
168 | "architecture only. Architectures may be specified by " |
169 | "name or by number. This option can be specified " |
170 | "multiple times, once for each desired architecture." ), |
171 | cat(DwarfDumpCategory)); |
172 | static opt<bool> |
173 | Diff("diff" , |
174 | desc("Emit diff-friendly output by omitting offsets and addresses." ), |
175 | cat(DwarfDumpCategory)); |
176 | static list<std::string> |
177 | Find("find" , |
178 | desc("Search for the exact match for <name> in the accelerator tables " |
179 | "and print the matching debug information entries. When no " |
180 | "accelerator tables are available, the slower but more complete " |
181 | "-name option can be used instead." ), |
182 | value_desc("name" ), cat(DwarfDumpCategory)); |
183 | static alias FindAlias("f" , desc("Alias for --find." ), aliasopt(Find), |
184 | cl::NotHidden); |
185 | static opt<bool> FindAllApple( |
186 | "find-all-apple" , |
187 | desc("Print every debug information entry in the accelerator tables." ), |
188 | cat(DwarfDumpCategory)); |
189 | static opt<bool> IgnoreCase("ignore-case" , |
190 | desc("Ignore case distinctions when using --name." ), |
191 | value_desc("i" ), cat(DwarfDumpCategory)); |
192 | static opt<bool> DumpNonSkeleton( |
193 | "dwo" , |
194 | desc("Dump the non skeleton DIE in the .dwo or .dwp file after dumping the " |
195 | "skeleton DIE from the main executable. This allows dumping the .dwo " |
196 | "files with resolved addresses." ), |
197 | value_desc("d" ), cat(DwarfDumpCategory)); |
198 | |
199 | static alias IgnoreCaseAlias("i" , desc("Alias for --ignore-case." ), |
200 | aliasopt(IgnoreCase), cl::NotHidden); |
201 | static list<std::string> Name( |
202 | "name" , |
203 | desc("Find and print all debug info entries whose name (DW_AT_name " |
204 | "attribute) matches the exact text in <pattern>. When used with the " |
205 | "the -regex option <pattern> is interpreted as a regular expression." ), |
206 | value_desc("pattern" ), cat(DwarfDumpCategory)); |
207 | static alias NameAlias("n" , desc("Alias for --name" ), aliasopt(Name), |
208 | cl::NotHidden); |
209 | static opt<uint64_t> |
210 | Lookup("lookup" , |
211 | desc("Lookup <address> in the debug information and print out any " |
212 | "available file, function, block and line table details." ), |
213 | value_desc("address" ), cat(DwarfDumpCategory)); |
214 | static opt<std::string> |
215 | OutputFilename("o" , cl::init(Val: "-" ), |
216 | cl::desc("Redirect output to the specified file." ), |
217 | cl::value_desc("filename" ), cat(DwarfDumpCategory)); |
218 | static alias OutputFilenameAlias("out-file" , desc("Alias for -o." ), |
219 | aliasopt(OutputFilename)); |
220 | static opt<bool> UseRegex( |
221 | "regex" , |
222 | desc("Treat any <pattern> strings as regular " |
223 | "expressions when searching with --name. If --ignore-case is also " |
224 | "specified, the regular expression becomes case-insensitive." ), |
225 | cat(DwarfDumpCategory)); |
226 | static alias RegexAlias("x" , desc("Alias for --regex" ), aliasopt(UseRegex), |
227 | cl::NotHidden); |
228 | static opt<bool> |
229 | ShowChildren("show-children" , |
230 | desc("Show a debug info entry's children when selectively " |
231 | "printing entries." ), |
232 | cat(DwarfDumpCategory)); |
233 | static alias ShowChildrenAlias("c" , desc("Alias for --show-children." ), |
234 | aliasopt(ShowChildren), cl::NotHidden); |
235 | static opt<bool> |
236 | ShowParents("show-parents" , |
237 | desc("Show a debug info entry's parents when selectively " |
238 | "printing entries." ), |
239 | cat(DwarfDumpCategory)); |
240 | static alias ShowParentsAlias("p" , desc("Alias for --show-parents." ), |
241 | aliasopt(ShowParents), cl::NotHidden); |
242 | static opt<bool> |
243 | ShowForm("show-form" , |
244 | desc("Show DWARF form types after the DWARF attribute types." ), |
245 | cat(DwarfDumpCategory)); |
246 | static alias ShowFormAlias("F" , desc("Alias for --show-form." ), |
247 | aliasopt(ShowForm), cat(DwarfDumpCategory), |
248 | cl::NotHidden); |
249 | static opt<unsigned> |
250 | ChildRecurseDepth("recurse-depth" , |
251 | desc("Only recurse to a depth of N when displaying " |
252 | "children of debug info entries." ), |
253 | cat(DwarfDumpCategory), init(Val: -1U), value_desc("N" )); |
254 | static alias ChildRecurseDepthAlias("r" , desc("Alias for --recurse-depth." ), |
255 | aliasopt(ChildRecurseDepth), cl::NotHidden); |
256 | static opt<unsigned> |
257 | ParentRecurseDepth("parent-recurse-depth" , |
258 | desc("Only recurse to a depth of N when displaying " |
259 | "parents of debug info entries." ), |
260 | cat(DwarfDumpCategory), init(Val: -1U), value_desc("N" )); |
261 | static opt<bool> |
262 | SummarizeTypes("summarize-types" , |
263 | desc("Abbreviate the description of type unit entries." ), |
264 | cat(DwarfDumpCategory)); |
265 | static cl::opt<bool> |
266 | Statistics("statistics" , |
267 | cl::desc("Emit JSON-formatted debug info quality metrics." ), |
268 | cat(DwarfDumpCategory)); |
269 | static cl::opt<bool> |
270 | ShowSectionSizes("show-section-sizes" , |
271 | cl::desc("Show the sizes of all debug sections, " |
272 | "expressed in bytes." ), |
273 | cat(DwarfDumpCategory)); |
274 | static cl::opt<bool> ManuallyGenerateUnitIndex( |
275 | "manaully-generate-unit-index" , |
276 | cl::desc("if the input is dwp file, parse .debug_info " |
277 | "section and use it to populate " |
278 | "DW_SECT_INFO contributions in cu-index. " |
279 | "For DWARF5 it also populated TU Index." ), |
280 | cl::init(Val: false), cl::Hidden, cl::cat(DwarfDumpCategory)); |
281 | static cl::opt<bool> |
282 | ShowSources("show-sources" , |
283 | cl::desc("Show the sources across all compilation units." ), |
284 | cat(DwarfDumpCategory)); |
285 | static opt<bool> Verify("verify" , desc("Verify the DWARF debug info." ), |
286 | cat(DwarfDumpCategory)); |
287 | static opt<ErrorDetailLevel> ErrorDetails( |
288 | "error-display" , init(Val: Unspecified), |
289 | desc("Set the level of detail and summary to display when verifying " |
290 | "(implies --verify)" ), |
291 | values(clEnumValN(NoDetailsOrSummary, "quiet" , |
292 | "Only display whether errors occurred." ), |
293 | clEnumValN(NoDetailsOnlySummary, "summary" , |
294 | "Display only a summary of the errors found." ), |
295 | clEnumValN(OnlyDetailsNoSummary, "details" , |
296 | "Display each error in detail but no summary." ), |
297 | clEnumValN(BothDetailsAndSummary, "full" , |
298 | "Display each error as well as a summary. [default]" )), |
299 | cat(DwarfDumpCategory)); |
300 | static opt<std::string> JsonErrSummaryFile( |
301 | "verify-json" , init(Val: "" ), |
302 | desc("Output JSON-formatted error summary to the specified file. " |
303 | "(Implies --verify)" ), |
304 | value_desc("filename.json" ), cat(DwarfDumpCategory)); |
305 | static opt<bool> Quiet("quiet" , desc("Use with -verify to not emit to STDOUT." ), |
306 | cat(DwarfDumpCategory)); |
307 | static opt<bool> DumpUUID("uuid" , desc("Show the UUID for each architecture." ), |
308 | cat(DwarfDumpCategory)); |
309 | static alias DumpUUIDAlias("u" , desc("Alias for --uuid." ), aliasopt(DumpUUID), |
310 | cl::NotHidden); |
311 | static opt<bool> Verbose("verbose" , |
312 | desc("Print more low-level encoding details." ), |
313 | cat(DwarfDumpCategory)); |
314 | static alias VerboseAlias("v" , desc("Alias for --verbose." ), aliasopt(Verbose), |
315 | cat(DwarfDumpCategory), cl::NotHidden); |
316 | static cl::extrahelp |
317 | HelpResponse("\nPass @FILE as argument to read options from FILE.\n" ); |
318 | } // namespace |
319 | /// @} |
320 | //===----------------------------------------------------------------------===// |
321 | |
322 | static void error(Error Err) { |
323 | if (!Err) |
324 | return; |
325 | WithColor::error() << toString(E: std::move(Err)) << "\n" ; |
326 | exit(status: 1); |
327 | } |
328 | |
329 | static void error(StringRef Prefix, Error Err) { |
330 | if (!Err) |
331 | return; |
332 | WithColor::error() << Prefix << ": " << toString(E: std::move(Err)) << "\n" ; |
333 | exit(status: 1); |
334 | } |
335 | |
336 | static void error(StringRef Prefix, std::error_code EC) { |
337 | error(Prefix, Err: errorCodeToError(EC)); |
338 | } |
339 | |
340 | static DIDumpOptions getDumpOpts(DWARFContext &C) { |
341 | DIDumpOptions DumpOpts; |
342 | DumpOpts.DumpType = DumpType; |
343 | DumpOpts.ChildRecurseDepth = ChildRecurseDepth; |
344 | DumpOpts.ParentRecurseDepth = ParentRecurseDepth; |
345 | DumpOpts.ShowAddresses = !Diff; |
346 | DumpOpts.ShowChildren = ShowChildren; |
347 | DumpOpts.ShowParents = ShowParents; |
348 | DumpOpts.ShowForm = ShowForm; |
349 | DumpOpts.SummarizeTypes = SummarizeTypes; |
350 | DumpOpts.Verbose = Verbose; |
351 | DumpOpts.DumpNonSkeleton = DumpNonSkeleton; |
352 | DumpOpts.RecoverableErrorHandler = C.getRecoverableErrorHandler(); |
353 | // In -verify mode, print DIEs without children in error messages. |
354 | if (Verify) { |
355 | DumpOpts.Verbose = ErrorDetails != NoDetailsOnlySummary && |
356 | ErrorDetails != NoDetailsOrSummary; |
357 | DumpOpts.ShowAggregateErrors = ErrorDetails != OnlyDetailsNoSummary && |
358 | ErrorDetails != NoDetailsOnlySummary; |
359 | DumpOpts.JsonErrSummaryFile = JsonErrSummaryFile; |
360 | return DumpOpts.noImplicitRecursion(); |
361 | } |
362 | return DumpOpts; |
363 | } |
364 | |
365 | static uint32_t getCPUType(MachOObjectFile &MachO) { |
366 | if (MachO.is64Bit()) |
367 | return MachO.getHeader64().cputype; |
368 | else |
369 | return MachO.getHeader().cputype; |
370 | } |
371 | |
372 | /// Return true if the object file has not been filtered by an --arch option. |
373 | static bool filterArch(ObjectFile &Obj) { |
374 | if (ArchFilters.empty()) |
375 | return true; |
376 | |
377 | if (auto *MachO = dyn_cast<MachOObjectFile>(Val: &Obj)) { |
378 | for (const StringRef Arch : ArchFilters) { |
379 | // Match architecture number. |
380 | unsigned Value; |
381 | if (!Arch.getAsInteger(Radix: 0, Result&: Value)) |
382 | if (Value == getCPUType(MachO&: *MachO)) |
383 | return true; |
384 | |
385 | // Match as name. |
386 | if (MachO->getArchTriple().getArchName() == Triple(Arch).getArchName()) |
387 | return true; |
388 | } |
389 | } |
390 | return false; |
391 | } |
392 | |
393 | using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, |
394 | const Twine &, raw_ostream &)>; |
395 | |
396 | /// Print only DIEs that have a certain name. |
397 | static bool filterByName( |
398 | const StringSet<> &Names, DWARFDie Die, StringRef NameRef, raw_ostream &OS, |
399 | std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) { |
400 | DIDumpOptions DumpOpts = getDumpOpts(C&: Die.getDwarfUnit()->getContext()); |
401 | DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg; |
402 | std::string Name = |
403 | (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str(); |
404 | if (UseRegex) { |
405 | // Match regular expression. |
406 | for (auto Pattern : Names.keys()) { |
407 | Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags); |
408 | std::string Error; |
409 | if (!RE.isValid(Error)) { |
410 | errs() << "error in regular expression: " << Error << "\n" ; |
411 | exit(status: 1); |
412 | } |
413 | if (RE.match(String: Name)) { |
414 | Die.dump(OS, indent: 0, DumpOpts); |
415 | return true; |
416 | } |
417 | } |
418 | } else if (Names.count(Key: Name)) { |
419 | // Match full text. |
420 | Die.dump(OS, indent: 0, DumpOpts); |
421 | return true; |
422 | } |
423 | return false; |
424 | } |
425 | |
426 | /// Print only DIEs that have a certain name. |
427 | static void filterByName( |
428 | const StringSet<> &Names, DWARFContext::unit_iterator_range CUs, |
429 | raw_ostream &OS, |
430 | std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) { |
431 | auto filterDieNames = [&](DWARFUnit *Unit) { |
432 | for (const auto &Entry : Unit->dies()) { |
433 | DWARFDie Die = {Unit, &Entry}; |
434 | if (const char *Name = Die.getName(Kind: DINameKind::ShortName)) |
435 | if (filterByName(Names, Die, NameRef: Name, OS, GetNameForDWARFReg)) |
436 | continue; |
437 | if (const char *Name = Die.getName(Kind: DINameKind::LinkageName)) |
438 | filterByName(Names, Die, NameRef: Name, OS, GetNameForDWARFReg); |
439 | } |
440 | }; |
441 | for (const auto &CU : CUs) { |
442 | filterDieNames(CU.get()); |
443 | if (DumpNonSkeleton) { |
444 | // If we have split DWARF, then recurse down into the .dwo files as well. |
445 | DWARFDie CUDie = CU->getUnitDIE(ExtractUnitDIEOnly: false); |
446 | DWARFDie CUNonSkeletonDie = CU->getNonSkeletonUnitDIE(ExtractUnitDIEOnly: false); |
447 | // If we have a DWO file, we need to search it as well |
448 | if (CUNonSkeletonDie && CUDie != CUNonSkeletonDie) |
449 | filterDieNames(CUNonSkeletonDie.getDwarfUnit()); |
450 | } |
451 | } |
452 | } |
453 | |
454 | static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel, |
455 | StringRef Name, SmallVectorImpl<DWARFDie> &Dies) { |
456 | for (const auto &Entry : Accel.equal_range(Key: Name)) { |
457 | if (std::optional<uint64_t> Off = Entry.getDIESectionOffset()) { |
458 | if (DWARFDie Die = DICtx.getDIEForOffset(Offset: *Off)) |
459 | Dies.push_back(Elt: Die); |
460 | } |
461 | } |
462 | } |
463 | |
464 | static DWARFDie toDie(const DWARFDebugNames::Entry &Entry, |
465 | DWARFContext &DICtx) { |
466 | std::optional<uint64_t> CUOff = Entry.getCUOffset(); |
467 | std::optional<uint64_t> Off = Entry.getDIEUnitOffset(); |
468 | if (!CUOff || !Off) |
469 | return DWARFDie(); |
470 | |
471 | DWARFCompileUnit *CU = DICtx.getCompileUnitForOffset(Offset: *CUOff); |
472 | if (!CU) |
473 | return DWARFDie(); |
474 | |
475 | if (std::optional<uint64_t> DWOId = CU->getDWOId()) { |
476 | // This is a skeleton unit. Look up the DIE in the DWO unit. |
477 | CU = DICtx.getDWOCompileUnitForHash(Hash: *DWOId); |
478 | if (!CU) |
479 | return DWARFDie(); |
480 | } |
481 | |
482 | return CU->getDIEForOffset(Offset: CU->getOffset() + *Off); |
483 | } |
484 | |
485 | static void getDies(DWARFContext &DICtx, const DWARFDebugNames &Accel, |
486 | StringRef Name, SmallVectorImpl<DWARFDie> &Dies) { |
487 | for (const auto &Entry : Accel.equal_range(Key: Name)) { |
488 | if (DWARFDie Die = toDie(Entry, DICtx)) |
489 | Dies.push_back(Elt: Die); |
490 | } |
491 | } |
492 | |
493 | /// Print only DIEs that have a certain name. |
494 | static void filterByAccelName( |
495 | ArrayRef<std::string> Names, DWARFContext &DICtx, raw_ostream &OS, |
496 | std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) { |
497 | SmallVector<DWARFDie, 4> Dies; |
498 | for (const auto &Name : Names) { |
499 | getDies(DICtx, Accel: DICtx.getAppleNames(), Name, Dies); |
500 | getDies(DICtx, Accel: DICtx.getAppleTypes(), Name, Dies); |
501 | getDies(DICtx, Accel: DICtx.getAppleNamespaces(), Name, Dies); |
502 | getDies(DICtx, Accel: DICtx.getDebugNames(), Name, Dies); |
503 | } |
504 | llvm::sort(C&: Dies); |
505 | Dies.erase(CS: llvm::unique(R&: Dies), CE: Dies.end()); |
506 | |
507 | DIDumpOptions DumpOpts = getDumpOpts(C&: DICtx); |
508 | DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg; |
509 | for (DWARFDie Die : Dies) |
510 | Die.dump(OS, indent: 0, DumpOpts); |
511 | } |
512 | |
513 | /// Print all DIEs in apple accelerator tables |
514 | static void findAllApple( |
515 | DWARFContext &DICtx, raw_ostream &OS, |
516 | std::function<StringRef(uint64_t RegNum, bool IsEH)> GetNameForDWARFReg) { |
517 | MapVector<StringRef, llvm::SmallSet<DWARFDie, 2>> NameToDies; |
518 | |
519 | auto PushDIEs = [&](const AppleAcceleratorTable &Accel) { |
520 | for (const auto &Entry : Accel.entries()) { |
521 | if (std::optional<uint64_t> Off = Entry.BaseEntry.getDIESectionOffset()) { |
522 | std::optional<StringRef> MaybeName = Entry.readName(); |
523 | DWARFDie Die = DICtx.getDIEForOffset(Offset: *Off); |
524 | if (Die && MaybeName) |
525 | NameToDies[*MaybeName].insert(V: Die); |
526 | } |
527 | } |
528 | }; |
529 | |
530 | PushDIEs(DICtx.getAppleNames()); |
531 | PushDIEs(DICtx.getAppleNamespaces()); |
532 | PushDIEs(DICtx.getAppleTypes()); |
533 | |
534 | DIDumpOptions DumpOpts = getDumpOpts(C&: DICtx); |
535 | DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg; |
536 | for (const auto &[Name, Dies] : NameToDies) { |
537 | OS << llvm::formatv(Fmt: "\nApple accelerator entries with name = \"{0}\":\n" , |
538 | Vals: Name); |
539 | for (DWARFDie Die : Dies) |
540 | Die.dump(OS, indent: 0, DumpOpts); |
541 | } |
542 | } |
543 | |
544 | /// Handle the --lookup option and dump the DIEs and line info for the given |
545 | /// address. |
546 | /// TODO: specified Address for --lookup option could relate for several |
547 | /// different sections(in case not-linked object file). llvm-dwarfdump |
548 | /// need to do something with this: extend lookup option with section |
549 | /// information or probably display all matched entries, or something else... |
550 | static bool lookup(ObjectFile &Obj, DWARFContext &DICtx, uint64_t Address, |
551 | raw_ostream &OS) { |
552 | auto DIEsForAddr = DICtx.getDIEsForAddress(Address: Lookup, CheckDWO: DumpNonSkeleton); |
553 | |
554 | if (!DIEsForAddr) |
555 | return false; |
556 | |
557 | DIDumpOptions DumpOpts = getDumpOpts(C&: DICtx); |
558 | DumpOpts.ChildRecurseDepth = 0; |
559 | DIEsForAddr.CompileUnit->dump(OS, DumpOpts); |
560 | if (DIEsForAddr.FunctionDIE) { |
561 | DIEsForAddr.FunctionDIE.dump(OS, indent: 2, DumpOpts); |
562 | if (DIEsForAddr.BlockDIE) |
563 | DIEsForAddr.BlockDIE.dump(OS, indent: 4, DumpOpts); |
564 | } |
565 | |
566 | // TODO: it is neccessary to set proper SectionIndex here. |
567 | // object::SectionedAddress::UndefSection works for only absolute addresses. |
568 | if (DILineInfo LineInfo = DICtx.getLineInfoForAddress( |
569 | Address: {.Address: Lookup, .SectionIndex: object::SectionedAddress::UndefSection})) |
570 | LineInfo.dump(OS); |
571 | |
572 | return true; |
573 | } |
574 | |
575 | // Collect all sources referenced from the given line table, scoped to the given |
576 | // CU compilation directory. |
577 | static bool collectLineTableSources(const DWARFDebugLine::LineTable <, |
578 | StringRef CompDir, |
579 | std::vector<std::string> &Sources) { |
580 | bool Result = true; |
581 | std::optional<uint64_t> LastIndex = LT.getLastValidFileIndex(); |
582 | for (uint64_t I = LT.hasFileAtIndex(FileIndex: 0) ? 0 : 1, |
583 | E = LastIndex ? *LastIndex + 1 : 0; |
584 | I < E; ++I) { |
585 | std::string Path; |
586 | Result &= LT.getFileNameByIndex( |
587 | FileIndex: I, CompDir, Kind: DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, |
588 | Result&: Path); |
589 | Sources.push_back(x: std::move(Path)); |
590 | } |
591 | return Result; |
592 | } |
593 | |
594 | static bool collectObjectSources(ObjectFile &Obj, DWARFContext &DICtx, |
595 | const Twine &Filename, raw_ostream &OS) { |
596 | bool Result = true; |
597 | std::vector<std::string> Sources; |
598 | |
599 | bool HasCompileUnits = false; |
600 | for (const auto &CU : DICtx.compile_units()) { |
601 | HasCompileUnits = true; |
602 | // Extract paths from the line table for this CU. This allows combining the |
603 | // compilation directory with the line information, in case both the include |
604 | // directory and file names in the line table are relative. |
605 | const DWARFDebugLine::LineTable *LT = DICtx.getLineTableForUnit(U: CU.get()); |
606 | StringRef CompDir = CU->getCompilationDir(); |
607 | if (LT) { |
608 | Result &= collectLineTableSources(LT: *LT, CompDir, Sources); |
609 | } else { |
610 | // Since there's no line table for this CU, collect the name from the CU |
611 | // itself. |
612 | const char *Name = CU->getUnitDIE().getShortName(); |
613 | if (!Name) { |
614 | WithColor::warning() |
615 | << Filename << ": missing name for compilation unit\n" ; |
616 | continue; |
617 | } |
618 | SmallString<64> AbsName; |
619 | if (sys::path::is_relative(path: Name, style: sys::path::Style::posix) && |
620 | sys::path::is_relative(path: Name, style: sys::path::Style::windows)) |
621 | AbsName = CompDir; |
622 | sys::path::append(path&: AbsName, a: Name); |
623 | Sources.push_back(x: std::string(AbsName)); |
624 | } |
625 | } |
626 | |
627 | if (!HasCompileUnits) { |
628 | // Since there's no compile units available, walk the line tables and |
629 | // extract out any referenced paths. |
630 | DWARFDataExtractor LineData(DICtx.getDWARFObj(), |
631 | DICtx.getDWARFObj().getLineSection(), |
632 | DICtx.isLittleEndian(), 0); |
633 | DWARFDebugLine::SectionParser Parser(LineData, DICtx, DICtx.normal_units()); |
634 | while (!Parser.done()) { |
635 | const auto RecoverableErrorHandler = [&](Error Err) { |
636 | Result = false; |
637 | WithColor::defaultErrorHandler(Err: std::move(Err)); |
638 | }; |
639 | void (*UnrecoverableErrorHandler)(Error Err) = error; |
640 | |
641 | DWARFDebugLine::LineTable LT = |
642 | Parser.parseNext(RecoverableErrorHandler, UnrecoverableErrorHandler); |
643 | Result &= collectLineTableSources(LT, /*CompDir=*/"" , Sources); |
644 | } |
645 | } |
646 | |
647 | // Dedup and order the sources. |
648 | llvm::sort(C&: Sources); |
649 | Sources.erase(first: llvm::unique(R&: Sources), last: Sources.end()); |
650 | |
651 | for (StringRef Name : Sources) |
652 | OS << Name << "\n" ; |
653 | return Result; |
654 | } |
655 | |
656 | static std::unique_ptr<MCRegisterInfo> |
657 | createRegInfo(const object::ObjectFile &Obj) { |
658 | std::unique_ptr<MCRegisterInfo> MCRegInfo; |
659 | Triple TT; |
660 | TT.setArch(Kind: Triple::ArchType(Obj.getArch())); |
661 | TT.setVendor(Triple::UnknownVendor); |
662 | TT.setOS(Triple::UnknownOS); |
663 | std::string TargetLookupError; |
664 | const Target *TheTarget = |
665 | TargetRegistry::lookupTarget(Triple: TT.str(), Error&: TargetLookupError); |
666 | if (!TargetLookupError.empty()) |
667 | return nullptr; |
668 | MCRegInfo.reset(p: TheTarget->createMCRegInfo(TT: TT.str())); |
669 | return MCRegInfo; |
670 | } |
671 | |
672 | static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, |
673 | const Twine &Filename, raw_ostream &OS) { |
674 | |
675 | auto MCRegInfo = createRegInfo(Obj); |
676 | if (!MCRegInfo) |
677 | logAllUnhandledErrors(E: createStringError(EC: inconvertibleErrorCode(), |
678 | S: "Error in creating MCRegInfo" ), |
679 | OS&: errs(), ErrorBanner: Filename.str() + ": " ); |
680 | |
681 | auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> StringRef { |
682 | if (!MCRegInfo) |
683 | return {}; |
684 | if (std::optional<unsigned> LLVMRegNum = |
685 | MCRegInfo->getLLVMRegNum(RegNum: DwarfRegNum, isEH: IsEH)) |
686 | if (const char *RegName = MCRegInfo->getName(RegNo: *LLVMRegNum)) |
687 | return StringRef(RegName); |
688 | return {}; |
689 | }; |
690 | |
691 | // The UUID dump already contains all the same information. |
692 | if (!(DumpType & DIDT_UUID) || DumpType == DIDT_All) |
693 | OS << Filename << ":\tfile format " << Obj.getFileFormatName() << '\n'; |
694 | |
695 | // Handle the --lookup option. |
696 | if (Lookup) |
697 | return lookup(Obj, DICtx, Address: Lookup, OS); |
698 | |
699 | // Handle the --name option. |
700 | if (!Name.empty()) { |
701 | StringSet<> Names; |
702 | for (const auto &name : Name) |
703 | Names.insert(key: (IgnoreCase && !UseRegex) ? StringRef(name).lower() : name); |
704 | |
705 | filterByName(Names, CUs: DICtx.normal_units(), OS, GetNameForDWARFReg: GetRegName); |
706 | filterByName(Names, CUs: DICtx.dwo_units(), OS, GetNameForDWARFReg: GetRegName); |
707 | return true; |
708 | } |
709 | |
710 | // Handle the --find option and lower it to --debug-info=<offset>. |
711 | if (!Find.empty()) { |
712 | filterByAccelName(Names: Find, DICtx, OS, GetNameForDWARFReg: GetRegName); |
713 | return true; |
714 | } |
715 | |
716 | // Handle the --find-all-apple option and lower it to --debug-info=<offset>. |
717 | if (FindAllApple) { |
718 | findAllApple(DICtx, OS, GetNameForDWARFReg: GetRegName); |
719 | return true; |
720 | } |
721 | |
722 | // Dump the complete DWARF structure. |
723 | auto DumpOpts = getDumpOpts(C&: DICtx); |
724 | DumpOpts.GetNameForDWARFReg = GetRegName; |
725 | DICtx.dump(OS, DumpOpts, DumpOffsets); |
726 | return true; |
727 | } |
728 | |
729 | static bool verifyObjectFile(ObjectFile &Obj, DWARFContext &DICtx, |
730 | const Twine &Filename, raw_ostream &OS) { |
731 | // Verify the DWARF and exit with non-zero exit status if verification |
732 | // fails. |
733 | raw_ostream &stream = Quiet ? nulls() : OS; |
734 | stream << "Verifying " << Filename.str() << ":\tfile format " |
735 | << Obj.getFileFormatName() << "\n" ; |
736 | bool Result = DICtx.verify(OS&: stream, DumpOpts: getDumpOpts(C&: DICtx)); |
737 | if (Result) |
738 | stream << "No errors.\n" ; |
739 | else |
740 | stream << "Errors detected.\n" ; |
741 | return Result; |
742 | } |
743 | |
744 | static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, |
745 | HandlerFn HandleObj, raw_ostream &OS); |
746 | |
747 | static bool handleArchive(StringRef Filename, Archive &Arch, |
748 | HandlerFn HandleObj, raw_ostream &OS) { |
749 | bool Result = true; |
750 | Error Err = Error::success(); |
751 | for (const auto &Child : Arch.children(Err)) { |
752 | auto BuffOrErr = Child.getMemoryBufferRef(); |
753 | error(Prefix: Filename, Err: BuffOrErr.takeError()); |
754 | auto NameOrErr = Child.getName(); |
755 | error(Prefix: Filename, Err: NameOrErr.takeError()); |
756 | std::string Name = (Filename + "(" + NameOrErr.get() + ")" ).str(); |
757 | Result &= handleBuffer(Filename: Name, Buffer: BuffOrErr.get(), HandleObj, OS); |
758 | } |
759 | error(Prefix: Filename, Err: std::move(Err)); |
760 | |
761 | return Result; |
762 | } |
763 | |
764 | static bool handleBuffer(StringRef Filename, MemoryBufferRef Buffer, |
765 | HandlerFn HandleObj, raw_ostream &OS) { |
766 | Expected<std::unique_ptr<Binary>> BinOrErr = object::createBinary(Source: Buffer); |
767 | error(Prefix: Filename, Err: BinOrErr.takeError()); |
768 | |
769 | bool Result = true; |
770 | auto RecoverableErrorHandler = [&](Error E) { |
771 | Result = false; |
772 | WithColor::defaultErrorHandler(Err: std::move(E)); |
773 | }; |
774 | if (auto *Obj = dyn_cast<ObjectFile>(Val: BinOrErr->get())) { |
775 | if (filterArch(Obj&: *Obj)) { |
776 | std::unique_ptr<DWARFContext> DICtx = DWARFContext::create( |
777 | Obj: *Obj, RelocAction: DWARFContext::ProcessDebugRelocations::Process, L: nullptr, DWPName: "" , |
778 | RecoverableErrorHandler); |
779 | DICtx->setParseCUTUIndexManually(ManuallyGenerateUnitIndex); |
780 | if (!HandleObj(*Obj, *DICtx, Filename, OS)) |
781 | Result = false; |
782 | } |
783 | } else if (auto *Fat = dyn_cast<MachOUniversalBinary>(Val: BinOrErr->get())) |
784 | for (auto &ObjForArch : Fat->objects()) { |
785 | std::string ObjName = |
786 | (Filename + "(" + ObjForArch.getArchFlagName() + ")" ).str(); |
787 | if (auto MachOOrErr = ObjForArch.getAsObjectFile()) { |
788 | auto &Obj = **MachOOrErr; |
789 | if (filterArch(Obj)) { |
790 | std::unique_ptr<DWARFContext> DICtx = DWARFContext::create( |
791 | Obj, RelocAction: DWARFContext::ProcessDebugRelocations::Process, L: nullptr, DWPName: "" , |
792 | RecoverableErrorHandler); |
793 | if (!HandleObj(Obj, *DICtx, ObjName, OS)) |
794 | Result = false; |
795 | } |
796 | continue; |
797 | } else |
798 | consumeError(Err: MachOOrErr.takeError()); |
799 | if (auto ArchiveOrErr = ObjForArch.getAsArchive()) { |
800 | error(Prefix: ObjName, Err: ArchiveOrErr.takeError()); |
801 | if (!handleArchive(Filename: ObjName, Arch&: *ArchiveOrErr.get(), HandleObj, OS)) |
802 | Result = false; |
803 | continue; |
804 | } else |
805 | consumeError(Err: ArchiveOrErr.takeError()); |
806 | } |
807 | else if (auto *Arch = dyn_cast<Archive>(Val: BinOrErr->get())) |
808 | Result = handleArchive(Filename, Arch&: *Arch, HandleObj, OS); |
809 | return Result; |
810 | } |
811 | |
812 | static bool handleFile(StringRef Filename, HandlerFn HandleObj, |
813 | raw_ostream &OS) { |
814 | ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr = |
815 | MemoryBuffer::getFileOrSTDIN(Filename); |
816 | error(Prefix: Filename, EC: BuffOrErr.getError()); |
817 | std::unique_ptr<MemoryBuffer> Buffer = std::move(BuffOrErr.get()); |
818 | return handleBuffer(Filename, Buffer: *Buffer, HandleObj, OS); |
819 | } |
820 | |
821 | int main(int argc, char **argv) { |
822 | InitLLVM X(argc, argv); |
823 | |
824 | // Flush outs() when printing to errs(). This avoids interleaving output |
825 | // between the two. |
826 | errs().tie(TieTo: &outs()); |
827 | |
828 | llvm::InitializeAllTargetInfos(); |
829 | llvm::InitializeAllTargetMCs(); |
830 | |
831 | HideUnrelatedOptions( |
832 | Categories: {&DwarfDumpCategory, &SectionCategory, &getColorCategory()}); |
833 | cl::ParseCommandLineOptions( |
834 | argc, argv, |
835 | Overview: "pretty-print DWARF debug information in object files" |
836 | " and debug info archives.\n" ); |
837 | |
838 | // FIXME: Audit interactions between these two options and make them |
839 | // compatible. |
840 | if (Diff && Verbose) { |
841 | WithColor::error() << "incompatible arguments: specifying both -diff and " |
842 | "-verbose is currently not supported" ; |
843 | return 1; |
844 | } |
845 | // -error-detail and -json-summary-file both imply -verify |
846 | if (ErrorDetails != Unspecified || !JsonErrSummaryFile.empty()) { |
847 | Verify = true; |
848 | } |
849 | |
850 | std::error_code EC; |
851 | ToolOutputFile OutputFile(OutputFilename, EC, sys::fs::OF_TextWithCRLF); |
852 | error(Prefix: "unable to open output file " + OutputFilename, EC); |
853 | // Don't remove output file if we exit with an error. |
854 | OutputFile.keep(); |
855 | |
856 | bool OffsetRequested = false; |
857 | |
858 | // Defaults to dumping only debug_info, unless: A) verbose mode is specified, |
859 | // in which case all sections are dumped, or B) a specific section is |
860 | // requested. |
861 | #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ |
862 | if (Dump##ENUM_NAME.IsRequested) { \ |
863 | DumpType |= DIDT_##ENUM_NAME; \ |
864 | if (Dump##ENUM_NAME.HasValue) { \ |
865 | DumpOffsets[DIDT_ID_##ENUM_NAME] = Dump##ENUM_NAME.Val; \ |
866 | OffsetRequested = true; \ |
867 | } \ |
868 | } |
869 | #include "llvm/BinaryFormat/Dwarf.def" |
870 | #undef HANDLE_DWARF_SECTION |
871 | if (DumpUUID) |
872 | DumpType |= DIDT_UUID; |
873 | if (DumpAll) |
874 | DumpType = DIDT_All; |
875 | if (DumpType == DIDT_Null) { |
876 | if (Verbose || Verify) |
877 | DumpType = DIDT_All; |
878 | else |
879 | DumpType = DIDT_DebugInfo; |
880 | } |
881 | |
882 | // Unless dumping a specific DIE, default to --show-children. |
883 | if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && |
884 | Find.empty() && !FindAllApple) |
885 | ShowChildren = true; |
886 | |
887 | // Defaults to a.out if no filenames specified. |
888 | if (InputFilenames.empty()) |
889 | InputFilenames.push_back(value: "a.out" ); |
890 | |
891 | // Expand any .dSYM bundles to the individual object files contained therein. |
892 | std::vector<std::string> Objects; |
893 | for (const auto &F : InputFilenames) { |
894 | if (auto DsymObjectsOrErr = MachOObjectFile::findDsymObjectMembers(Path: F)) { |
895 | if (DsymObjectsOrErr->empty()) |
896 | Objects.push_back(x: F); |
897 | else |
898 | llvm::append_range(C&: Objects, R&: *DsymObjectsOrErr); |
899 | } else { |
900 | error(Err: DsymObjectsOrErr.takeError()); |
901 | } |
902 | } |
903 | |
904 | bool Success = true; |
905 | if (Verify) { |
906 | for (StringRef Object : Objects) |
907 | Success &= handleFile(Filename: Object, HandleObj: verifyObjectFile, OS&: OutputFile.os()); |
908 | } else if (Statistics) { |
909 | for (StringRef Object : Objects) |
910 | Success &= handleFile(Filename: Object, HandleObj: collectStatsForObjectFile, OS&: OutputFile.os()); |
911 | } else if (ShowSectionSizes) { |
912 | for (StringRef Object : Objects) |
913 | Success &= handleFile(Filename: Object, HandleObj: collectObjectSectionSizes, OS&: OutputFile.os()); |
914 | } else if (ShowSources) { |
915 | for (StringRef Object : Objects) |
916 | Success &= handleFile(Filename: Object, HandleObj: collectObjectSources, OS&: OutputFile.os()); |
917 | } else { |
918 | for (StringRef Object : Objects) |
919 | Success &= handleFile(Filename: Object, HandleObj: dumpObjectFile, OS&: OutputFile.os()); |
920 | } |
921 | |
922 | return Success ? EXIT_SUCCESS : EXIT_FAILURE; |
923 | } |
924 | |