Skip to content

Commit e1de85f

Browse files
committed
Add verification for DW_AT_decl_file and DW_AT_call_file.
LTO builds have been creating invalid DWARF and one of the errors was a file index that was out of bounds. "llvm-dwarfdump --verify" will check all file indexes for line tables already, but there are no checks for the validity of file indexes in attributes. The verification will verify if there is a DW_AT_decl_file/DW_AT_call_file that: - there is a line table for the compile unit - the file index is valid - the encoding is appropriate Tests are added that test all of the above conditions. Differential Revision: https://reviews.llvm.org/D84817
1 parent 31137b8 commit e1de85f

File tree

7 files changed

+406
-0
lines changed

7 files changed

+406
-0
lines changed

llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLine.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ class DWARFDebugLine {
121121

122122
bool hasFileAtIndex(uint64_t FileIndex) const;
123123

124+
Optional<uint64_t> getLastValidFileIndex() const;
125+
124126
bool
125127
getFileNameByIndex(uint64_t FileIndex, StringRef CompDir,
126128
DILineInfoSpecifier::FileLineInfoKind Kind,
@@ -251,6 +253,10 @@ class DWARFDebugLine {
251253
return Prologue.hasFileAtIndex(FileIndex);
252254
}
253255

256+
Optional<uint64_t> getLastValidFileIndex() const {
257+
return Prologue.getLastValidFileIndex();
258+
}
259+
254260
/// Extracts filename by its index in filename table in prologue.
255261
/// In Dwarf 4, the files are 1-indexed and the current compilation file
256262
/// name is not represented in the list. In DWARF v5, the files are

llvm/lib/DebugInfo/DWARF/DWARFDebugLine.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ bool DWARFDebugLine::Prologue::hasFileAtIndex(uint64_t FileIndex) const {
7979
return FileIndex != 0 && FileIndex <= FileNames.size();
8080
}
8181

82+
Optional<uint64_t> DWARFDebugLine::Prologue::getLastValidFileIndex() const {
83+
if (FileNames.empty())
84+
return None;
85+
uint16_t DwarfVersion = getVersion();
86+
assert(DwarfVersion != 0 &&
87+
"line table prologue has no dwarf version information");
88+
// In DWARF v5 the file names are 0-indexed.
89+
if (DwarfVersion >= 5)
90+
return FileNames.size() - 1;
91+
return FileNames.size();
92+
}
93+
8294
const llvm::DWARFDebugLine::FileNameEntry &
8395
DWARFDebugLine::Prologue::getFileNameEntry(uint64_t Index) const {
8496
uint16_t DwarfVersion = getVersion();

llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,39 @@ unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die,
538538
}
539539
break;
540540
}
541+
case DW_AT_call_file:
542+
case DW_AT_decl_file: {
543+
if (auto FileIdx = AttrValue.Value.getAsUnsignedConstant()) {
544+
DWARFUnit *U = Die.getDwarfUnit();
545+
const auto *LT = U->getContext().getLineTableForUnit(U);
546+
if (LT) {
547+
if (!LT->hasFileAtIndex(*FileIdx)) {
548+
bool IsZeroIndexed = LT->Prologue.getVersion() >= 5;
549+
if (Optional<uint64_t> LastFileIdx = LT->getLastValidFileIndex()) {
550+
ReportError("DIE has " + AttributeString(Attr) +
551+
" with an invalid file index " +
552+
llvm::formatv("{0}", *FileIdx) +
553+
" (valid values are [" + (IsZeroIndexed ? "0-" : "1-") +
554+
llvm::formatv("{0}", *LastFileIdx) + "])");
555+
} else {
556+
ReportError("DIE has " + AttributeString(Attr) +
557+
" with an invalid file index " +
558+
llvm::formatv("{0}", *FileIdx) +
559+
" (the file table in the prologue is empty)");
560+
}
561+
}
562+
} else {
563+
ReportError("DIE has " + AttributeString(Attr) +
564+
" that references a file with index " +
565+
llvm::formatv("{0}", *FileIdx) +
566+
" and the compile unit has no line table");
567+
}
568+
} else {
569+
ReportError("DIE has " + AttributeString(Attr) +
570+
" with invalid encoding");
571+
}
572+
break;
573+
}
541574
default:
542575
break;
543576
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
2+
3+
# CHECK: error: DIE has DW_AT_decl_file with an invalid file index 2 (valid values are [1-1]){{[[:space:]]}}
4+
# CHECK-NEXT: 0x0000001e: DW_TAG_subprogram
5+
# CHECK-NEXT: DW_AT_name ("main")
6+
# CHECK-NEXT: DW_AT_low_pc (0x0000000000001000)
7+
# CHECK-NEXT: DW_AT_high_pc (0x0000000000002000)
8+
# CHECK-NEXT: DW_AT_decl_file (0x02)
9+
# CHECK-NEXT: DW_AT_call_line (5){{[[:space:]]}}
10+
# CHECK-NEXT: error: DIE has DW_AT_call_file with an invalid file index 3 (valid values are [1-1]){{[[:space:]]}}
11+
# CHECK-NEXT: 0x00000035: DW_TAG_inlined_subroutine
12+
# CHECK-NEXT: DW_AT_name ("inline1")
13+
# CHECK-NEXT: DW_AT_low_pc (0x0000000000001100)
14+
# CHECK-NEXT: DW_AT_high_pc (0x0000000000001200)
15+
# CHECK-NEXT: DW_AT_call_file (0x03)
16+
# CHECK-NEXT: DW_AT_call_line (10){{[[:space:]]}}
17+
18+
# CHECK: Errors detected.
19+
20+
--- !ELF
21+
FileHeader:
22+
Class: ELFCLASS64
23+
Data: ELFDATA2LSB
24+
Type: ET_EXEC
25+
Machine: EM_X86_64
26+
DWARF:
27+
debug_str:
28+
- ''
29+
- '/tmp/main.c'
30+
- main
31+
- inline1
32+
debug_abbrev:
33+
- Code: 0x0000000000000001
34+
Tag: DW_TAG_compile_unit
35+
Children: DW_CHILDREN_yes
36+
Attributes:
37+
- Attribute: DW_AT_name
38+
Form: DW_FORM_strp
39+
- Attribute: DW_AT_language
40+
Form: DW_FORM_data2
41+
- Attribute: DW_AT_low_pc
42+
Form: DW_FORM_addr
43+
- Attribute: DW_AT_stmt_list
44+
Form: DW_FORM_sec_offset
45+
- Code: 0x0000000000000002
46+
Tag: DW_TAG_subprogram
47+
Children: DW_CHILDREN_yes
48+
Attributes:
49+
- Attribute: DW_AT_name
50+
Form: DW_FORM_strp
51+
- Attribute: DW_AT_low_pc
52+
Form: DW_FORM_addr
53+
- Attribute: DW_AT_high_pc
54+
Form: DW_FORM_addr
55+
- Attribute: DW_AT_decl_file
56+
Form: DW_FORM_data1
57+
- Attribute: DW_AT_call_line
58+
Form: DW_FORM_data1
59+
- Code: 0x0000000000000003
60+
Tag: DW_TAG_inlined_subroutine
61+
Children: DW_CHILDREN_no
62+
Attributes:
63+
- Attribute: DW_AT_name
64+
Form: DW_FORM_strp
65+
- Attribute: DW_AT_low_pc
66+
Form: DW_FORM_addr
67+
- Attribute: DW_AT_high_pc
68+
Form: DW_FORM_data4
69+
- Attribute: DW_AT_call_file
70+
Form: DW_FORM_data1
71+
- Attribute: DW_AT_call_line
72+
Form: DW_FORM_data1
73+
debug_info:
74+
- Length: 0x0000000000000046
75+
Version: 4
76+
AbbrOffset: 0x0000000000000000
77+
AddrSize: 8
78+
Entries:
79+
- AbbrCode: 0x00000001
80+
Values:
81+
- Value: 0x0000000000000001
82+
- Value: 0x0000000000000002
83+
- Value: 0x0000000000000000
84+
- Value: 0x0000000000000000
85+
- AbbrCode: 0x00000002
86+
Values:
87+
- Value: 0x000000000000000D
88+
- Value: 0x0000000000001000
89+
- Value: 0x0000000000002000
90+
- Value: 0x0000000000000002
91+
- Value: 0x0000000000000005
92+
- AbbrCode: 0x00000003
93+
Values:
94+
- Value: 0x0000000000000012
95+
- Value: 0x0000000000001100
96+
- Value: 0x0000000000000100
97+
- Value: 0x0000000000000003
98+
- Value: 0x000000000000000A
99+
- AbbrCode: 0x00000000
100+
Values: []
101+
- AbbrCode: 0x00000000
102+
Values: []
103+
debug_line:
104+
- Length: 40
105+
Version: 2
106+
PrologueLength: 34
107+
MinInstLength: 1
108+
DefaultIsStmt: 1
109+
LineBase: 251
110+
LineRange: 14
111+
OpcodeBase: 13
112+
StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
113+
IncludeDirs:
114+
- '/tmp'
115+
Files:
116+
- Name: main.c
117+
DirIdx: 1
118+
ModTime: 0
119+
Length: 0
120+
Opcodes: []
121+
...
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# RUN: yaml2obj %s | not llvm-dwarfdump --verify - | FileCheck %s --implicit-check-not=error:
2+
3+
# CHECK: error: DIE has DW_AT_decl_file with an invalid file index 2 (the file table in the prologue is empty){{[[:space:]]}}
4+
# CHECK-NEXT: 0x0000001e: DW_TAG_subprogram
5+
# CHECK-NEXT: DW_AT_name ("main")
6+
# CHECK-NEXT: DW_AT_low_pc (0x0000000000001000)
7+
# CHECK-NEXT: DW_AT_high_pc (0x0000000000002000)
8+
# CHECK-NEXT: DW_AT_decl_file (0x02)
9+
# CHECK-NEXT: DW_AT_call_line (5){{[[:space:]]}}
10+
# CHECK-NEXT: error: DIE has DW_AT_call_file with an invalid file index 3 (the file table in the prologue is empty){{[[:space:]]}}
11+
# CHECK-NEXT: 0x00000035: DW_TAG_inlined_subroutine
12+
# CHECK-NEXT: DW_AT_name ("inline1")
13+
# CHECK-NEXT: DW_AT_low_pc (0x0000000000001100)
14+
# CHECK-NEXT: DW_AT_high_pc (0x0000000000001200)
15+
# CHECK-NEXT: DW_AT_call_file (0x03)
16+
# CHECK-NEXT: DW_AT_call_line (10){{[[:space:]]}}
17+
18+
# CHECK: Errors detected.
19+
20+
--- !ELF
21+
FileHeader:
22+
Class: ELFCLASS64
23+
Data: ELFDATA2LSB
24+
Type: ET_EXEC
25+
Machine: EM_X86_64
26+
DWARF:
27+
debug_str:
28+
- ''
29+
- '/tmp/main.c'
30+
- main
31+
- inline1
32+
debug_abbrev:
33+
- Code: 0x0000000000000001
34+
Tag: DW_TAG_compile_unit
35+
Children: DW_CHILDREN_yes
36+
Attributes:
37+
- Attribute: DW_AT_name
38+
Form: DW_FORM_strp
39+
- Attribute: DW_AT_language
40+
Form: DW_FORM_data2
41+
- Attribute: DW_AT_low_pc
42+
Form: DW_FORM_addr
43+
- Attribute: DW_AT_stmt_list
44+
Form: DW_FORM_sec_offset
45+
- Code: 0x0000000000000002
46+
Tag: DW_TAG_subprogram
47+
Children: DW_CHILDREN_yes
48+
Attributes:
49+
- Attribute: DW_AT_name
50+
Form: DW_FORM_strp
51+
- Attribute: DW_AT_low_pc
52+
Form: DW_FORM_addr
53+
- Attribute: DW_AT_high_pc
54+
Form: DW_FORM_addr
55+
- Attribute: DW_AT_decl_file
56+
Form: DW_FORM_data1
57+
- Attribute: DW_AT_call_line
58+
Form: DW_FORM_data1
59+
- Code: 0x0000000000000003
60+
Tag: DW_TAG_inlined_subroutine
61+
Children: DW_CHILDREN_no
62+
Attributes:
63+
- Attribute: DW_AT_name
64+
Form: DW_FORM_strp
65+
- Attribute: DW_AT_low_pc
66+
Form: DW_FORM_addr
67+
- Attribute: DW_AT_high_pc
68+
Form: DW_FORM_data4
69+
- Attribute: DW_AT_call_file
70+
Form: DW_FORM_data1
71+
- Attribute: DW_AT_call_line
72+
Form: DW_FORM_data1
73+
debug_info:
74+
- Length: 0x0000000000000046
75+
Version: 4
76+
AbbrOffset: 0x0000000000000000
77+
AddrSize: 8
78+
Entries:
79+
- AbbrCode: 0x00000001
80+
Values:
81+
- Value: 0x0000000000000001
82+
- Value: 0x0000000000000002
83+
- Value: 0x0000000000000000
84+
- Value: 0x0000000000000000
85+
- AbbrCode: 0x00000002
86+
Values:
87+
- Value: 0x000000000000000D
88+
- Value: 0x0000000000001000
89+
- Value: 0x0000000000002000
90+
- Value: 0x0000000000000002
91+
- Value: 0x0000000000000005
92+
- AbbrCode: 0x00000003
93+
Values:
94+
- Value: 0x0000000000000012
95+
- Value: 0x0000000000001100
96+
- Value: 0x0000000000000100
97+
- Value: 0x0000000000000003
98+
- Value: 0x000000000000000A
99+
- AbbrCode: 0x00000000
100+
Values: []
101+
- AbbrCode: 0x00000000
102+
Values: []
103+
debug_line:
104+
- Length: 30
105+
Version: 2
106+
PrologueLength: 24
107+
MinInstLength: 1
108+
DefaultIsStmt: 1
109+
LineBase: 251
110+
LineRange: 14
111+
OpcodeBase: 13
112+
StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
113+
IncludeDirs:
114+
- '/tmp'
115+
Files:
116+
Opcodes: []
117+
...

llvm/test/tools/llvm-dwarfdump/X86/verify_debug_info.s

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@
1111
# CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000003f] = "/Users/sgravani/Development/tests")
1212
# CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
1313
# CHECK-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000016){{[[:space:]]}}
14+
# CHECK-NEXT: error: DIE has DW_AT_decl_file that references a file with index 1 and the compile unit has no line table{{[[:space:]]}}
15+
# CHECK-NEXT: 0x0000002b: DW_TAG_subprogram [2] *
16+
# CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
17+
# CHECK-NEXT: DW_AT_high_pc [DW_FORM_data4] (0x00000016)
18+
# CHECK-NEXT: DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_reg6)
19+
# CHECK-NEXT: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000061] = "main")
20+
# CHECK-NEXT: DW_AT_decl_file [DW_FORM_data1] (0x01)
21+
# CHECK-NEXT: DW_AT_decl_line [DW_FORM_data1] (1)
22+
# CHECK-NEXT: DW_AT_prototyped [DW_FORM_flag_present] (true)
23+
# CHECK-NEXT: DW_AT_type [DW_FORM_ref4] (cu + 0x0052 => {0x00000052} "")
24+
# CHECK-NEXT: DW_AT_external [DW_FORM_flag_present] (true){{[[:space:]]}}
1425
# CHECK-NEXT: error: DIE has DW_AT_type with incompatible tag DW_TAG_null{{[[:space:]]}}
1526
# CHECK-NEXT: 0x0000002b: DW_TAG_subprogram [2] *
1627
# CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
@@ -22,6 +33,13 @@
2233
# CHECK-NEXT: DW_AT_prototyped [DW_FORM_flag_present] (true)
2334
# CHECK-NEXT: DW_AT_type [DW_FORM_ref4] (cu + 0x0052 => {0x00000052} "")
2435
# CHECK-NEXT: DW_AT_external [DW_FORM_flag_present] (true){{[[:space:]]}}
36+
# CHECK-NEXT: error: DIE has DW_AT_decl_file that references a file with index 1 and the compile unit has no line table{{[[:space:]]}}
37+
# CHECK-NEXT: 0x00000044: DW_TAG_variable [3]
38+
# CHECK-NEXT: DW_AT_location [DW_FORM_exprloc] (DW_OP_fbreg -8)
39+
# CHECK-NEXT: DW_AT_name [DW_FORM_strp] ( .debug_str[0x0000006a] = "a")
40+
# CHECK-NEXT: DW_AT_decl_file [DW_FORM_data1] (0x01)
41+
# CHECK-NEXT: DW_AT_decl_line [DW_FORM_data1] (2)
42+
# CHECK-NEXT: DW_AT_use_location [DW_FORM_ref4] (cu + 0x0053 => {0x00000053}){{[[:space:]]}}
2543
# CHECK-NEXT: error: Compilation unit root DIE is not a unit DIE: DW_TAG_null.
2644
# CHECK-NEXT: error: Compilation unit type (DW_UT_compile) and root DIE (DW_TAG_null) do not match.
2745
# CHECK-NEXT: error: Units[2] - start offset: 0x00000068

0 commit comments

Comments
 (0)