Skip to content

Commit 79cd942

Browse files
committedMar 22, 2018
DWARFVerifier: verify debug_names abbreviation table
Summary: This commit adds checks of the abbreviation table in a DWARF v5 Name Index. The most interesting/useful check is the one which checks that each index attributes is encoded using the correct form class, but it also checks for the more obvious errors like unknown forms/tags/attributes and duplicated attributes. Reviewers: JDevlieghere, aprantl, dblaikie Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D44736 llvm-svn: 328202
1 parent fd32767 commit 79cd942

File tree

5 files changed

+190
-3
lines changed

5 files changed

+190
-3
lines changed
 

‎llvm/include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h

+4
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,10 @@ class DWARFDebugNames : public DWARFAcceleratorTable {
425425

426426
uint32_t getNameCount() const { return Hdr.NameCount; }
427427

428+
const DenseSet<Abbrev, AbbrevMapInfo> &getAbbrevs() const {
429+
return Abbrevs;
430+
}
431+
428432
llvm::Error extract();
429433
uint32_t getUnitOffset() const { return Base; }
430434
uint32_t getNextUnitOffset() const { return Base + 4 + Hdr.UnitLength; }

‎llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,17 @@ class DWARFVerifier {
236236
unsigned verifyDebugNamesCULists(const DWARFDebugNames &AccelTable);
237237
unsigned verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
238238
const DataExtractor &StrData);
239+
unsigned verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI);
240+
unsigned verifyNameIndexAttribute(const DWARFDebugNames::NameIndex &NI,
241+
const DWARFDebugNames::Abbrev &Abbr,
242+
DWARFDebugNames::AttributeEncoding AttrEnc);
239243

240244
/// Verify that the DWARF v5 accelerator table is valid.
241245
///
242246
/// This function currently checks that:
243-
/// - Headers and abbreviation tables of individual Name Indices fit into the
244-
/// section and can be parsed.
247+
/// - Headers individual Name Indices fit into the section and can be parsed.
248+
/// - Abbreviation tables can be parsed and contain valid index attributes
249+
/// with correct form encodings.
245250
/// - The CU lists reference existing compile units.
246251
/// - The buckets have a valid index, or they are empty.
247252
/// - All names are reachable via the hash table (they have the correct hash,

‎llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp

+86
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
//===----------------------------------------------------------------------===//
99

1010
#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
11+
#include "llvm/ADT/SmallSet.h"
1112
#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"
1213
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
1314
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
@@ -939,6 +940,89 @@ DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI,
939940
return NumErrors;
940941
}
941942

