aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.dwarf2/dw2-unexpected-entry-pc.exp
blob: d8b738d38c77b585c47cb0eea731249c9fd6ba9e (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
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
# 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/>.

# Create an inline function which uses DW_AT_ranges and which has a
# DW_AT_entry_pc.
#
# Within the function's ranges, create an empty sub-range, many
# versions of gcc (8.x to at least 14.x) do this, and point the
# DW_AT_entry_pc at this empty sub-range (at last 8.x to 9.x did
# this).
#
# Now place a breakpoint on the inline function and run to the
# breakpoint, check that GDB reports we are inside the inline
# function.
#
# At one point GDB would use the entry-pc value as the breakpoint
# location even though that address is not actually associated with
# the inline function.  Now GDB will reject the entry-pc value and
# select a suitable default entry-pc value instead, one which is
# associated with the inline function.

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 foo

if { [prepare_for_testing "failed to prepare" ${testfile} \
	  [list ${srcfile}]] } {
    return
}

if ![runto_main] {
    return
}

# Some label addresses, needed to match against the output later.
foreach foo {foo_1 foo_2 foo_3 foo_4 foo_5 foo_6} {
    set $foo [get_hexadecimal_valueof "&$foo" "UNKNOWN" \
		  "get address for $foo label"]
}

# Some line numbers needed in the generated DWARF.
set foo_decl_line [gdb_get_line_number "foo decl line"]
set bar_call_line [gdb_get_line_number "bar call line"]

if [is_ilp32_target] {
    set ptr_type "data4"
} else {
    set ptr_type "data8"
}

# Setup the fake DWARF (see comment at top of this file for more
# details).  Use DWARF_VERSION (either 4 or 5) to select which type of
# ranges are created.  Compile the source and generated DWARF and run
# the test.
#
# The ENTRY_LABEL is the label to use as the entry-pc value.  The
# useful choices are 'foo_3', this label is for an empty sub-range,
# 'foo_4', this label is within the blocks low/high addresses, but is
# not in any sub-range for the block at all, or 'foo_6', this label is
# the end address of a non-empty sub-range, and is also the end
# address for the whole block.
#
# The 'foo_4' case is not something that has been seen generated by
# any compiler, but it doesn't hurt to test.
#
# When WITH_LINE_TABLE is true a small snippet of line table will be
# generated which covers some parts of the inlined function.  This
# makes most sense when being tested with the 'foo_6' label, as that
# label is all about handling the end of the inline function case.

proc run_test { entry_label dwarf_version with_line_table } {
    set dw_testname "${::testfile}-${dwarf_version}-${entry_label}"

    if { $with_line_table } {
	set dw_testname ${dw_testname}-lt
    }

    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 inline_func ranges_label

	cu { version $dwarf_version } {
	    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}
	    } {
		inline_func: subprogram {
		    {name bar}
		    {inline @DW_INL_declared_inlined}
		}
		subprogram {
		    {name foo}
		    {decl_file 1 data1}
		    {decl_line $::foo_decl_line data1}
		    {decl_column 1 data1}
		    {low_pc $::foo_start addr}
		    {high_pc $::foo_len $::ptr_type}
		    {external 1 flag}
		} {
		    inlined_subroutine {
			{abstract_origin %$inline_func}
			{call_file 1 data1}
			{call_line $::bar_call_line data1}
			{entry_pc $entry_label addr}
			{ranges ${ranges_label} DW_FORM_sec_offset}
		    }
		}
	    }
	}

	lines {version 2} lines_table {
	    include_dir "$::srcdir/$::subdir"
	    file_name "$::srcfile" 1

	    upvar with_line_table with_line_table

	    if {$with_line_table} {
		program {
		    DW_LNE_set_address foo_label
		    line [expr $::bar_call_line - 2]
		    DW_LNS_copy

		    DW_LNE_set_address foo_0
		    line [expr $::bar_call_line - 1]
		    DW_LNS_copy

		    DW_LNE_set_address foo_1
		    line 1
		    DW_LNS_copy

		    DW_LNE_set_address foo_2
		    line 2
		    DW_LNS_copy

		    DW_LNE_set_address foo_6
		    line 10
		    DW_LNS_copy

		    DW_LNE_set_address foo_6
		    line 10
		    DW_LNS_negate_stmt
		    DW_LNS_copy

		    DW_LNE_set_address foo_6
		    line $::bar_call_line
		    DW_LNS_copy

		    DW_LNE_set_address "$::foo_start + $::foo_len"
		    DW_LNE_end_sequence
		}
	    }
	}

	if { $dwarf_version == 5 } {
	    rnglists {} {
		table {} {
		    ranges_label: list_ {
			start_end foo_3 foo_3
			start_end foo_1 foo_2
			start_end foo_5 foo_6
		    }
		}
	    }
	} else {
	    ranges { } {
		ranges_label: sequence {
		    range foo_3 foo_3
		    range foo_1 foo_2
		    range foo_5 foo_6
		}
	    }
	}
    }

    if {[prepare_for_testing "failed to prepare" "${dw_testname}" \
	     [list $::srcfile $asm_file] {nodebug}]} {
	return false
    }

    if ![runto_main] {
	return false
    }

    # Place a breakpoint on `bar` and run to the breakpoint.  Use
    # gdb_test as we want full pattern matching against the stop
    # location.
    #
    # When we have a line table GDB will find a line for the
    # breakpoint location, so the output will be different.
    if { $with_line_table } {
	set re \
	    [multi_line \
		 "Breakpoint $::decimal, bar \\(\\) at \[^\r\n\]+/$::srcfile:1" \
		 "1\\s+\[^\r\n\]+"]
    } else {
	set re "Breakpoint $::decimal, $::hex in bar \\(\\)"
    }
    gdb_breakpoint bar
    gdb_test "continue" $re

    # Inspect the block structure of `bar` at this location.  We are
    # expecting that the empty range (that contained the entry-pc) has
    # been removed from the block, and that the entry-pc has its
    # default value.
    gdb_test "maint info blocks" \
	[multi_line \
	     "\\\[\\(block \\*\\) $::hex\\\] $::foo_1\\.\\.$::foo_6" \
	     "  entry pc: $::foo_1" \
	     "  inline function: bar" \
	     "  symbol count: $::decimal" \
	     "  address ranges:" \
	     "    $::foo_1\\.\\.$::foo_2" \
	     "    $::foo_5\\.\\.$::foo_6"]
}

foreach_with_prefix dwarf_version { 4 5 } {
    # Test various labels without any line table present.
    foreach_with_prefix entry_label { foo_3 foo_4 foo_2 foo_6 } {
	run_test $entry_label $dwarf_version false
    }

    # Now test what happens if we use the end address of the block,
    # but also supply a line table.  Does GDB do anything different?
    run_test foo_6 $dwarf_version true
}