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