blob: ea8c7d3e19fbcfa9052114c9f988cee2c9797402 (
plain)
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
|
# 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/>.
# This test creates a function 'foo' that contains an inline function
# 'bar'. Both 'foo' and 'bar' are split into two regions. The layout
# in memory looks something like this:
#
# <part-a> <gap> <part-b>
#
# bar: |------------| |---|
# foo: |------------------| |--------|
#
# We see things like this in "real" code where the parts after the gap
# (part-b) are cold paths within the function that have been moved
# aside.
#
# However, in this test program we use 'goto' to jump directly from
# the first part of the function (part-a) to the second part (part-b).
#
# At the time we hit the goto we are inside 'bar'. After the goto we
# expect to still be in bar. At one point GDB would get this wrong
# and after the jump we would revert back to 'foo'.
#
# This bug will only trigger if both 'foo' and 'bar' are split, and
# both 'foo' and 'bar' must start at the same address for part-b.
load_lib dwarf.exp
require dwarf2_support
# The source program use 'goto *ADDR' which is a GCC extension.
require is_c_compiler_gcc
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 foo
# Make some DWARF for the test.
set asm_file [standard_output_file "$::testfile-dw.S"]
Dwarf::assemble $asm_file {
global srcfile
# Create local varibles BAR_SRC_* containing the line number for
# the four souce lines of 'foo' and 'bar'. These will be
# referenced in the generated DWARF.
for { set i 1 } { $i <= 4 } { incr i } {
set bar_src_$i [gdb_get_line_number "bar line $i"]
set foo_src_$i [gdb_get_line_number "foo line $i"]
}
# More line numbers needed for the generated DWARF.
set foo_decl_line [gdb_get_line_number "foo decl line"]
set bar_decl_line [gdb_get_line_number "bar decl line"]
# Labels used to link parts of the DWARF together.
declare_labels lines_table bar_label ranges_label_bar ranges_label_foo
cu { version 4 } {
compile_unit {
{producer "gcc"}
{language @DW_LANG_C}
{name ${srcfile}}
{comp_dir /tmp}
{stmt_list $lines_table DW_FORM_sec_offset}
{low_pc 0 addr}
} {
bar_label: subprogram {
{external 1 flag}
{name bar}
{decl_file 1 data1}
{decl_line $bar_decl_line data1}
{decl_column 1 data1}
{inline 3 data1}
}
subprogram {
{name foo}
{decl_file 1 data1}
{decl_line $foo_decl_line data1}
{decl_column 1 data1}
{ranges ${ranges_label_foo} DW_FORM_sec_offset}
{external 1 flag}
} {
inlined_subroutine {
{abstract_origin %$bar_label}
{call_file 1 data1}
{call_line $foo_src_3 data1}
{ranges ${ranges_label_bar} DW_FORM_sec_offset}
}
}
}
}
lines {version 2} lines_table {
include_dir "$::srcdir/$::subdir"
file_name "$srcfile" 1
program {
DW_LNE_set_address "foo_label"
line $foo_src_1
DW_LNS_copy
DW_LNE_set_address "foo_label_1"
line $foo_src_2
DW_LNS_copy
DW_LNE_set_address "foo_label_2"
line $foo_src_3
DW_LNS_copy
DW_LNE_set_address "foo_label_2"
line $bar_src_1
DW_LNS_copy
DW_LNE_set_address "foo_label_3"
line $bar_src_2
DW_LNS_copy
DW_LNE_set_address "foo_label_4"
DW_LNE_end_sequence
DW_LNE_set_address "foo_label_6"
line $bar_src_3
DW_LNS_copy
DW_LNE_set_address "foo_label_7"
line $bar_src_4
DW_LNS_copy
DW_LNS_negate_stmt
DW_LNE_set_address "foo_label_7"
line $foo_src_3
DW_LNS_copy
DW_LNS_negate_stmt
DW_LNE_set_address "foo_label_8"
line $foo_src_4
DW_LNS_copy
DW_LNE_set_address $::foo_end
DW_LNE_end_sequence
}
}
ranges { } {
ranges_label_bar: sequence {
range foo_label_2 foo_label_4
range foo_label_6 foo_label_8
}
ranges_label_foo: sequence {
range foo_label_1 foo_label_4
range foo_label_6 foo_label_9
}
}
}
if {[prepare_for_testing "failed to prepare" "${::testfile}" \
[list $srcfile $asm_file] {nodebug}]} {
return -1
}
if ![runto_main] {
return -1
}
gdb_breakpoint bar
gdb_continue_to_breakpoint "continue to bar line 1" \
".*bar line 1\[^\r\n\]+"
gdb_test "step" ".*bar line 2\[^\r\n\]+" \
"step to bar line 2"
# This is the interesting one. This step will take us over the goto
# and into the second range of both 'foo' and 'bar'. As we started
# the step in 'bar' GDB should reselect 'bar' after the step.
#
# If this goes wrong the GDB will claim we are at foo_line_3, which is
# the DW_AT_call_line for 'bar'.
gdb_test "step" ".*bar line 3\[^\r\n\]+" \
"step to bar line 3"
# These following steps should be straight forward, but lets just
# check we can step out of 'bar' and back to 'foo', there shouldn't be
# anything tricky going on here though.
gdb_test "step" ".*bar line 4\[^\r\n\]+" \
"step to bar line 4"
gdb_test "step" ".*foo line 4\[^\r\n\]+" \
"step out of bar to foo line 4"
|