| 12
 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 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
}
 |