943+
unsigned DWARFVerifier::verifyNameIndexAttribute(
944+
const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::Abbrev &Abbr,
945+
DWARFDebugNames::AttributeEncoding AttrEnc) {
946+
StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form);
947+
if (FormName.empty()) {
948+
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an "
949+
"unknown form: {3}.\n",
950+
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,
951+
AttrEnc.Form);
952+
return 1;
953+
}
954+
955+
if (AttrEnc.Index == DW_IDX_type_hash) {
956+
if (AttrEnc.Form != dwarf::DW_FORM_data8) {
957+
error() << formatv(
958+
"NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash "
959+
"uses an unexpected form {2} (should be {3}).\n",
960+
NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8);
961+
return 1;
962+
}
963+
}
964+
965+
// A list of known index attributes and their expected form classes.
966+
// DW_IDX_type_hash is handled specially in the check above, as it has a
967+
// specific form (not just a form class) we should expect.
968+
struct FormClassTable {
969+
dwarf::Index Index;
970+
DWARFFormValue::FormClass Class;
971+
StringLiteral ClassName;
972+
};
973+
static constexpr FormClassTable Table[] = {
974+
{dwarf::DW_IDX_compile_unit, DWARFFormValue::FC_Constant, {"constant"}},
975+
{dwarf::DW_IDX_type_unit, DWARFFormValue::FC_Constant, {"constant"}},
976+
{dwarf::DW_IDX_die_offset, DWARFFormValue::FC_Reference, {"reference"}},
977+
{dwarf::DW_IDX_parent, DWARFFormValue::FC_Constant, {"constant"}},
978+
};
979+
980+
ArrayRef<FormClassTable> TableRef(Table);
981+
auto Iter = find_if(TableRef, [AttrEnc](const FormClassTable &T) {
982+
return T.Index == AttrEnc.Index;
983+
});
984+
if (Iter == TableRef.end()) {
985+
warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains an "
986+
"unknown index attribute: {2}.\n",
987+
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index);
988+
return 0;
989+
}
990+
991+
if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) {
992+
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an "
993+
"unexpected form {3} (expected form class {4}).\n",
994+
NI.getUnitOffset(), Abbr.Code, AttrEnc.Index,
995+
AttrEnc.Form, Iter->ClassName);
996+
return 1;
997+
}
998+
return 0;
999+
}
1000+
1001+
unsigned
1002+
DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) {
1003+
unsigned NumErrors = 0;
1004+
for (const auto &Abbrev : NI.getAbbrevs()) {
1005+
StringRef TagName = dwarf::TagString(Abbrev.Tag);
1006+
if (TagName.empty()) {
1007+
warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} references an "
1008+
"unknown tag: {2}.\n",
1009+
NI.getUnitOffset(), Abbrev.Code, Abbrev.Tag);
1010+
}
1011+
SmallSet<unsigned, 5> Attributes;
1012+
for (const auto &AttrEnc : Abbrev.Attributes) {
1013+
if (!Attributes.insert(AttrEnc.Index).second) {
1014+
error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains "
1015+
"multiple {2} attributes.\n",
1016+
NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index);
1017+
++NumErrors;
1018+
continue;
1019+
}
1020+
NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc);
1021+
}
1022+
}
1023+
return NumErrors;
1024+
}
1025+
9421026
unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
9431027
const DataExtractor &StrData) {
9441028
unsigned NumErrors = 0;
@@ -958,6 +1042,8 @@ unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection,
9581042
NumErrors += verifyDebugNamesCULists(AccelTable);
9591043
for (const auto &NI : AccelTable)
9601044
NumErrors += verifyNameIndexBuckets(NI, StrData);
1045+
for (const auto &NI : AccelTable)
1046+
NumErrors += verifyNameIndexAbbrevs(NI);
9611047

9621048
return NumErrors;
9631049
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# RUN: llvm-mc -triple x86_64-pc-linux %s -filetype=obj | \
2+
# RUN: not llvm-dwarfdump -verify - | FileCheck %s
3+
4+
# CHECK: error: NameIndex @ 0x0: Abbreviation 0x2: DW_IDX_compile_unit uses an unexpected form DW_FORM_ref1 (expected form class constant).
5+
# CHECK: error: NameIndex @ 0x0: Abbreviation 0x2: DW_IDX_type_unit uses an unexpected form DW_FORM_ref1 (expected form class constant).
6+
# CHECK: error: NameIndex @ 0x0: Abbreviation 0x2: DW_IDX_type_hash uses an unexpected form DW_FORM_data4 (should be DW_FORM_data8).
7+
# CHECK: warning: NameIndex @ 0x0: Abbreviation 0x2 contains an unknown index attribute: DW_IDX_unknown_2020.
8+
# CHECK: error: NameIndex @ 0x0: Abbreviation 0x4 contains multiple DW_IDX_die_offset attributes.
9+
# CHECK: NameIndex @ 0x0: Abbreviation 0x1: DW_IDX_die_offset uses an unknown form: DW_FORM_unknown_1fff.
10+
# CHECK: warning: NameIndex @ 0x0: Abbreviation 0x3 references an unknown tag: DW_TAG_unknown_8080.
11+
12+
.section .debug_str,"MS",@progbits,1
13+
.Lstring_producer:
14+
.asciz "Hand-written dwarf"
15+
16+
.section .debug_abbrev,"",@progbits
17+
.Lsection_abbrev:
18+
.byte 1 # Abbreviation Code
19+
.byte 17 # DW_TAG_compile_unit
20+
.byte 1 # DW_CHILDREN_yes
21+
.byte 37 # DW_AT_producer
22+
.byte 14 # DW_FORM_strp
23+
.byte 19 # DW_AT_language
24+
.byte 5 # DW_FORM_data2
25+
.byte 0 # EOM(1)
26+
.byte 0 # EOM(2)
27+
.byte 0 # EOM(3)
28+
29+
.section .debug_info,"",@progbits
30+
.Lcu_begin0:
31+
.long .Lcu_end0-.Lcu_start0 # Length of Unit
32+
.Lcu_start0:
33+
.short 4 # DWARF version number
34+
.long .Lsection_abbrev # Offset Into Abbrev. Section
35+
.byte 8 # Address Size (in bytes)
36+
.byte 1 # Abbrev [1] DW_TAG_compile_unit
37+
.long .Lstring_producer # DW_AT_producer
38+
.short 12 # DW_AT_language
39+
.byte 0 # End Of Children Mark
40+
.Lcu_end0:
41+
42+
.section .debug_names,"",@progbits
43+
.long .Lnames_end0-.Lnames_start0 # Header: contribution length
44+
.Lnames_start0:
45+
.short 5 # Header: version
46+
.short 0 # Header: padding
47+
.long 1 # Header: compilation unit count
48+
.long 0 # Header: local type unit count
49+
.long 0 # Header: foreign type unit count
50+
.long 0 # Header: bucket count
51+
.long 0 # Header: name count
52+
.long .Lnames_abbrev_end0-.Lnames_abbrev_start0 # Header: abbreviation table size
53+
.long 0 # Header: augmentation length
54+
.long .Lcu_begin0 # Compilation unit 0
55+
.Lnames_abbrev_start0:
56+
.byte 1 # Abbrev code
57+
.byte 46 # DW_TAG_subprogram
58+
.byte 3 # DW_IDX_die_offset
59+
.uleb128 0x1fff # DW_FORM_unknown_1fff
60+
.byte 0 # End of abbrev
61+
.byte 0 # End of abbrev
62+
.byte 2 # Abbrev code
63+
.byte 46 # DW_TAG_subprogram
64+
.byte 1 # DW_IDX_compile_unit
65+
.byte 17 # DW_FORM_ref1
66+
.byte 2 # DW_IDX_type_unit
67+
.byte 17 # DW_FORM_ref1
68+
.byte 2 # DW_IDX_die_offset
69+
.byte 5 # DW_FORM_data2
70+
.byte 5 # DW_IDX_type_hash
71+
.byte 6 # DW_FORM_data4
72+
.uleb128 0x2020 # DW_IDX_unknown_2020
73+
.byte 6 # DW_FORM_data4
74+
.byte 0 # End of abbrev
75+
.byte 0 # End of abbrev
76+
.byte 3 # Abbrev code
77+
.uleb128 0x8080 # DW_TAG_unknown_8080
78+
.byte 3 # DW_IDX_die_offset
79+
.byte 17 # DW_FORM_ref1
80+
.byte 0 # End of abbrev
81+
.byte 0 # End of abbrev
82+
.byte 4 # Abbrev code
83+
.byte 46 # DW_TAG_subprogram
84+
.byte 3 # DW_IDX_die_offset
85+
.byte 17 # DW_FORM_ref1
86+
.byte 3 # DW_IDX_die_offset
87+
.byte 17 # DW_FORM_ref1
88+
.byte 0 # End of abbrev
89+
.byte 0 # End of abbrev
90+
.byte 0 # End of abbrev list
91+
.Lnames_abbrev_end0:
92+
.Lnames_end0:

‎llvm/test/tools/llvm-dwarfdump/X86/debug-names-verify-no-buckets.s

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
.byte 46 # Abbrev code
6767
.byte 46 # DW_TAG_subprogram
6868
.byte 3 # DW_IDX_die_offset
69-
.byte 6 # DW_FORM_data4
69+
.byte 19 # DW_FORM_ref4
7070
.byte 0 # End of abbrev
7171
.byte 0 # End of abbrev
7272
.byte 0 # End of abbrev list

0 commit comments

Comments
 (0)
Please sign in to comment.