Skip to content

Commit ad32576

Browse files
authored
[DWARFVerifier] Allow overlapping ranges for ICF-merged functions (llvm#117952)
This patch modifies the DWARF verifier to handle a valid case where two or more functions have identical address ranges due to being merged by ICF (Identical Code Folding). Previously, the verifier would incorrectly report these as errors, but functions merged via ICF (such as when using LLD's --keep-icf-stabs option) can legitimately share the same address range. A new test case has been added to verify this behavior using YAML-based DWARF data that simulates two DW_TAG_subprogram entries with identical address ranges. The test ensures that the verifier correctly identifies this as a valid case and doesn't emit any errors, while still maintaining the existing verification for truly invalid overlapping ranges in other scenarios. Before this change, the newly added test case would have failed, with `llvm-dwarfdump` marking the overlapping address ranges in the DWARF as an error. We also modify the existing tests `llvm-dwarfutil/ELF/X86/verify.test` and `llvm/test/tools/llvm-dwarfdump/X86/verify_parent_zero_length.yaml` which rely on the existence of the error that we're trying to suppress. We slightly change one offset so that the ranges don't perfectly overlap and an error is still generated.
1 parent de2acda commit ad32576

File tree

5 files changed

+175
-10
lines changed

5 files changed

+175
-10
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ class DWARFVerifier {
6868

6969
/// Inserts the address range. If the range overlaps with an existing
7070
/// range, the range that it overlaps with will be returned and the two
71-
/// address ranges will be unioned together in "Ranges".
71+
/// address ranges will be unioned together in "Ranges". If a duplicate
72+
/// entry is attempted to be added, the duplicate range will not actually be
73+
/// added and the returned iterator will point to end().
7274
///
7375
/// This is used for finding overlapping ranges in the DW_AT_ranges
7476
/// attribute of a DIE. It is also used as a set of address ranges that
@@ -77,7 +79,9 @@ class DWARFVerifier {
7779

7880
/// Inserts the address range info. If any of its ranges overlaps with a
7981
/// range in an existing range info, the range info is *not* added and an
80-
/// iterator to the overlapping range info.
82+
/// iterator to the overlapping range info. If a duplicate entry is
83+
/// attempted to be added, the duplicate range will not actually be added
84+
/// and the returned iterator will point to end().
8185
///
8286
/// This is used for finding overlapping children of the same DIE.
8387
die_range_info_iterator insert(const DieRangeInfo &RI);
@@ -86,7 +90,7 @@ class DWARFVerifier {
8690
bool contains(const DieRangeInfo &RHS) const;
8791

8892
/// Return true if any range in this object intersects with any range in
89-
/// RHS.
93+
/// RHS. Identical ranges are not considered to be intersecting.
9094
bool intersects(const DieRangeInfo &RHS) const;
9195
};
9296

llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) {
5353
auto End = Ranges.end();
5454
auto Pos = std::lower_bound(Begin, End, R);
5555

56+
// Check for exact duplicates which is an allowed special case
57+
if (Pos != End && *Pos == R) {
58+
return std::nullopt;
59+
}
60+
5661
if (Pos != End) {
5762
DWARFAddressRange Range(*Pos);
5863
if (Pos->merge(R))
@@ -113,8 +118,11 @@ bool DWARFVerifier::DieRangeInfo::intersects(const DieRangeInfo &RHS) const {
113118
auto I1 = Ranges.begin(), E1 = Ranges.end();
114119
auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end();
115120
while (I1 != E1 && I2 != E2) {
116-
if (I1->intersects(*I2))
117-
return true;
121+
if (I1->intersects(*I2)) {
122+
// Exact duplicates are allowed
123+
if (!(*I1 == *I2))
124+
return true;
125+
}
118126
if (I1->LowPC < I2->LowPC)
119127
++I1;
120128
else
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#--- comments.txt
2+
3+
# This test verifies several scenarios with DW_TAG_subprogram address ranges:
4+
# 1. Two subprograms can have identical ranges (shown with foo2 and foo3 having same low_pc/high_pc)
5+
# This is valid and can happen when ICF (Identical Code Folding) merges functions.
6+
# 2. Two subprograms can have overlapping ranges when using DW_AT_ranges
7+
# (shown with func1_with_ranges and func2_with_ranges sharing range 0x5000-0x6000)
8+
# This is also valid and can occur with -fbasic-block-sections=all
9+
# 3. The test also verifies that non-identical overlapping ranges are correctly flagged as errors:
10+
# - When modifying just the first range's high offset from 0x6000 to 0x5999, it creates an invalid subrange overlap
11+
# - When modifying just the first instance of DW_AT_high_pc 0x77 to 0x66, it creates an invalid function overlap
12+
# The test ensures llvm-dwarfdump --verify correctly validates these cases by:
13+
# a) Accepting valid identical overlapping ranges
14+
# b) Rejecting invalid non-identical overlapping ranges
15+
16+
# Need to use split-file in order for `sed` calls below to work correctly
17+
# RUN: split-file %s %t
18+
# RUN: yaml2obj %t/test.yaml | llvm-dwarfdump --error-display=details --verify - | FileCheck %s
19+
# CHECK: No errors.
20+
21+
# RUN: sed '0,/HighOffset: 0x6000/{s//HighOffset: 0x5999/}' %t/test.yaml | yaml2obj | not llvm-dwarfdump --error-display=details --verify - | FileCheck %s --check-prefix=CHECK-RANGES
22+
# CHECK-RANGES: error: DIEs have overlapping address ranges
23+
24+
# RUN: sed '0,/Value: 0x77/{s/Value: 0x77/Value: 0x66/}' %t/test.yaml | yaml2obj | not llvm-dwarfdump --error-display=details --verify - | FileCheck %s --check-prefix=CHECK-HIGH-PC
25+
# CHECK-HIGH-PC: error: DIEs have overlapping address ranges
26+
27+
# RUN: sed '0,/LowOffset: 0x880111/{s//LowOffset: 0x880112/}' %t/test.yaml | yaml2obj | not llvm-dwarfdump --error-display=details --verify - | FileCheck %s --check-prefix=CHECK-LEX-BLOCK
28+
# CHECK-LEX-BLOCK: DIE has overlapping ranges in DW_AT_ranges attribute
29+
30+
#--- test.yaml
31+
--- !ELF
32+
FileHeader:
33+
Class: ELFCLASS64
34+
Data: ELFDATA2LSB
35+
Type: ET_REL
36+
Machine: EM_X86_64
37+
DWARF:
38+
debug_abbrev:
39+
- Table:
40+
- Tag: DW_TAG_compile_unit
41+
Children: DW_CHILDREN_yes
42+
Attributes:
43+
- Attribute: DW_AT_producer
44+
Form: DW_FORM_string
45+
- Attribute: DW_AT_language
46+
Form: DW_FORM_data2
47+
- Attribute: DW_AT_name
48+
Form: DW_FORM_string
49+
- Attribute: DW_AT_low_pc
50+
Form: DW_FORM_addr
51+
- Attribute: DW_AT_high_pc
52+
Form: DW_FORM_data8
53+
- Tag: DW_TAG_subprogram
54+
Children: DW_CHILDREN_no
55+
Attributes:
56+
- Attribute: DW_AT_name
57+
Form: DW_FORM_string
58+
- Attribute: DW_AT_low_pc
59+
Form: DW_FORM_addr
60+
- Attribute: DW_AT_high_pc
61+
Form: DW_FORM_data8
62+
- Tag: DW_TAG_subprogram
63+
Children: DW_CHILDREN_no
64+
Attributes:
65+
- Attribute: DW_AT_name
66+
Form: DW_FORM_string
67+
- Attribute: DW_AT_ranges
68+
Form: DW_FORM_sec_offset
69+
- Tag: DW_TAG_base_type
70+
Children: DW_CHILDREN_no
71+
Attributes:
72+
- Attribute: DW_AT_name
73+
Form: DW_FORM_string
74+
- Tag: DW_TAG_lexical_block
75+
Children: DW_CHILDREN_no
76+
Attributes:
77+
- Attribute: DW_AT_ranges
78+
Form: DW_FORM_sec_offset
79+
debug_ranges:
80+
- Offset: 0x0
81+
AddrSize: 0x8
82+
Entries:
83+
- LowOffset: 0x1000
84+
HighOffset: 0x2000
85+
- LowOffset: 0x3000
86+
HighOffset: 0x4000
87+
- LowOffset: 0x5000 # Overlaps with 2nd range below
88+
HighOffset: 0x6000
89+
- LowOffset: 0x0
90+
HighOffset: 0x0
91+
- Offset: 0x50
92+
AddrSize: 0x8
93+
Entries:
94+
- LowOffset: 0x2500
95+
HighOffset: 0x2800
96+
- LowOffset: 0x5000 # Overlaps with 3rd range above
97+
HighOffset: 0x6000
98+
- LowOffset: 0x7000
99+
HighOffset: 0x8000
100+
- LowOffset: 0x0
101+
HighOffset: 0x0
102+
- Offset: 0xA0 # Added Range List #3 for lexical block
103+
AddrSize: 0x8
104+
Entries:
105+
- LowOffset: 0x880111
106+
HighOffset: 0x881222
107+
- LowOffset: 0x882333
108+
HighOffset: 0x883444
109+
- LowOffset: 0x880111 # Overlaps with 1st range in the same list
110+
HighOffset: 0x881222
111+
- LowOffset: 0x0 # End of list
112+
HighOffset: 0x0
113+
debug_info:
114+
- Version: 4
115+
Entries:
116+
- AbbrCode: 1
117+
Values:
118+
- CStr: by_hand
119+
- Value: 0x04
120+
- CStr: CU1
121+
- Value: 0x1000
122+
- Value: 0x100
123+
- AbbrCode: 4
124+
Values:
125+
- CStr: int
126+
- AbbrCode: 2
127+
Values:
128+
- CStr: foo1
129+
- Value: 0x1000
130+
- Value: 0x10
131+
- AbbrCode: 2
132+
Values:
133+
- CStr: foo2
134+
- Value: 0x0 # Overlaps with 'foo3' below
135+
- Value: 0x77
136+
- AbbrCode: 2
137+
Values:
138+
- CStr: foo3
139+
- Value: 0x0 # Overlaps with 'foo2' above
140+
- Value: 0x77
141+
- AbbrCode: 3
142+
Values:
143+
- CStr: func1_with_ranges
144+
- Value: 0x0
145+
- AbbrCode: 3
146+
Values:
147+
- CStr: func2_with_ranges
148+
- Value: 0x50
149+
- AbbrCode: 5 # Added lexical block using ranges
150+
Values:
151+
- Value: 0xA0 # Range list index in debug_ranges
152+
- AbbrCode: 0
153+
...

llvm/test/tools/llvm-dwarfdump/X86/verify_parent_zero_length.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# DW_AT_high_pc (0x0000000000000000)
2020
#
2121
# 0x00000033: DW_TAG_lexical_block
22-
# DW_AT_low_pc (0x0000000000001000)
22+
# DW_AT_low_pc (0x0000000000001001)
2323
# DW_AT_high_pc (0x0000000000002000)
2424
#
2525
# 0x00000044: DW_TAG_lexical_block
@@ -47,7 +47,7 @@
4747

4848
# CHECK: error: DIEs have overlapping address ranges:
4949
# CHECK: 0x00000044: DW_TAG_lexical_block
50-
# CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000001000)
50+
# CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000001001)
5151
# CHECK: DW_AT_high_pc [DW_FORM_addr] (0x0000000000002000)
5252

5353
# CHECK: 0x00000033: DW_TAG_lexical_block
@@ -61,7 +61,7 @@
6161
# CHECK: DW_AT_high_pc [DW_FORM_addr] (0x0000000000000000)
6262

6363
# CHECK: 0x00000044: DW_TAG_lexical_block
64-
# CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000001000)
64+
# CHECK: DW_AT_low_pc [DW_FORM_addr] (0x0000000000001001)
6565
# CHECK: DW_AT_high_pc [DW_FORM_addr] (0x0000000000002000)
6666

6767

@@ -229,7 +229,7 @@ DWARF:
229229
- Value: 0x0000000000002000
230230
- AbbrCode: 0x00000003
231231
Values:
232-
- Value: 0x0000000000001000
232+
- Value: 0x0000000000001001
233233
- Value: 0x0000000000002000
234234
- AbbrCode: 0x00000000
235235
- AbbrCode: 0x00000000

llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ DWARF:
150150
Values:
151151
- CStr: foo3
152152
- Value: 0x0
153-
- Value: 0x100
153+
- Value: 0x80
154154
- Value: 0x00000040
155155
- AbbrCode: 0
156156
...

0 commit comments

Comments
 (0)