diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -22,7 +22,7 @@ (defined HANDLE_DW_CFA && defined HANDLE_DW_CFA_PRED) || \ defined HANDLE_DW_APPLE_PROPERTY || defined HANDLE_DW_UT || \ defined HANDLE_DWARF_SECTION || defined HANDLE_DW_IDX || \ - defined HANDLE_DW_END) + defined HANDLE_DW_END || defined HANDLE_DW_SECT) #error "Missing macro definition of HANDLE_DW*" #endif @@ -128,6 +128,10 @@ #define HANDLE_DW_END(ID, NAME) #endif +#ifndef HANDLE_DW_SECT +#define HANDLE_DW_SECT(ID, NAME) +#endif + HANDLE_DW_TAG(0x0000, null, 2, DWARF, DW_KIND_NONE) HANDLE_DW_TAG(0x0001, array_type, 2, DWARF, DW_KIND_TYPE) HANDLE_DW_TAG(0x0002, class_type, 2, DWARF, DW_KIND_TYPE) @@ -957,6 +961,15 @@ HANDLE_DW_IDX(0x04, parent) HANDLE_DW_IDX(0x05, type_hash) +// DWARF package file section identifiers. +// DWARFv5, section 7.3.5.3, table 7.1. +HANDLE_DW_SECT(1, INFO) +HANDLE_DW_SECT(3, ABBREV) +HANDLE_DW_SECT(4, LINE) +HANDLE_DW_SECT(5, LOCLISTS) +HANDLE_DW_SECT(6, STR_OFFSETS) +HANDLE_DW_SECT(7, MACRO) +HANDLE_DW_SECT(8, RNGLISTS) #undef HANDLE_DW_TAG #undef HANDLE_DW_AT @@ -981,3 +994,4 @@ #undef HANDLE_DWARF_SECTION #undef HANDLE_DW_IDX #undef HANDLE_DW_END +#undef HANDLE_DW_SECT diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnitIndex.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnitIndex.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnitIndex.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnitIndex.h @@ -19,17 +19,64 @@ class raw_ostream; +/// The enum of section identifiers to be used in internal interfaces. +/// +/// Pre-standard implementation of package files defined a number of section +/// identifiers with values that clash definitions in the DWARFv5 standard. +/// See https://gcc.gnu.org/wiki/DebugFissionDWP and Section 7.3.5.3 in DWARFv5. +/// +/// The following identifiers are the same in the proposal and in DWARFv5: +/// - DW_SECT_INFO = 1 (.debug_info.dwo) +/// - DW_SECT_ABBREV = 3 (.debug_abbrev.dwo) +/// - DW_SECT_LINE = 4 (.debug_line.dwo) +/// - DW_SECT_STR_OFFSETS = 6 (.debug_str_offsets.dwo) +/// +/// The following identifiers are defined only in DWARFv5: +/// - DW_SECT_LOCLISTS = 5 (.debug_loclists.dwo) +/// - DW_SECT_RNGLISTS = 8 (.debug_rnglists.dwo) +/// +/// The following identifiers are defined only in the GNU proposal: +/// - DW_SECT_TYPES = 2 (.debug_types.dwo) +/// - DW_SECT_LOC = 5 (.debug_loc.dwo) +/// - DW_SECT_MACINFO = 7 (.debug_macinfo.dwo) +/// +/// DW_SECT_MACRO for the .debug_macro.dwo section is defined in both standards, +/// but with different values, 8 in GNU and 7 in DWARFv5. +/// +/// This enum defines constants to represent the identifiers of both sets. +/// For DWARFv5 ones, the values are the same as defined in the standard. +/// For pre-standard ones that correspond to sections being deprecated in +/// DWARFv5, the values are chosen arbitrary and a tag "_EXT_" is added to +/// the names. +/// +/// The enum is for internal use only. The user should not expect the values +/// to correspond to any input/output constants. Special conversion functions, +/// serializeSectionKind() and deserializeSectionKind(), should be used for +/// the translation. enum DWARFSectionKind { - DW_SECT_INFO = 1, - DW_SECT_EXT_TYPES, - DW_SECT_ABBREV, - DW_SECT_LINE, - DW_SECT_EXT_LOC, - DW_SECT_STR_OFFSETS, - DW_SECT_EXT_MACINFO, - DW_SECT_MACRO, + /// Denotes a value read from an index section that does not correspond + /// to any of the supported standards. + DW_SECT_EXT_unknown = 0, +#define HANDLE_DW_SECT(ID, NAME) DW_SECT_##NAME = ID, +#include "llvm/BinaryFormat/Dwarf.def" + DW_SECT_EXT_TYPES = 2, + DW_SECT_EXT_LOC = 9, + DW_SECT_EXT_MACINFO = 10, }; +/// Convert the internal value for a section kind to an on-disk value. +/// +/// The conversion depends on the version of the index section. +/// IndexVersion is expected to be either 2 for pre-standard GNU proposal +/// or 5 for DWARFv5 package file. +uint32_t serializeSectionKind(DWARFSectionKind Kind, unsigned IndexVersion); + +/// Convert a value read from an index section to the internal representation. +/// +/// The conversion depends on the index section version, which is expected +/// to be either 2 for pre-standard GNU proposal or 5 for DWARFv5 package file. +DWARFSectionKind deserializeSectionKind(uint32_t Value, unsigned IndexVersion); + class DWARFUnitIndex { struct Header { uint32_t Version; @@ -72,6 +119,10 @@ DWARFSectionKind InfoColumnKind; int InfoColumn = -1; std::unique_ptr ColumnKinds; + // This is a parallel array of section identifiers as they read from the input + // file. The mapping from raw values to DWARFSectionKind is not revertable in + // case of unknown identifiers, so we keep them here. + std::unique_ptr RawSectionIds; std::unique_ptr Rows; mutable std::vector OffsetLookup; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp @@ -17,15 +17,98 @@ using namespace llvm; +namespace { + +enum class DWARFSectionKindV2 { + DW_SECT_INFO = 1, + DW_SECT_TYPES = 2, + DW_SECT_ABBREV = 3, + DW_SECT_LINE = 4, + DW_SECT_LOC = 5, + DW_SECT_STR_OFFSETS = 6, + DW_SECT_MACINFO = 7, + DW_SECT_MACRO = 8, +}; + +} // namespace + +// Return true if the section identifier is defined in the DWARFv5 standard. +constexpr bool isKnownV5SectionID(uint32_t ID) { + return ID >= DW_SECT_INFO && ID <= DW_SECT_RNGLISTS && + ID != DW_SECT_EXT_TYPES; +} + +uint32_t llvm::serializeSectionKind(DWARFSectionKind Kind, + unsigned IndexVersion) { + if (IndexVersion == 5) { + assert(isKnownV5SectionID(Kind)); + return static_cast(Kind); + } + assert(IndexVersion == 2); + switch (Kind) { +#define CASE(S,T) \ + case DW_SECT_##S: \ + return static_cast(DWARFSectionKindV2::DW_SECT_##T) + CASE(INFO, INFO); + CASE(EXT_TYPES, TYPES); + CASE(ABBREV, ABBREV); + CASE(LINE, LINE); + CASE(EXT_LOC, LOC); + CASE(STR_OFFSETS, STR_OFFSETS); + CASE(EXT_MACINFO, MACINFO); + CASE(MACRO, MACRO); +#undef CASE + default: + // All other section kinds have no corresponding values in v2 indexes. + llvm_unreachable("Invalid DWARFSectionKind"); + } +} + +DWARFSectionKind llvm::deserializeSectionKind(uint32_t Value, + unsigned IndexVersion) { + if (IndexVersion == 5) + return isKnownV5SectionID(Value) + ? static_cast(Value) + : DW_SECT_EXT_unknown; + assert(IndexVersion == 2); + switch (static_cast(Value)) { +#define CASE(S,T) \ + case DWARFSectionKindV2::DW_SECT_##S: \ + return DW_SECT_##T + CASE(INFO, INFO); + CASE(TYPES, EXT_TYPES); + CASE(ABBREV, ABBREV); + CASE(LINE, LINE); + CASE(LOC, EXT_LOC); + CASE(STR_OFFSETS, STR_OFFSETS); + CASE(MACINFO, EXT_MACINFO); + CASE(MACRO, MACRO); +#undef CASE + } + return DW_SECT_EXT_unknown; +} + bool DWARFUnitIndex::Header::parse(DataExtractor IndexData, uint64_t *OffsetPtr) { + const uint64_t BeginOffset = *OffsetPtr; if (!IndexData.isValidOffsetForDataOfSize(*OffsetPtr, 16)) return false; + // GCC Debug Fission defines the version as an unsigned 32-bit field + // with value of 2, https://gcc.gnu.org/wiki/DebugFissionDWP. + // DWARFv5 defines the same space as an uhalf version field with value of 5 + // and a 2 bytes long padding, see Section 7.3.5.3. Version = IndexData.getU32(OffsetPtr); + if (Version != 2) { + *OffsetPtr = BeginOffset; + Version = IndexData.getU16(OffsetPtr); + if (Version != 5) + return false; + *OffsetPtr += 2; // Skip padding. + } NumColumns = IndexData.getU32(OffsetPtr); NumUnits = IndexData.getU32(OffsetPtr); NumBuckets = IndexData.getU32(OffsetPtr); - return Version <= 2; + return true; } void DWARFUnitIndex::Header::dump(raw_ostream &OS) const { @@ -49,6 +132,10 @@ if (!Header.parse(IndexData, &Offset)) return false; + // Fix InfoColumnKind: in DWARFv5, type units are in .debug_info.dwo. + if (Header.Version == 5) + InfoColumnKind = DW_SECT_INFO; + if (!IndexData.isValidOffsetForDataOfSize( Offset, Header.NumBuckets * (8 + 4) + (2 * Header.NumUnits + 1) * 4 * Header.NumColumns)) @@ -58,6 +145,7 @@ auto Contribs = std::make_unique(Header.NumUnits); ColumnKinds = std::make_unique(Header.NumColumns); + RawSectionIds = std::make_unique(Header.NumColumns); // Read Hash Table of Signatures for (unsigned i = 0; i != Header.NumBuckets; ++i) @@ -76,7 +164,8 @@ // Read the Column Headers for (unsigned i = 0; i != Header.NumColumns; ++i) { - ColumnKinds[i] = static_cast(IndexData.getU32(&Offset)); + RawSectionIds[i] = IndexData.getU32(&Offset); + ColumnKinds[i] = deserializeSectionKind(RawSectionIds[i], Header.Version); if (ColumnKinds[i] == InfoColumnKind) { if (InfoColumn != -1) return false; @@ -105,23 +194,21 @@ } StringRef DWARFUnitIndex::getColumnHeader(DWARFSectionKind DS) { -#define CASE(DS) \ - case DW_SECT_##DS: \ - return #DS; switch (DS) { - CASE(INFO); - CASE(ABBREV); - CASE(LINE); - CASE(STR_OFFSETS); - CASE(MACRO); +#define HANDLE_DW_SECT(ID, NAME) \ + case DW_SECT_##NAME: \ + return #NAME; +#include "llvm/BinaryFormat/Dwarf.def" case DW_SECT_EXT_TYPES: return "TYPES"; case DW_SECT_EXT_LOC: return "LOC"; case DW_SECT_EXT_MACINFO: return "MACINFO"; + case DW_SECT_EXT_unknown: + return StringRef(); } - return StringRef(); + llvm_unreachable("Unknown DWARFSectionKind"); } void DWARFUnitIndex::dump(raw_ostream &OS) const { @@ -136,7 +223,7 @@ if (!Name.empty()) OS << ' ' << left_justify(Name, 24); else - OS << format(" Unknown: %-15u", static_cast(Kind)); + OS << format(" Unknown: %-15" PRIu32, RawSectionIds[i]); } OS << "\n----- ------------------"; for (unsigned i = 0; i != Header.NumColumns; ++i) diff --git a/llvm/test/DebugInfo/X86/dwp-v2-cu-index.s b/llvm/test/DebugInfo/X86/dwp-v2-cu-index.s new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwp-v2-cu-index.s @@ -0,0 +1,51 @@ +## The test checks that we can parse and dump a pre-standard CU index section. +## See https://gcc.gnu.org/wiki/DebugFissionDWP for the proposal. + +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o - | \ +# RUN: llvm-dwarfdump -debug-cu-index - | \ +# RUN: FileCheck %s + +# CHECK: .debug_cu_index contents: +# CHECK-NEXT: version = 2 slots = 2 +# CHECK-EMPTY: +# CHECK-NEXT: Index Signature INFO ABBREV LINE LOC STR_OFFSETS MACINFO MACRO +# CHECK-NEXT: ----- ------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ +# CHECK-NEXT: 1 0x1100001122222222 [0x00001000, 0x00001010) [0x00002000, 0x00002020) [0x00003000, 0x00003030) [0x00004000, 0x00004040) [0x00005000, 0x00005050) [0x00006000, 0x00006060) [0x00007000, 0x00007070) + + .section .debug_cu_index, "", @progbits +## Header: + .long 2 # Version + .long 7 # Section count + .long 1 # Unit count + .long 2 # Slot count +## Hash Table of Signatures: + .quad 0x1100001122222222 + .quad 0 +## Parallel Table of Indexes: + .long 1 + .long 0 +## Table of Section Offsets: +## Row 0: + .long 1 # DW_SECT_INFO + .long 3 # DW_SECT_ABBREV + .long 4 # DW_SECT_LINE + .long 5 # DW_SECT_LOC + .long 6 # DW_SECT_STR_OFFSETS + .long 7 # DW_SECT_MACINFO + .long 8 # DW_SECT_MACRO +## Row 1: + .long 0x1000 # Offset in .debug_info.dwo + .long 0x2000 # Offset in .debug_abbrev.dwo + .long 0x3000 # Offset in .debug_line.dwo + .long 0x4000 # Offset in .debug_loc.dwo + .long 0x5000 # Offset in .debug_str_offsets.dwo + .long 0x6000 # Offset in .debug_macinfo.dwo + .long 0x7000 # Offset in .debug_macro.dwo +## Table of Section Sizes: + .long 0x10 # Size in .debug_info.dwo + .long 0x20 # Size in .debug_abbrev.dwo + .long 0x30 # Size in .debug_line.dwo + .long 0x40 # Size in .debug_loc.dwo + .long 0x50 # Size in .debug_str_offsets.dwo + .long 0x60 # Size in .debug_macinfo.dwo + .long 0x70 # Size in .debug_macro.dwo diff --git a/llvm/test/DebugInfo/X86/dwp-v2-tu-index.s b/llvm/test/DebugInfo/X86/dwp-v2-tu-index.s new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwp-v2-tu-index.s @@ -0,0 +1,42 @@ +## The test checks that we can parse and dump a pre-standard TU index section. +## See https://gcc.gnu.org/wiki/DebugFissionDWP for the proposal. + +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o - | \ +# RUN: llvm-dwarfdump -debug-tu-index - | \ +# RUN: FileCheck %s + +# CHECK: .debug_tu_index contents: +# CHECK-NEXT: version = 2 slots = 2 +# CHECK-EMPTY: +# CHECK-NEXT: Index Signature TYPES ABBREV LINE STR_OFFSETS +# CHECK-NEXT: ----- ------------------ ------------------------ ------------------------ ------------------------ ------------------------ +# CHECK-NEXT: 1 0x1100001122222222 [0x00001000, 0x00001010) [0x00002000, 0x00002020) [0x00003000, 0x00003030) [0x00004000, 0x00004040) + + .section .debug_tu_index, "", @progbits +## Header: + .long 2 # Version + .long 4 # Section count + .long 1 # Unit count + .long 2 # Slot count +## Hash Table of Signatures: + .quad 0x1100001122222222 + .quad 0 +## Parallel Table of Indexes: + .long 1 + .long 0 +## Table of Section Offsets: +## Row 0: + .long 2 # DW_SECT_TYPES + .long 3 # DW_SECT_ABBREV + .long 4 # DW_SECT_LINE + .long 6 # DW_SECT_STR_OFFSETS +## Row 1: + .long 0x1000 # Offset in .debug_types.dwo + .long 0x2000 # Offset in .debug_abbrev.dwo + .long 0x3000 # Offset in .debug_line.dwo + .long 0x4000 # Offset in .debug_str_offsets.dwo +## Table of Section Sizes: + .long 0x10 # Size in .debug_types.dwo + .long 0x20 # Size in .debug_abbrev.dwo + .long 0x30 # Size in .debug_line.dwo + .long 0x40 # Size in .debug_str_offsets.dwo diff --git a/llvm/test/DebugInfo/X86/dwp-v5-cu-index.s b/llvm/test/DebugInfo/X86/dwp-v5-cu-index.s new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwp-v5-cu-index.s @@ -0,0 +1,52 @@ +## The test checks that we can parse and dump a CU index section that is +## compliant to the DWARFv5 standard. + +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o - | \ +# RUN: llvm-dwarfdump -debug-cu-index - | \ +# RUN: FileCheck %s + +# CHECK: .debug_cu_index contents: +# CHECK-NEXT: version = 5 slots = 2 +# CHECK-EMPTY: +# CHECK-NEXT: Index Signature INFO ABBREV LINE LOCLISTS STR_OFFSETS MACRO RNGLISTS +# CHECK-NEXT: ----- ------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ ------------------------ +# CHECK-NEXT: 1 0x1100001122222222 [0x00001000, 0x00001010) [0x00002000, 0x00002020) [0x00003000, 0x00003030) [0x00004000, 0x00004040) [0x00005000, 0x00005050) [0x00006000, 0x00006060) [0x00007000, 0x00007070) + + .section .debug_cu_index, "", @progbits +## Header: + .short 5 # Version + .space 2 # Padding + .long 7 # Section count + .long 1 # Unit count + .long 2 # Slot count +## Hash Table of Signatures: + .quad 0x1100001122222222 + .quad 0 +## Parallel Table of Indexes: + .long 1 + .long 0 +## Table of Section Offsets: +## Row 0: + .long 1 # DW_SECT_INFO + .long 3 # DW_SECT_ABBREV + .long 4 # DW_SECT_LINE + .long 5 # DW_SECT_LOCLISTS + .long 6 # DW_SECT_STR_OFFSETS + .long 7 # DW_SECT_MACRO + .long 8 # DW_SECT_RNGLISTS +## Row 1: + .long 0x1000 # Offset in .debug_info.dwo + .long 0x2000 # Offset in .debug_abbrev.dwo + .long 0x3000 # Offset in .debug_line.dwo + .long 0x4000 # Offset in .debug_loclists.dwo + .long 0x5000 # Offset in .debug_str_offsets.dwo + .long 0x6000 # Offset in .debug_macro.dwo + .long 0x7000 # Offset in .debug_rnglists.dwo +## Table of Section Sizes: + .long 0x10 # Size in .debug_info.dwo + .long 0x20 # Size in .debug_abbrev.dwo + .long 0x30 # Size in .debug_line.dwo + .long 0x40 # Size in .debug_loclists.dwo + .long 0x50 # Size in .debug_str_offsets.dwo + .long 0x60 # Size in .debug_macro.dwo + .long 0x70 # Size in .debug_rnglists.dwo diff --git a/llvm/test/DebugInfo/X86/dwp-v5-tu-index.s b/llvm/test/DebugInfo/X86/dwp-v5-tu-index.s new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwp-v5-tu-index.s @@ -0,0 +1,43 @@ +## The test checks that we can parse and dump a TU index section that is +## compliant to the DWARFv5 standard. + +# RUN: llvm-mc -triple x86_64-unknown-linux %s -filetype=obj -o - | \ +# RUN: llvm-dwarfdump -debug-tu-index - | \ +# RUN: FileCheck %s + +# CHECK: .debug_tu_index contents: +# CHECK-NEXT: version = 5 slots = 2 +# CHECK-EMPTY: +# CHECK-NEXT: Index Signature INFO ABBREV LINE STR_OFFSETS +# CHECK-NEXT: ----- ------------------ ------------------------ ------------------------ ------------------------ ------------------------ +# CHECK-NEXT: 1 0x1100001122222222 [0x00001000, 0x00001010) [0x00002000, 0x00002020) [0x00003000, 0x00003030) [0x00004000, 0x00004040) + + .section .debug_tu_index, "", @progbits +## Header: + .short 5 # Version + .space 2 # Padding + .long 4 # Section count + .long 1 # Unit count + .long 2 # Slot count +## Hash Table of Signatures: + .quad 0x1100001122222222 + .quad 0 +## Parallel Table of Indexes: + .long 1 + .long 0 +## Table of Section Offsets: +## Row 0: + .long 1 # DW_SECT_INFO + .long 3 # DW_SECT_ABBREV + .long 4 # DW_SECT_LINE + .long 6 # DW_SECT_STR_OFFSETS +## Row 1: + .long 0x1000 # Offset in .debug_info.dwo + .long 0x2000 # Offset in .debug_abbrev.dwo + .long 0x3000 # Offset in .debug_line.dwo + .long 0x4000 # Offset in .debug_str_offsets.dwo +## Table of Section Sizes: + .long 0x10 # Size in .debug_info.dwo + .long 0x20 # Size in .debug_abbrev.dwo + .long 0x30 # Size in .debug_line.dwo + .long 0x40 # Size in .debug_str_offsets.dwo diff --git a/llvm/tools/llvm-dwp/llvm-dwp.cpp b/llvm/tools/llvm-dwp/llvm-dwp.cpp --- a/llvm/tools/llvm-dwp/llvm-dwp.cpp +++ b/llvm/tools/llvm-dwp/llvm-dwp.cpp @@ -216,11 +216,12 @@ StringRef DWPName; }; -// Convert a section identifier into the index to use with +// Convert an internal section identifier into the index to use with // UnitIndexEntry::Contributions. static unsigned getContributionIndex(DWARFSectionKind Kind) { - assert(Kind >= DW_SECT_INFO); - return Kind - DW_SECT_INFO; + // Assuming the pre-standard DWP format. + assert(serializeSectionKind(Kind, 2) >= DW_SECT_INFO); + return serializeSectionKind(Kind, 2) - DW_SECT_INFO; } // Convert a UnitIndexEntry::Contributions index to the corresponding on-disk