Skip to content

Commit

Permalink
[DebugInfo] Support DWARFv5 index sections.
Browse files Browse the repository at this point in the history
DWARFv5 defines index sections in package files in a slightly different
way than the pre-standard GNU proposal, see Section 7.3.5 in the DWARF
standard and https://gcc.gnu.org/wiki/DebugFissionDWP for GNU proposal.
The main concern here is values for section identifiers, which are
partially overlapped with changed meanings. The patch adds support for
v5 index sections and resolves that difficulty by defining a set of
identifiers for internal use which can represent and distinct values
of both standards.

Differential Revision: https://reviews.llvm.org/D75929
  • Loading branch information
igorkudrin committed Apr 6, 2020
1 parent a0249fe commit 714324b
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 24 deletions.
16 changes: 15 additions & 1 deletion llvm/include/llvm/BinaryFormat/Dwarf.def
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -957,6 +961,15 @@ HANDLE_DW_IDX(0x03, die_offset)
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
Expand All @@ -981,3 +994,4 @@ HANDLE_DW_IDX(0x05, type_hash)
#undef HANDLE_DWARF_SECTION
#undef HANDLE_DW_IDX
#undef HANDLE_DW_END
#undef HANDLE_DW_SECT
67 changes: 59 additions & 8 deletions llvm/include/llvm/DebugInfo/DWARF/DWARFUnitIndex.h
Expand Up @@ -19,17 +19,64 @@ namespace llvm {

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;
Expand Down Expand Up @@ -72,6 +119,10 @@ class DWARFUnitIndex {
DWARFSectionKind InfoColumnKind;
int InfoColumn = -1;
std::unique_ptr<DWARFSectionKind[]> 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<uint32_t[]> RawSectionIds;
std::unique_ptr<Entry[]> Rows;
mutable std::vector<Entry *> OffsetLookup;

Expand Down
111 changes: 99 additions & 12 deletions llvm/lib/DebugInfo/DWARF/DWARFUnitIndex.cpp
Expand Up @@ -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<uint32_t>(Kind);
}
assert(IndexVersion == 2);
switch (Kind) {
#define CASE(S,T) \
case DW_SECT_##S: \
return static_cast<uint32_t>(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<DWARFSectionKind>(Value)
: DW_SECT_EXT_unknown;
assert(IndexVersion == 2);
switch (static_cast<DWARFSectionKindV2>(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 {
Expand All @@ -49,6 +132,10 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) {
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))
Expand All @@ -58,6 +145,7 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) {
auto Contribs =
std::make_unique<Entry::SectionContribution *[]>(Header.NumUnits);
ColumnKinds = std::make_unique<DWARFSectionKind[]>(Header.NumColumns);
RawSectionIds = std::make_unique<uint32_t[]>(Header.NumColumns);

// Read Hash Table of Signatures
for (unsigned i = 0; i != Header.NumBuckets; ++i)
Expand All @@ -76,7 +164,8 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) {

// Read the Column Headers
for (unsigned i = 0; i != Header.NumColumns; ++i) {
ColumnKinds[i] = static_cast<DWARFSectionKind>(IndexData.getU32(&Offset));
RawSectionIds[i] = IndexData.getU32(&Offset);
ColumnKinds[i] = deserializeSectionKind(RawSectionIds[i], Header.Version);
if (ColumnKinds[i] == InfoColumnKind) {
if (InfoColumn != -1)
return false;
Expand Down Expand Up @@ -105,23 +194,21 @@ bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) {
}

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 {
Expand All @@ -136,7 +223,7 @@ void DWARFUnitIndex::dump(raw_ostream &OS) const {
if (!Name.empty())
OS << ' ' << left_justify(Name, 24);
else
OS << format(" Unknown: %-15u", static_cast<unsigned>(Kind));
OS << format(" Unknown: %-15" PRIu32, RawSectionIds[i]);
}
OS << "\n----- ------------------";
for (unsigned i = 0; i != Header.NumColumns; ++i)
Expand Down
51 changes: 51 additions & 0 deletions 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
42 changes: 42 additions & 0 deletions 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

0 comments on commit 714324b

Please sign in to comment.