# Copyright 2018-2019 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 . load_lib dwarf.exp # Test DW_AT_ranges in the context of a subprogram scope. # This test can only be run on targets which support DWARF-2 and use gas. if {![dwarf2_support]} { unsupported "dwarf2 support required for this test" return 0 } if [get_compiler_info] { return -1 } if !$gcc_compiled { unsupported "gcc required for this test" return 0 } standard_testfile dw2-ranges-func.c dw2-ranges-func-dw.S # We need to know the size of integer and address types in order to # write some of the debugging info we'd like to generate. # # For that, we ask GDB by debugging our test program. Any program # would do, but since we already have it specifically for this # testcase, might as well use that. if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { return -1 } set asm_file [standard_output_file $srcfile2] Dwarf::assemble $asm_file { global srcdir subdir srcfile srcfile2 declare_labels integer_label volatile_label func_ranges_label cu_ranges_label L set int_size [get_sizeof "int" 4] # Find start address and length for our functions. lassign [function_range main [list ${srcdir}/${subdir}/$srcfile]] \ main_start main_len set main_end "$main_start + $main_len" lassign [function_range foo [list ${srcdir}/${subdir}/$srcfile]] \ foo_start foo_len set foo_end "$foo_start + $foo_len" lassign [function_range foo_low [list ${srcdir}/${subdir}/$srcfile]] \ foo_low_start foo_low_len set foo_low_end "$foo_low_start + $foo_low_len" lassign [function_range bar [list ${srcdir}/${subdir}/$srcfile]] \ bar_start bar_len set bar_end "$bar_start + $bar_len" lassign [function_range baz [list ${srcdir}/${subdir}/$srcfile]] \ baz_start baz_len set baz_end "$baz_start + $baz_len" set e_var [gdb_target_symbol e] cu {} { compile_unit { {language @DW_LANG_C} {name dw-ranges-func.c} {stmt_list $L DW_FORM_sec_offset} {low_pc 0 addr} {ranges ${cu_ranges_label} DW_FORM_sec_offset} } { integer_label: DW_TAG_base_type { {DW_AT_byte_size $int_size DW_FORM_sdata} {DW_AT_encoding @DW_ATE_signed} {DW_AT_name integer} } volatile_label: DW_TAG_volatile_type { {type :$integer_label} } DW_TAG_variable { {name e} {external 1 flag} {type :$volatile_label} {location {addr $e_var} SPECIAL_expr} } subprogram { {external 1 flag} {name main} {DW_AT_type :$integer_label} {low_pc $main_start addr} {high_pc $main_len DW_FORM_data4} } subprogram { {external 1 flag} {name foo} {ranges ${func_ranges_label} DW_FORM_sec_offset} } subprogram { {external 1 flag} {name bar} {low_pc $bar_start addr} {high_pc $bar_len DW_FORM_data4} } subprogram { {external 1 flag} {name baz} {low_pc $baz_start addr} {high_pc $baz_len DW_FORM_data4} } } } lines {version 2} L { include_dir "${srcdir}/${subdir}" file_name "$srcfile" 1 # Generate a line table program. An attempt was made to make it # reasonably accurate as it made debugging the test case easier. program { {DW_LNE_set_address $main_start} {DW_LNS_advance_line [expr [gdb_get_line_number "main prologue"] - 1]} {DW_LNS_copy} {DW_LNE_set_address main_label} {DW_LNS_advance_line [expr [gdb_get_line_number "main foo call"] - [gdb_get_line_number "main prologue"]]} {DW_LNS_copy} {DW_LNE_set_address main_label2} {DW_LNS_advance_line [expr [gdb_get_line_number "main return"] - [gdb_get_line_number "main foo call"]]} {DW_LNS_copy} {DW_LNE_set_address $main_end} {DW_LNS_advance_line [expr [gdb_get_line_number "main end"] - [gdb_get_line_number "main return"] + 1]} {DW_LNS_copy} {DW_LNE_end_sequence} {DW_LNE_set_address $foo_start} {DW_LNS_advance_line [expr [gdb_get_line_number "foo prologue"] - 1] } {DW_LNS_copy} {DW_LNE_set_address foo_label} {DW_LNS_advance_line [expr [gdb_get_line_number "foo bar call"] - [gdb_get_line_number "foo prologue"]]} {DW_LNS_copy} {DW_LNE_set_address foo_label2} {DW_LNS_advance_line [expr [gdb_get_line_number "foo foo_low call"] - [gdb_get_line_number "foo bar call"]]} {DW_LNS_copy} {DW_LNE_set_address foo_label3} {DW_LNS_advance_line [expr [gdb_get_line_number "foo end"] - [gdb_get_line_number "foo foo_low call"]]} {DW_LNS_copy} {DW_LNE_set_address $foo_end} {DW_LNS_advance_line 1} {DW_LNS_copy} {DW_LNE_end_sequence} {DW_LNE_set_address $bar_start} {DW_LNS_advance_line [expr [gdb_get_line_number "bar end"] - 1]} {DW_LNS_copy} {DW_LNS_advance_pc $bar_len} {DW_LNS_advance_line 1} {DW_LNS_copy} {DW_LNE_end_sequence} {DW_LNE_set_address $baz_start} {DW_LNS_advance_line [expr [gdb_get_line_number "baz end"] - 1]} {DW_LNS_copy} {DW_LNS_advance_pc $baz_len} {DW_LNS_advance_line 1} {DW_LNS_copy} {DW_LNE_end_sequence} {DW_LNE_set_address $foo_low_start} {DW_LNS_advance_line [expr [gdb_get_line_number "foo_low prologue"] - 1]} {DW_LNS_copy} {DW_LNE_set_address foo_low_label} {DW_LNS_advance_line [expr [gdb_get_line_number "foo_low baz call"] - [gdb_get_line_number "foo_low prologue"]]} {DW_LNS_copy} {DW_LNE_set_address foo_low_label2} {DW_LNS_advance_line [expr [gdb_get_line_number "foo_low end"] - [gdb_get_line_number "foo_low baz call"]]} {DW_LNS_copy} {DW_LNE_set_address $foo_low_end} {DW_LNS_advance_line 1} {DW_LNS_copy} {DW_LNE_end_sequence} } } # Generate ranges data. ranges {is_64 [is_64_target]} { func_ranges_label: sequence { {range {$foo_start } $foo_end} {range {$foo_low_start} $foo_low_end} } cu_ranges_label: sequence { {range {$foo_start } $foo_end} {range {$foo_low_start} $foo_low_end} {range {$main_start} $main_end} {range {$bar_start} $bar_end} {range {$baz_start} $baz_end} } } } if { [prepare_for_testing "failed to prepare" ${testfile} \ [list $srcfile $asm_file] {nodebug}] } { return -1 } if ![runto_main] { return -1 } set main_prologue_line_num [gdb_get_line_number "main prologue"] # Do a sanity check to make sure that line number info is available. gdb_test "info line main" \ "Line ${main_prologue_line_num} of .* starts at address .* and ends at .*" with_test_prefix "step-test-1" { set bp_foo_bar [gdb_get_line_number "foo bar call"] gdb_test "break $bp_foo_bar" \ "Breakpoint.*at.* file .*$srcfile, line $bp_foo_bar\\." \ "break at call to bar" gdb_test "continue" \ "Continuing\\..*Breakpoint \[0-9\]+, foo \\(\\).*$bp_foo_bar\\s+bar\\s\\(\\);.*foo bar call.*" \ "continue to call of bar" gdb_test "step" \ "bar \\(\\).*bar end.*" \ "step into bar" gdb_test "step" \ "foo \\(\\).*foo foo_low call.*" \ "step out of bar, back into foo" } with_test_prefix "step-test-2" { clean_restart ${testfile} if ![runto_main] { return -1 } # Note that the RE used for the following test will fail when the # breakpoint has been set on multiple locations. E.g. "(2 locations)". # This is intentional since that behavior is one of the bugs that # this test case tests for. gdb_test "break foo" \ "Breakpoint.*at.* file .*$srcfile, line \\d+\\." \ "break foo" # Continue to foo. Allow execution to stop either on the prologue # or on the call to bar since either behavior is acceptable though # the latter is preferred. set test "continue to foo" gdb_test_multiple "continue" $test { -re "Breakpoint \\d+, foo \\(\\).*foo prologue.*${gdb_prompt}" { pass $test gdb_test "step" \ "foo bar call .*" \ "step to call of bar after landing on prologue" } -re "Breakpoint \\d+, foo \\(\\).*foo bar call.*${gdb_prompt}" { pass $test } } gdb_test "step" \ "bar \\(\\).*bar end.*" \ "step into bar" gdb_test "step" \ "foo \\(\\).*foo foo_low call.*" \ "step out of bar, back into foo" } clean_restart ${testfile} if ![runto_main] { return -1 } # Disassembly of foo should have multiple address ranges. gdb_test_sequence "disassemble foo" "" [list \ "Dump of assembler code for function foo:" \ "Address range $hex to $hex:" \ " $hex <\\+0>:" \ "Address range $hex to $hex:" \ " $hex <(.+?)>:" \ "End of assembler dump\\." \ ] set foo_low_addr -1 set test "x/i foo_low" gdb_test_multiple $test $test { -re " ($hex) .*${gdb_prompt}" { set foo_low_addr $expect_out(1,string) pass $test } } set foo_addr -1 set test "x/i foo" gdb_test_multiple $test $test { -re " ($hex) .*${gdb_prompt}" { set foo_addr $expect_out(1,string) pass $test } } gdb_assert {$foo_low_addr != $foo_addr} "foo and foo_low are at different addresses" # This more permissive RE for "break foo" will allow a breakpoint on # multiple locations to PASS. */ gdb_test "break foo" \ "Breakpoint.*at.*" \ "break foo" gdb_test "break baz" \ "Breakpoint.*at.* file .*$srcfile, line \\d+\\." gdb_test "continue" \ "Breakpoint \\d+, foo \\(\\).*" \ "continue to foo" gdb_test_no_output "set variable e=1" # If GDB incorrectly places the foo breakpoint on multiple locations, # then GDB will (incorrectly) stop in foo_low instead of in baz. gdb_test "continue" \ "Breakpoint \\d+, (?:$hex in )?baz \\(\\).*" \ "continue to baz" with_test_prefix "step-test-3" { clean_restart ${testfile} if ![runto_main] { return -1 } gdb_test "step" \ "foo \\(\\).*bar \\(\\);.*foo bar call.*" \ "step into foo from main" gdb_test "step" \ "bar \\(\\).*\}.* bar end.*" \ "step into bar from foo" gdb_test "step" \ "foo(_label2)? \\(\\).*foo_low \\(\\);.*foo foo_low call.*" \ "step out of bar to foo" # The tests in the "enable_foo_low_stepping" section, below, work # with some versions of gcc, though it's not clear that they # should. This test case causes foo_low, originally a separate # function invoked via a subroutine call, to be considered as part # of foo via use of DW_AT_ranges. Real code that I've looked at # uses a branch instruction to cause code in the "cold" range to # be executed. # # For the moment though, these tests have been left in place, but # disabled, in case we decide that making such a subroutine call # is a reasonable thing to do that should also be supported by # GDB. set enable_foo_low_stepping false if { $enable_foo_low_stepping } { gdb_test_no_output "set variable e=1" set test "step into foo_low from foo" gdb_test_multiple "step" $test { -re "foo(_low)? \\(\\).*\{.*foo_low prologue.*${gdb_prompt}" { pass $test gdb_test "step" \ "foo \\(\\).*baz \\(\\);.*foo_low baz call.*" \ "step to baz call in foo_low" } -re "foo(_low)? \\(\\).*baz \\(\\);.*foo_low baz call.*${gdb_prompt}" { pass $test } } gdb_test "step" \ "baz \\(\\).*\}.*baz end.*" \ "step into baz from foo_low" gdb_test "step" \ "foo(?:_low(?:_label2)?)? \\(\\).*\}.*foo_low end.*" \ "step out of baz to foo_low" gdb_test "step" \ "foo(?:_label3)? \\(\\).*\}.*foo end.*" \ "step out of foo_low to foo" } else { gdb_test "next" \ ".*foo end.*" \ "next over foo_low call" } gdb_test "step" \ "main(?:_label2)? \\(\\).*" \ "step out of foo to main" }