dw2-unexpected-entry-pc.exp revision 1.1.1.1 1 # Copyright 2024 Free Software Foundation, Inc.
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16 # Create an inline function which uses DW_AT_ranges and which has a
17 # DW_AT_entry_pc.
18 #
19 # Within the function's ranges, create an empty sub-range, many
20 # versions of gcc (8.x to at least 14.x) do this, and point the
21 # DW_AT_entry_pc at this empty sub-range (at last 8.x to 9.x did
22 # this).
23 #
24 # Now place a breakpoint on the inline function and run to the
25 # breakpoint, check that GDB reports we are inside the inline
26 # function.
27 #
28 # At one point GDB would use the entry-pc value as the breakpoint
29 # location even though that address is not actually associated with
30 # the inline function. Now GDB will reject the entry-pc value and
31 # select a suitable default entry-pc value instead, one which is
32 # associated with the inline function.
33
34 load_lib dwarf.exp
35
36 require dwarf2_support
37
38 standard_testfile
39
40 # This compiles the source file and starts and stops GDB, so run it
41 # before calling prepare_for_testing otherwise GDB will have exited.
42 get_func_info foo
43
44 if { [prepare_for_testing "failed to prepare" ${testfile} \
45 [list ${srcfile}]] } {
46 return
47 }
48
49 if ![runto_main] {
50 return
51 }
52
53 # Some label addresses, needed to match against the output later.
54 foreach foo {foo_1 foo_2 foo_3 foo_4 foo_5 foo_6} {
55 set $foo [get_hexadecimal_valueof "&$foo" "UNKNOWN" \
56 "get address for $foo label"]
57 }
58
59 # Some line numbers needed in the generated DWARF.
60 set foo_decl_line [gdb_get_line_number "foo decl line"]
61 set bar_call_line [gdb_get_line_number "bar call line"]
62
63 if [is_ilp32_target] {
64 set ptr_type "data4"
65 } else {
66 set ptr_type "data8"
67 }
68
69 # Setup the fake DWARF (see comment at top of this file for more
70 # details). Use DWARF_VERSION (either 4 or 5) to select which type of
71 # ranges are created. Compile the source and generated DWARF and run
72 # the test.
73 #
74 # The ENTRY_LABEL is the label to use as the entry-pc value. The
75 # useful choices are 'foo_3', this label is for an empty sub-range,
76 # 'foo_4', this label is within the blocks low/high addresses, but is
77 # not in any sub-range for the block at all, or 'foo_6', this label is
78 # the end address of a non-empty sub-range, and is also the end
79 # address for the whole block.
80 #
81 # The 'foo_4' case is not something that has been seen generated by
82 # any compiler, but it doesn't hurt to test.
83 #
84 # When WITH_LINE_TABLE is true a small snippet of line table will be
85 # generated which covers some parts of the inlined function. This
86 # makes most sense when being tested with the 'foo_6' label, as that
87 # label is all about handling the end of the inline function case.
88
89 proc run_test { entry_label dwarf_version with_line_table } {
90 set dw_testname "${::testfile}-${dwarf_version}-${entry_label}"
91
92 if { $with_line_table } {
93 set dw_testname ${dw_testname}-lt
94 }
95
96 set asm_file [standard_output_file "${dw_testname}.S"]
97 Dwarf::assemble $asm_file {
98 upvar dwarf_version dwarf_version
99 upvar entry_label entry_label
100
101 declare_labels lines_table inline_func ranges_label
102
103 cu { version $dwarf_version } {
104 compile_unit {
105 {producer "gcc"}
106 {language @DW_LANG_C}
107 {name $::srcfile}
108 {comp_dir /tmp}
109 {stmt_list $lines_table DW_FORM_sec_offset}
110 {low_pc 0 addr}
111 } {
112 inline_func: subprogram {
113 {name bar}
114 {inline @DW_INL_declared_inlined}
115 }
116 subprogram {
117 {name foo}
118 {decl_file 1 data1}
119 {decl_line $::foo_decl_line data1}
120 {decl_column 1 data1}
121 {low_pc $::foo_start addr}
122 {high_pc $::foo_len $::ptr_type}
123 {external 1 flag}
124 } {
125 inlined_subroutine {
126 {abstract_origin %$inline_func}
127 {call_file 1 data1}
128 {call_line $::bar_call_line data1}
129 {entry_pc $entry_label addr}
130 {ranges ${ranges_label} DW_FORM_sec_offset}
131 }
132 }
133 }
134 }
135
136 lines {version 2} lines_table {
137 include_dir "$::srcdir/$::subdir"
138 file_name "$::srcfile" 1
139
140 upvar with_line_table with_line_table
141
142 if {$with_line_table} {
143 program {
144 DW_LNE_set_address foo_label
145 line [expr $::bar_call_line - 2]
146 DW_LNS_copy
147
148 DW_LNE_set_address foo_0
149 line [expr $::bar_call_line - 1]
150 DW_LNS_copy
151
152 DW_LNE_set_address foo_1
153 line 1
154 DW_LNS_copy
155
156 DW_LNE_set_address foo_2
157 line 2
158 DW_LNS_copy
159
160 DW_LNE_set_address foo_6
161 line 10
162 DW_LNS_copy
163
164 DW_LNE_set_address foo_6
165 line 10
166 DW_LNS_negate_stmt
167 DW_LNS_copy
168
169 DW_LNE_set_address foo_6
170 line $::bar_call_line
171 DW_LNS_copy
172
173 DW_LNE_set_address "$::foo_start + $::foo_len"
174 DW_LNE_end_sequence
175 }
176 }
177 }
178
179 if { $dwarf_version == 5 } {
180 rnglists {} {
181 table {} {
182 ranges_label: list_ {
183 start_end foo_3 foo_3
184 start_end foo_1 foo_2
185 start_end foo_5 foo_6
186 }
187 }
188 }
189 } else {
190 ranges { } {
191 ranges_label: sequence {
192 range foo_3 foo_3
193 range foo_1 foo_2
194 range foo_5 foo_6
195 }
196 }
197 }
198 }
199
200 if {[prepare_for_testing "failed to prepare" "${dw_testname}" \
201 [list $::srcfile $asm_file] {nodebug}]} {
202 return false
203 }
204
205 if ![runto_main] {
206 return false
207 }
208
209 # Place a breakpoint on `bar` and run to the breakpoint. Use
210 # gdb_test as we want full pattern matching against the stop
211 # location.
212 #
213 # When we have a line table GDB will find a line for the
214 # breakpoint location, so the output will be different.
215 if { $with_line_table } {
216 set re \
217 [multi_line \
218 "Breakpoint $::decimal, bar \\(\\) at \[^\r\n\]+/$::srcfile:1" \
219 "1\\s+\[^\r\n\]+"]
220 } else {
221 set re "Breakpoint $::decimal, $::hex in bar \\(\\)"
222 }
223 gdb_breakpoint bar
224 gdb_test "continue" $re
225
226 # Inspect the block structure of `bar` at this location. We are
227 # expecting that the empty range (that contained the entry-pc) has
228 # been removed from the block, and that the entry-pc has its
229 # default value.
230 gdb_test "maint info blocks" \
231 [multi_line \
232 "\\\[\\(block \\*\\) $::hex\\\] $::foo_1\\.\\.$::foo_6" \
233 " entry pc: $::foo_1" \
234 " inline function: bar" \
235 " symbol count: $::decimal" \
236 " address ranges:" \
237 " $::foo_1\\.\\.$::foo_2" \
238 " $::foo_5\\.\\.$::foo_6"]
239 }
240
241 foreach_with_prefix dwarf_version { 4 5 } {
242 # Test various labels without any line table present.
243 foreach_with_prefix entry_label { foo_3 foo_4 foo_2 foo_6 } {
244 run_test $entry_label $dwarf_version false
245 }
246
247 # Now test what happens if we use the end address of the block,
248 # but also supply a line table. Does GDB do anything different?
249 run_test foo_6 $dwarf_version true
250 }
251