diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h @@ -140,6 +140,10 @@ /// Return the offset into the section where the offset list begins. uint64_t getOffsetBase() const { return getHashBase() + getNumHashes() * 4; } + uint64_t getEntriesBase() const { + return getOffsetBase() + getNumHashes() * 4; + } + /// Return the offset into the section where the I-th offset is. uint64_t getIthOffsetBase(uint32_t I) const { return getOffsetBase() + I * 4; @@ -188,12 +192,13 @@ public: /// Apple-specific implementation of an Accelerator Entry. - class Entry final : public DWARFAcceleratorTable::Entry { + class Entry : public DWARFAcceleratorTable::Entry { const AppleAcceleratorTable &Table; Entry(const AppleAcceleratorTable &Table); void extract(uint64_t *Offset); + virtual ~Entry(){} public: std::optional getCUOffset() const override; @@ -240,6 +245,56 @@ } }; + struct EntryWithName final : Entry { + EntryWithName(const AppleAcceleratorTable &Table) + : Entry(Table), StrOffset(0) {} + + void setOffset(uint32_t NewOffset) { StrOffset = NewOffset; } + std::optional readName() const { + return Table.readStringFromStrSection(StrOffset); + } + + private: + uint32_t StrOffset; + }; + + /// An iterator for all entries in the table. + class Iterator + : public iterator_facade_base { + constexpr static auto EndMarker = std::numeric_limits::max(); + + EntryWithName Current; + uint64_t Offset; + uint32_t NumEntriesToCome; + + void setToEnd() { Offset = EndMarker; } + bool isEnd() const { return Offset == EndMarker; } + + /// Reads the next Entry in the table, populating `Current`. + /// If not possible (e.g. end of the section), becomes the end iterator. + void prepareNextEntryOrEnd(); + + /// Reads the next string pointer and the entry count for that string, + /// populating `NumEntriesToCome`. + /// If not possible (e.g. end of the section), becomes the end iterator. + /// Assumes `Offset` points to a string reference. + void prepareNextStringOrEnd(); + + public: + Iterator(const AppleAcceleratorTable &Table, bool SetEnd = false); + + Iterator &operator++() { + prepareNextEntryOrEnd(); + return *this; + } + bool operator==(const Iterator &It) const { return Offset == It.Offset; } + const EntryWithName &operator*() const { + assert(!isEnd() && "dereferencing end iterator"); + return Current; + } + }; + AppleAcceleratorTable(const DWARFDataExtractor &AccelSection, DataExtractor StringSection) : DWARFAcceleratorTable(AccelSection, StringSection) {} @@ -271,6 +326,11 @@ /// Look up all entries in the accelerator table matching \c Key. iterator_range equal_range(StringRef Key) const; + + /// Lookup all entries in the accelerator table. + auto entries() const { + return make_range(Iterator(*this), Iterator(*this, /*SetEnd*/ true)); + } }; /// .debug_names section consists of one or more units. Each unit starts with a diff --git a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -309,6 +309,44 @@ const AppleAcceleratorTable &AccelTable, uint64_t DataOffset) : Current(AccelTable), Offset(DataOffset) {} +void AppleAcceleratorTable::Iterator::prepareNextEntryOrEnd() { + if (NumEntriesToCome == 0) + prepareNextStringOrEnd(); + if (isEnd()) + return; + uint64_t OffsetCopy = Offset; + Current.extract(&OffsetCopy); + NumEntriesToCome--; + Offset += Current.Table.getHashDataEntryLength(); +} + +void AppleAcceleratorTable::Iterator::prepareNextStringOrEnd() { + std::optional StrOffset = Current.Table.readStringOffsetAt(Offset); + if (!StrOffset) + return setToEnd(); + + // A zero denotes the end of the collision list. Read the next string + // again. + if (*StrOffset == 0) + return prepareNextStringOrEnd(); + Current.setOffset(*StrOffset); + + std::optional MaybeNumEntries = + Current.Table.readU32FromAccel(Offset); + if (!MaybeNumEntries || *MaybeNumEntries == 0) + return setToEnd(); + NumEntriesToCome = *MaybeNumEntries; +} + +AppleAcceleratorTable::Iterator::Iterator(const AppleAcceleratorTable &Table, + bool SetEnd) + : Current(Table), Offset(Table.getEntriesBase()), NumEntriesToCome(0) { + if (SetEnd) + setToEnd(); + else + prepareNextEntryOrEnd(); +} + iterator_range AppleAcceleratorTable::equal_range(StringRef Key) const { const auto EmptyRange = diff --git a/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll b/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll --- a/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll +++ b/llvm/test/DebugInfo/Generic/apple-names-hash-collisions.ll @@ -2,6 +2,7 @@ ; RUN: %llc_dwarf -accel-tables=Apple -filetype=obj -o %t < %s ; RUN: llvm-dwarfdump -apple-names %t | FileCheck %s --check-prefix=NUM_HASHES ; RUN: llvm-dwarfdump --find=bb --find=cA %t | FileCheck %s --check-prefix=FOUND_VARS +; RUN: llvm-dwarfdump --find-all-apple %t | FileCheck %s --check-prefix=ALL_ENTRIES ; The strings 'bb' and 'cA' hash to the same value under the Apple accelerator @@ -9,13 +10,23 @@ ; We first test that there is exactly one bucket and one hash. ; Then we check that both values are found. -; NUM_HASHES: Bucket count: 1 -; NUM_HASHES-NEXT: Hashes count: 1 +; NUM_HASHES: Bucket count: 2 +; NUM_HASHES-NEXT: Hashes count: 2 ; FOUND_VARS: DW_AT_name ("bb") ; FOUND_VARS: DW_AT_name ("cA") +; ALL_ENTRIES: Apple accelerator entries with name = "cA": +; ALL_ENTRIES: DW_AT_name ("cA") +; ALL_ENTRIES: Apple accelerator entries with name = "some_other_hash": +; ALL_ENTRIES: DW_AT_name ("some_other_hash") +; ALL_ENTRIES: Apple accelerator entries with name = "int": +; ALL_ENTRIES: DW_AT_name ("int") +; ALL_ENTRIES: Apple accelerator entries with name = "bb": +; ALL_ENTRIES: DW_AT_name ("bb") + @bb = global i32 200, align 4, !dbg !0 @cA = global i32 10, align 4, !dbg !5 +@some_other_hash = global i32 10, !dbg !17 !llvm.module.flags = !{!9, !10, !11, !12, !13} !llvm.dbg.cu = !{!2} @@ -25,7 +36,7 @@ !1 = distinct !DIGlobalVariable(name: "bb", scope: !2, file: !3, line: 1, type: !7, isDefinition: true) !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "", emissionKind: FullDebug, globals: !4) !3 = !DIFile(filename: "test.cpp", directory: "blah") -!4 = !{!0, !5} +!4 = !{!0, !5, !17} !5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) !6 = distinct !DIGlobalVariable(name: "cA", scope: !2, file: !3, line: 2, type: !7, isLocal: false, isDefinition: true) !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) @@ -35,3 +46,5 @@ !12 = !{i32 8, !"PIC Level", i32 2} !13 = !{i32 7, !"uwtable", i32 1} !15 = !{!"blah"} +!16 = distinct !DIGlobalVariable(name: "some_other_hash", scope: !2, file: !3, line: 2, type: !7, isLocal: false, isDefinition: true) +!17 = !DIGlobalVariableExpression(var: !16, expr: !DIExpression()) diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -12,6 +12,7 @@ #include "llvm-dwarfdump.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" @@ -25,6 +26,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -171,6 +173,10 @@ value_desc("name"), cat(DwarfDumpCategory)); static alias FindAlias("f", desc("Alias for --find."), aliasopt(Find), cl::NotHidden); +static opt FindAllApple( + "find-all-apple", + desc("Print every debug information entry in the accelerator tables."), + cat(DwarfDumpCategory)); static opt IgnoreCase("ignore-case", desc("Ignore case distinctions when using --name."), value_desc("i"), cat(DwarfDumpCategory)); @@ -453,6 +459,37 @@ Die.dump(OS, 0, DumpOpts); } +/// Print all DIEs in apple accelerator tables +static void findAllApple( + DWARFContext &DICtx, raw_ostream &OS, + std::function GetNameForDWARFReg) { + StringMap> NameToDies; + + auto PushDIEs = [&](const AppleAcceleratorTable &Accel) { + for (const auto &Entry : Accel.entries()) { + if (std::optional Off = Entry.getDIESectionOffset()) { + std::optional MaybeName = Entry.readName(); + DWARFDie Die = DICtx.getDIEForOffset(*Off); + if (Die && MaybeName) + NameToDies[*MaybeName].insert(Die); + } + } + }; + + PushDIEs(DICtx.getAppleNames()); + PushDIEs(DICtx.getAppleNamespaces()); + PushDIEs(DICtx.getAppleTypes()); + + DIDumpOptions DumpOpts = getDumpOpts(DICtx); + DumpOpts.GetNameForDWARFReg = GetNameForDWARFReg; + for (const auto &[Name, Dies] : NameToDies) { + OS << llvm::formatv("\nApple accelerator entries with name = \"{0}\":\n", + Name); + for (DWARFDie Die : Dies) + Die.dump(OS, 0, DumpOpts); + } +} + /// Handle the --lookup option and dump the DIEs and line info for the given /// address. /// TODO: specified Address for --lookup option could relate for several @@ -625,6 +662,12 @@ return true; } + // Handle the --find-all-apple option and lower it to --debug-info=. + if (FindAllApple) { + findAllApple(DICtx, OS, GetRegName); + return true; + } + // Dump the complete DWARF structure. auto DumpOpts = getDumpOpts(DICtx); DumpOpts.GetNameForDWARFReg = GetRegName; @@ -782,7 +825,7 @@ // Unless dumping a specific DIE, default to --show-children. if (!ShowChildren && !Verify && !OffsetRequested && Name.empty() && - Find.empty()) + Find.empty() && !FindAllApple) ShowChildren = true; // Defaults to a.out if no filenames specified.