1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
|
# Copyright 2024-2025 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# There was a bug in GCC, which appears to be fixed in version 9 and
# later, where GCC would, in some case, create an invalid
# DW_AT_abstract_origin value.
#
# The bug was that there existed a function which could be inlined,
# and so the DWARF contained a DW_TAG_subprogram describing the
# abstract instance of the function.
#
# For whatever reason, the compiler generated a non-inline instance of
# the function, and so we had a DW_TAG_subprogram with a
# DW_AT_abstract_origin that referenced the abstract instance.
#
# Additionally there was an inlined instance of the function, and so
# we had a DW_TAG_inlined_subroutine with a DW_AT_abstract_origin that
# referenced the abstract instance.
#
# Within the function there was a DW_TAG_lexical_block, which also
# appeared in the abstract instance, and both concrete instances. The
# lexical block also has DW_AT_abstract_origin that should link back
# to the lexical block within the abstract instance.
#
# The bug was that the DW_AT_abstract_origin for the lexical block
# within the inlined instance instead referenced the lexical block
# within the non-inline instance, not within the abstract instance.
#
# The problem this caused is that the non-inline instance defined the
# extents of the lexical block using DW_AT_ranges, while the inline
# instance defined the extend using DW_AT_low_pc and DW_AT_high_pc.
#
# When GDB tried to parse the block ranges for the lexical block for
# the inline function GDB would then find both the DW_AT_ranges and
# the DW_AT_low_pc/DW_AT_high_pc values. This alone is unexpected.
#
# What is worse though, is that the DW_AT_ranges were not within the
# low-pc/high-pc bounds, and this really confused GDB.
#
# The solution is that, when GDB finds blocks with both ranges AND
# low-pc/high-pc information, GDB should only accept the
# low-pc/high-pc information.
#
# Of course, there's no guarantee which of the information is correct,
# but if GDB tries to hold both piece of information, then we end up
# in a non-consistent state, and this triggers assertions.
load_lib dwarf.exp
require dwarf2_support
standard_testfile
# This compiles the source file and starts and stops GDB, so run it
# before calling prepare_for_testing otherwise GDB will have exited.
get_func_info func_a
get_func_info func_b
# Some line numbers needed in the generated DWARF.
set func_a_decl_line [gdb_get_line_number "func_a decl line"]
set func_b_decl_line [gdb_get_line_number "func_b decl line"]
set call_line [gdb_get_line_number "inline func_a call line"]
# See the problem description at the head of this file.
#
# Create the test program, use DWARF_VERSION to decide which format of
# ranges table to generate.
#
# Then run the test program and check that GDB doesn't crash, and
# check that the block structure is as we expect.
proc run_test { dwarf_version } {
set dw_testname "${::testfile}-${dwarf_version}"
set asm_file [standard_output_file "${dw_testname}.S"]
Dwarf::assemble $asm_file {
upvar dwarf_version dwarf_version
upvar entry_label entry_label
declare_labels lines_table foo_func foo_block block_ranges bad_block \
value_label int_label
cu { version $dwarf_version } {
compile_unit {
{producer "GNU C 14.1.0"}
{language @DW_LANG_C}
{name $::srcfile}
{comp_dir /tmp}
{stmt_list $lines_table DW_FORM_sec_offset}
{low_pc 0 addr}
} {
int_label: base_type {
{name "int"}
{byte_size 4 sdata}
{encoding @DW_ATE_signed}
}
foo_func: subprogram {
{name foo}
{inline @DW_INL_declared_inlined}
{decl_file 1 data1}
{decl_line $::func_a_decl_line data1}
} {
foo_block: lexical_block {
} {
value_label: DW_TAG_variable {
{name value}
{type :$int_label}
}
}
}
subprogram {
{abstract_origin %$foo_func}
{low_pc func_a_0 addr}
{high_pc func_a_6 addr}
{external 1 flag}
} {
bad_block: lexical_block {
{abstract_origin %$foo_block}
{ranges $block_ranges DW_FORM_sec_offset}
} {
DW_TAG_variable {
{abstract_origin %$value_label}
{DW_AT_location {
DW_OP_const1u 23
DW_OP_stack_value
} SPECIAL_expr}
}
}
}
subprogram {
{name baz}
{low_pc func_b_0 addr}
{high_pc func_b_5 addr}
{external 1 flag}
} {
inlined_subroutine {
{abstract_origin %$foo_func}
{call_file 1 data1}
{call_line $::call_line data1}
{low_pc func_b_1 addr}
{high_pc func_b_4 addr}
} {
lexical_block {
{abstract_origin %$bad_block}
{low_pc func_b_2 addr}
{high_pc func_b_3 addr}
} {
DW_TAG_variable {
{abstract_origin %$value_label}
{DW_AT_location {
DW_OP_const1u 99
DW_OP_stack_value
} SPECIAL_expr}
}
}
}
}
}
}
lines {version 2} lines_table {
include_dir "$::srcdir/$::subdir"
file_name "$::srcfile" 1
}
if { $dwarf_version == 5 } {
rnglists {} {
table {} {
block_ranges: list_ {
start_end func_a_1 func_a_2
start_end func_a_4 func_a_5
}
}
}
} else {
ranges { } {
block_ranges: sequence {
range func_a_1 func_a_2
range func_a_4 func_a_5
}
}
}
}
if {[prepare_for_testing "failed to prepare" "${dw_testname}" \
[list $::srcfile $asm_file] {nodebug}]} {
return false
}
if {![runto_main]} {
return false
}
# Breakpoint on the inline function `foo'.
gdb_breakpoint foo
# Breakpoint within the lexical block inside of `foo'.
gdb_breakpoint func_a_1
gdb_breakpoint func_b_2
gdb_continue_to_breakpoint "continue to first foo breakpoint"
gdb_continue_to_breakpoint "continue to func_b_2 breakpoint"
gdb_test "print value" " = 99" "print value at func_b_2"
# Some addresses we need to look for in the 'maint info blocks'
# output.
set func_b_0 [get_hexadecimal_valueof "&func_b_0" "*UNKNOWN*"]
set func_b_1 [get_hexadecimal_valueof "&func_b_1" "*UNKNOWN*"]
set func_b_2 [get_hexadecimal_valueof "&func_b_2" "*UNKNOWN*"]
set func_b_3 [get_hexadecimal_valueof "&func_b_3" "*UNKNOWN*"]
set func_b_4 [get_hexadecimal_valueof "&func_b_4" "*UNKNOWN*"]
set func_b_5 [get_hexadecimal_valueof "&func_b_5" "*UNKNOWN*"]
gdb_test "maint info blocks" \
[multi_line \
"\\\[\\(block \\*\\) $::hex\\\] $func_b_0\\.\\.$func_b_5" \
" entry pc: $func_b_0" \
" function: baz" \
" is contiguous" \
"\\\[\\(block \\*\\) $::hex\\\] $func_b_1\\.\\.$func_b_4" \
" entry pc: $func_b_1" \
" inline function: foo" \
" symbol count: $::decimal" \
" is contiguous" \
"\\\[\\(block \\*\\) $::hex\\\] $func_b_2\\.\\.$func_b_3" \
" entry pc: $func_b_2" \
" symbol count: $::decimal" \
" is contiguous"] \
"check block structure at func_b_2"
gdb_continue_to_breakpoint "continue to second foo breakpoint"
gdb_continue_to_breakpoint "continue to func_a_1 breakpoint"
gdb_test "print value" " = 23" "print value at func_a_1"
# Some addresses we need to look for in the 'maint info blocks'
# output.
set func_a_0 [get_hexadecimal_valueof "&func_a_0" "*UNKNOWN*"]
set func_a_1 [get_hexadecimal_valueof "&func_a_1" "*UNKNOWN*"]
set func_a_2 [get_hexadecimal_valueof "&func_a_2" "*UNKNOWN*"]
set func_a_4 [get_hexadecimal_valueof "&func_a_4" "*UNKNOWN*"]
set func_a_5 [get_hexadecimal_valueof "&func_a_5" "*UNKNOWN*"]
set func_a_6 [get_hexadecimal_valueof "&func_a_6" "*UNKNOWN*"]
gdb_test "maint info blocks" \
[multi_line \
"\\\[\\(block \\*\\) $::hex\\\] $func_a_0\\.\\.$func_a_6" \
" entry pc: $func_a_0" \
" function: foo" \
" is contiguous" \
"\\\[\\(block \\*\\) $::hex\\\] $func_a_1\\.\\.$func_a_5" \
" entry pc: $func_a_1" \
" symbol count: $::decimal" \
" address ranges:" \
" $func_a_1\\.\\.$func_a_2" \
" $func_a_4\\.\\.$func_a_5"] \
"check block structure at func_a_1"
}
foreach_with_prefix dwarf_version { 4 5 } {
run_test $dwarf_version
}
|