diff --git a/include/llvm/DebugInfo/DIContext.h b/include/llvm/DebugInfo/DIContext.h index 3ef541aa303..3aa098d7c30 100644 --- a/include/llvm/DebugInfo/DIContext.h +++ b/include/llvm/DebugInfo/DIContext.h @@ -107,7 +107,11 @@ enum DIDumpType { DIDT_GnuPubtypes, DIDT_Str, DIDT_StrDwo, - DIDT_StrOffsetsDwo + DIDT_StrOffsetsDwo, + DIDT_AppleNames, + DIDT_AppleTypes, + DIDT_AppleNamespaces, + DIDT_AppleObjC }; // In place of applying the relocations to the data we've read from disk we use diff --git a/lib/DebugInfo/CMakeLists.txt b/lib/DebugInfo/CMakeLists.txt index 61a3fb066d1..81fc84d4a80 100644 --- a/lib/DebugInfo/CMakeLists.txt +++ b/lib/DebugInfo/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_library(LLVMDebugInfo DIContext.cpp DWARFAbbreviationDeclaration.cpp + DWARFAcceleratorTable.cpp DWARFCompileUnit.cpp DWARFContext.cpp DWARFDebugAbbrev.cpp diff --git a/lib/DebugInfo/DWARFAcceleratorTable.cpp b/lib/DebugInfo/DWARFAcceleratorTable.cpp new file mode 100644 index 00000000000..4b844e999eb --- /dev/null +++ b/lib/DebugInfo/DWARFAcceleratorTable.cpp @@ -0,0 +1,116 @@ +#include "DWARFAcceleratorTable.h" + +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +bool DWARFAcceleratorTable::extract() { + uint32_t Offset = 0; + + // Check that we can at least read the header. + if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength)+4)) + return false; + + Hdr.Magic = AccelSection.getU32(&Offset); + Hdr.Version = AccelSection.getU16(&Offset); + Hdr.HashFunction = AccelSection.getU16(&Offset); + Hdr.NumBuckets = AccelSection.getU32(&Offset); + Hdr.NumHashes = AccelSection.getU32(&Offset); + Hdr.HeaderDataLength = AccelSection.getU32(&Offset); + + // Check that we can read all the hashes and offsets from the + // section (see SourceLevelDebugging.rst for the structure of the index). + if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength + + Hdr.NumBuckets*4 + Hdr.NumHashes*8)) + return false; + + HdrData.DIEOffsetBase = AccelSection.getU32(&Offset); + uint32_t NumAtoms = AccelSection.getU32(&Offset); + + for (unsigned i = 0; i < NumAtoms; ++i) { + uint16_t AtomType = AccelSection.getU16(&Offset); + DWARFFormValue AtomForm(AccelSection.getU16(&Offset)); + HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm)); + } + + return true; +} + +void DWARFAcceleratorTable::dump(raw_ostream &OS) { + // Dump the header. + OS << "Magic = " << format("0x%08x", Hdr.Magic) << '\n' + << "Version = " << format("0x%04x", Hdr.Version) << '\n' + << "Hash function = " << format("0x%08x", Hdr.HashFunction) << '\n' + << "Bucket count = " << Hdr.NumBuckets << '\n' + << "Hashes count = " << Hdr.NumHashes << '\n' + << "HeaderData length = " << Hdr.HeaderDataLength << '\n' + << "DIE offset base = " << HdrData.DIEOffsetBase << '\n' + << "Number of atoms = " << HdrData.Atoms.size() << '\n'; + + unsigned i = 0; + for (const auto &Atom: HdrData.Atoms) { + OS << format("Atom[%d] Type: ", i++); + if (const char *TypeString = dwarf::AtomTypeString(Atom.first)) + OS << TypeString; + else + OS << format("DW_ATOM_Unknown_0x%x", Atom.first); + OS << " Form: "; + if (const char *FormString = dwarf::FormEncodingString(Atom.second.getForm())) + OS << FormString; + else + OS << format("DW_FORM_Unknown_0x%x", Atom.second.getForm()); + OS << '\n'; + } + + // Now go through the actual tables and dump them. + uint32_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength; + unsigned HashesBase = Offset + Hdr.NumBuckets * 4; + unsigned OffsetsBase = HashesBase + Hdr.NumHashes * 4; + + for (unsigned Bucket = 0; Bucket < Hdr.NumBuckets; ++Bucket) { + unsigned Index = AccelSection.getU32(&Offset); + + OS << format("Bucket[%d]\n", Bucket); + if (Index == UINT32_MAX) { + OS << " EMPTY\n"; + continue; + } + + for (unsigned HashIdx = Index; HashIdx < Hdr.NumHashes; ++HashIdx) { + unsigned HashOffset = HashesBase + HashIdx*4; + unsigned OffsetsOffset = OffsetsBase + HashIdx*4; + uint32_t Hash = AccelSection.getU32(&HashOffset); + + if (Hash % Hdr.NumBuckets != Bucket) + break; + + unsigned DataOffset = AccelSection.getU32(&OffsetsOffset); + OS << format(" Hash = 0x%08x Offset = 0x%08x\n", Hash, DataOffset); + if (!AccelSection.isValidOffset(DataOffset)) { + OS << " Invalid section offset\n"; + continue; + } + while (unsigned StringOffset = AccelSection.getU32(&DataOffset)) { + OS << format(" Name: %08x \"%s\"\n", StringOffset, + StringSection.getCStr(&StringOffset)); + unsigned NumData = AccelSection.getU32(&DataOffset); + for (unsigned Data = 0; Data < NumData; ++Data) { + OS << format(" Data[%d] => ", Data); + unsigned i = 0; + for (auto &Atom : HdrData.Atoms) { + OS << format("{Atom[%d]: ", i++); + if (Atom.second.extractValue(AccelSection, &DataOffset, nullptr)) + Atom.second.dump(OS, nullptr); + else + OS << "Error extracting the value"; + OS << "} "; + } + OS << '\n'; + } + } + } + } +} +} diff --git a/lib/DebugInfo/DWARFAcceleratorTable.h b/lib/DebugInfo/DWARFAcceleratorTable.h new file mode 100644 index 00000000000..bb25917b47d --- /dev/null +++ b/lib/DebugInfo/DWARFAcceleratorTable.h @@ -0,0 +1,38 @@ + +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/DWARFFormValue.h" + +#include + +namespace llvm { + +class DWARFAcceleratorTable { + + struct Header { + uint32_t Magic; + uint16_t Version; + uint16_t HashFunction; + uint32_t NumBuckets; + uint32_t NumHashes; + uint32_t HeaderDataLength; + }; + + struct HeaderData { + typedef uint16_t AtomType; + uint32_t DIEOffsetBase; + SmallVector, 1> Atoms; + }; + + struct Header Hdr; + struct HeaderData HdrData; + DataExtractor AccelSection; + DataExtractor StringSection; +public: + DWARFAcceleratorTable(DataExtractor AccelSection, DataExtractor StringSection) + : AccelSection(AccelSection), StringSection(StringSection) {} + + bool extract(); + void dump(raw_ostream &OS); +}; + +} diff --git a/lib/DebugInfo/DWARFContext.cpp b/lib/DebugInfo/DWARFContext.cpp index aa86f6aac03..845718d195d 100644 --- a/lib/DebugInfo/DWARFContext.cpp +++ b/lib/DebugInfo/DWARFContext.cpp @@ -9,6 +9,7 @@ #include "DWARFContext.h" #include "DWARFDebugArangeSet.h" +#include "DWARFAcceleratorTable.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -59,6 +60,17 @@ static void dumpPubSection(raw_ostream &OS, StringRef Name, StringRef Data, } } +static void dumpAccelSection(raw_ostream &OS, StringRef Name, StringRef Data, + StringRef StringSection, bool LittleEndian) { + DataExtractor AccelSection(Data, LittleEndian, 0); + DataExtractor StrData(StringSection, LittleEndian, 0); + OS << "\n." << Name << " contents:\n"; + DWARFAcceleratorTable Accel(AccelSection, StrData); + if (!Accel.extract()) + return; + Accel.dump(OS); +} + void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType) { if (DumpType == DIDT_All || DumpType == DIDT_Abbrev) { OS << ".debug_abbrev contents:\n"; @@ -218,6 +230,22 @@ void DWARFContext::dump(raw_ostream &OS, DIDumpType DumpType) { OS << format("%8.8x\n", strOffsetExt.getU32(&offset)); } } + + if (DumpType == DIDT_All || DumpType == DIDT_AppleNames) + dumpAccelSection(OS, "apple_names", getAppleNamesSection(), + getStringSection(), isLittleEndian()); + + if (DumpType == DIDT_All || DumpType == DIDT_AppleTypes) + dumpAccelSection(OS, "apple_types", getAppleTypesSection(), + getStringSection(), isLittleEndian()); + + if (DumpType == DIDT_All || DumpType == DIDT_AppleNamespaces) + dumpAccelSection(OS, "apple_namespaces", getAppleNamespacesSection(), + getStringSection(), isLittleEndian()); + + if (DumpType == DIDT_All || DumpType == DIDT_AppleObjC) + dumpAccelSection(OS, "apple_objc", getAppleObjCSection(), + getStringSection(), isLittleEndian()); } const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { @@ -565,6 +593,11 @@ DWARFContextInMemory::DWARFContextInMemory(const object::ObjectFile &Obj) .Case("debug_str.dwo", &StringDWOSection) .Case("debug_str_offsets.dwo", &StringOffsetDWOSection) .Case("debug_addr", &AddrSection) + .Case("apple_names", &AppleNamesSection) + .Case("apple_types", &AppleTypesSection) + .Case("apple_namespaces", &AppleNamespacesSection) + .Case("apple_namespac", &AppleNamespacesSection) + .Case("apple_objc", &AppleObjCSection) // Any more debug info sections go here. .Default(nullptr); if (SectionData) { diff --git a/lib/DebugInfo/DWARFContext.h b/lib/DebugInfo/DWARFContext.h index c4586b0d4c1..926f7c39bd3 100644 --- a/lib/DebugInfo/DWARFContext.h +++ b/lib/DebugInfo/DWARFContext.h @@ -192,6 +192,10 @@ public: virtual StringRef getStringOffsetDWOSection() = 0; virtual StringRef getRangeDWOSection() = 0; virtual StringRef getAddrSection() = 0; + virtual StringRef getAppleNamesSection() = 0; + virtual StringRef getAppleTypesSection() = 0; + virtual StringRef getAppleNamespacesSection() = 0; + virtual StringRef getAppleObjCSection() = 0; static bool isSupportedVersion(unsigned version) { return version == 2 || version == 3 || version == 4; @@ -236,6 +240,10 @@ class DWARFContextInMemory : public DWARFContext { StringRef StringOffsetDWOSection; StringRef RangeDWOSection; StringRef AddrSection; + StringRef AppleNamesSection; + StringRef AppleTypesSection; + StringRef AppleNamespacesSection; + StringRef AppleObjCSection; SmallVector, 4> UncompressedSections; @@ -256,6 +264,10 @@ public: StringRef getPubTypesSection() override { return PubTypesSection; } StringRef getGnuPubNamesSection() override { return GnuPubNamesSection; } StringRef getGnuPubTypesSection() override { return GnuPubTypesSection; } + StringRef getAppleNamesSection() override { return AppleNamesSection; } + StringRef getAppleTypesSection() override { return AppleTypesSection; } + StringRef getAppleNamespacesSection() override { return AppleNamespacesSection; } + StringRef getAppleObjCSection() override { return AppleObjCSection; } // Sections for DWARF5 split dwarf proposal. const DWARFSection &getInfoDWOSection() override { return InfoDWOSection; } diff --git a/test/DebugInfo/Inputs/gmlt.ll b/test/DebugInfo/Inputs/gmlt.ll index 8de03decd76..ba8d11342ae 100644 --- a/test/DebugInfo/Inputs/gmlt.ll +++ b/test/DebugInfo/Inputs/gmlt.ll @@ -95,6 +95,8 @@ ; CHECK: .debug_pubtypes contents: ; CHECK-NOT: Offset +; CHECK: .apple{{.*}} contents: + ; Function Attrs: nounwind uwtable define void @_Z2f1v() #0 { entry: diff --git a/test/DebugInfo/cross-cu-inlining.ll b/test/DebugInfo/cross-cu-inlining.ll index ea8d90d89e3..f262022eee9 100644 --- a/test/DebugInfo/cross-cu-inlining.ll +++ b/test/DebugInfo/cross-cu-inlining.ll @@ -1,6 +1,7 @@ ; REQUIRES: object-emission ; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-dump=info - | FileCheck -implicit-check-not=DW_TAG %s +; RUN: %llc_dwarf -dwarf-accel-tables=Enable -O0 -filetype=obj < %s | llvm-dwarfdump - | FileCheck --check-prefix=CHECK-ACCEL --check-prefix=CHECK %s ; Build from source: ; $ clang++ a.cpp b.cpp -g -c -emit-llvm @@ -52,6 +53,16 @@ ; CHECK: DW_AT_location ; CHECK: DW_AT_abstract_origin {{.*}} {0x[[ABS_VAR]]} "x" +; Check that both the inline and the non out of line version of func are +; correctly referenced in the accelerator table. Before r221837, the one +; in the second compilation unit had a wrong offset +; CHECK-ACCEL: .apple_names contents: +; CHECK-ACCEL: Name{{.*}}"func" +; CHECK-ACCEL-NOT: Name +; CHECK-ACCEL: Atom[0]{{.*}}[[INLINED]] +; CHECK-ACCEL-NOT: Name +; CHECK-ACCEL: Atom[0]{{.*}}[[FUNC]] + @i = external global i32 ; Function Attrs: uwtable diff --git a/test/DebugInfo/dwarfdump-accel.test b/test/DebugInfo/dwarfdump-accel.test new file mode 100644 index 00000000000..c5c3b0154c1 --- /dev/null +++ b/test/DebugInfo/dwarfdump-accel.test @@ -0,0 +1,63 @@ +RUN: llvm-dwarfdump %p/Inputs/dwarfdump-objc.x86_64.o | FileCheck %s + +Gather some DIE indexes to verify the accelerator table contents. +CHECK: .debug_info contents +CHECK: [[TESTINTERFACE:0x[0-9a-f]*]]:{{.*}}DW_TAG_structure_type +CHECK-NOT: DW_TAG +CHECK: DW_AT_name{{.*}}"TestInterface" +CHECK: [[READONLY:0x[0-9a-f]*]]:{{.*}}DW_TAG_subprogram +CHECK-NOT: DW_TAG +CHECK: DW_AT_name{{.*}}"-[TestInterface ReadOnly]" +CHECK: [[ASSIGN:0x[0-9a-f]*]]:{{.*}}DW_TAG_subprogram +CHECK-NOT: DW_TAG +CHECK: DW_AT_name{{.*}}"-[TestInterface Assign]" +CHECK: [[SETASSIGN:0x[0-9a-f]*]]:{{.*}}DW_TAG_subprogram +CHECK-NOT: DW_TAG +CHECK: DW_AT_name{{.*}}"-[TestInterface setAssign:]" + + +Check that the section header is printed correclty. +CHECK: .apple_names contents: +CHECK: Magic = 0x48415348 +CHECK: Version = 0x0001 +CHECK: Hash function = 0x00000000 +CHECK: Bucket count = 11 +CHECK: Hashes count = 22 +CHECK: HeaderData length = 12 +CHECK: DIE offset base = 0 +CHECK: Number of atoms = 1 +CHECK: Atom[0] Type: DW_ATOM_die_offset Form: DW_FORM_data4 + +Check that empty buckets are handled correctly. +CHECK: Bucket[2] +CHECK: EMPTY +CHECK: Bucket[3] + +Check that the accelerators point to the right DIEs. +CHECK: Name:{{.*}}"-[TestInterface ReadOnly]" +CHECK-NOT: Name +CHECK: {Atom[0]: [[READONLY]]} +CHECK: Name:{{.*}}"-[TestInterface setAssign:]" +CHECK-NOT: Name +CHECK: {Atom[0]: [[SETASSIGN]]} +CHECK: Name:{{.*}}"-[TestInterface Assign]" +CHECK-NOT: Name +CHECK: {Atom[0]: [[ASSIGN]]} + +Check that types are referenced correctly. +CHECK: .apple_types contents: +CHECK: Name{{.*}}"TestInterface" +CHECK-NOT: Name +CHECK: {Atom[0]: [[TESTINTERFACE]]} + +Check that an empty ecceleratorsection is handled correctly. +CHECK: .apple_namespaces contents: +CHECK-NOT: Magic + +Check ObjC specific accelerators. +CHECK: .apple_objc contents: +CHECK: Name{{.*}}"TestInterface" +CHECK-NOT Name +CHECK: {Atom[0]: [[READONLY]]} +CHECK: {Atom[0]: [[ASSIGN]]} +CHECK: {Atom[0]: [[SETASSIGN]]} diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index b4aeb6b8d4a..1c540c98842 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -45,6 +45,10 @@ DumpType("debug-dump", cl::init(DIDT_All), clEnumValN(DIDT_All, "all", "Dump all debug sections"), clEnumValN(DIDT_Abbrev, "abbrev", ".debug_abbrev"), clEnumValN(DIDT_AbbrevDwo, "abbrev.dwo", ".debug_abbrev.dwo"), + clEnumValN(DIDT_AppleNames, "apple_names", ".apple_names"), + clEnumValN(DIDT_AppleTypes, "apple_types", ".apple_types"), + clEnumValN(DIDT_AppleNamespaces, "apple_namespaces", ".apple_namespaces"), + clEnumValN(DIDT_AppleObjC, "apple_objc", ".apple_objc"), clEnumValN(DIDT_Aranges, "aranges", ".debug_aranges"), clEnumValN(DIDT_Info, "info", ".debug_info"), clEnumValN(DIDT_InfoDwo, "info.dwo", ".debug_info.dwo"),