| # 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/>. |
| |
| # Test different ways in which DW_AT_entry_pc can be expressed in the |
| # DWARF. Also test with DWARF-4 and DWARF-5. See the individule test |
| # procs below precise details of what DW_AT_entry_pc forms are tested. |
| |
| 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 -1 |
| } |
| |
| if ![runto_main] { |
| return -1 |
| } |
| |
| # Address for the middle of foo. This is used as our entry point when |
| # the entry_pc is defined as an address. |
| set foo_middle_addr [get_hexadecimal_valueof "&foo_middle" "UNKNOWN" \ |
| "get address for middle of foo"] |
| |
| # The FOO_START and FOO_END we get from get_func_info is an expression |
| # involving symbols and offsets. To check the 'maint info blocks' |
| # output we need these converted into actual addresses. |
| set foo_start_addr [get_hexadecimal_valueof "$foo_start" "UNKNOWN" \ |
| "get address for start of foo"] |
| set foo_end_addr [get_hexadecimal_valueof "$foo_end" "UNKNOWN" \ |
| "get address for end of foo"] |
| |
| # The ranges within foo. Used when foo is defined using ranges rather |
| # than a low pc and high pc pair. The entry point is in the middle of |
| # the second range. |
| foreach var { r1_s r1_e r2_s r2_e r3_s r3_e } { |
| set $var [get_hexadecimal_valueof "&foo_$var" "UNKNOWN" \ |
| "get address for foo_$var"] |
| } |
| |
| # Line on which 'foo' is declared. Used in generated debug. |
| set foo_decl_line [gdb_get_line_number "foo decl line"] |
| |
| if [is_ilp32_target] { |
| set ptr_type "data4" |
| } else { |
| set ptr_type "data8" |
| } |
| |
| # Generate a suffix number. Called from each of the test procs below |
| # to acquire a unique suffix for naming asm files and executables. |
| |
| set global_test_suffix 0 |
| proc get_next_suffix {} { |
| global global_test_suffix |
| incr global_test_suffix |
| |
| return $global_test_suffix |
| } |
| |
| # Helper for the two build_and_test_* procs below. Combine ASM_FILE |
| # with the global SRCFILE and build an executable. Use SUFFIX to give |
| # the executable a unique name. |
| |
| proc build_and_runto_main { suffix asm_file } { |
| if {[prepare_for_testing "failed to prepare" "${::testfile}-${suffix}" \ |
| [list $::srcfile $asm_file] {nodebug}]} { |
| return false |
| } |
| |
| if ![runto_main] { |
| return false |
| } |
| |
| # Ensure the CU containing 'foo' is expanded, so the blocks are |
| # visible. |
| gdb_test "info function foo" \ |
| "File \[^\r\n\]+/$::srcfile:\r\n$::foo_decl_line:\\s+void foo\\(\\);.*" |
| |
| return true |
| } |
| |
| |
| # Combine ASM_FILE with the global SRCFILE and build an executable, |
| # use SUFFIX to make the executable name unique. |
| # |
| # Then check the blocks at the symbol `foo_middle'. The inner most |
| # block should be a block for 'foo' with a continuous address range |
| # and an entry address of ENTRY_PC. |
| |
| proc build_and_test_continuous { suffix asm_file entry_pc } { |
| if { ![build_and_runto_main $suffix $asm_file] } { |
| return false |
| } |
| |
| gdb_test "maint info blocks foo_middle" \ |
| [multi_line \ |
| "\\\[\[^\]\]+\\\] $::foo_start_addr\.\.$::foo_end_addr" \ |
| " entry pc: $entry_pc" \ |
| " function: foo" \ |
| " is contiguous"] |
| } |
| |
| # Combine ASM_FILE with the global SRCFILE and build an executable, |
| # use SUFFIX to make the executable name unique. |
| # |
| # Then check the blocks at the symbol `foo_middle'. The inner most |
| # block should be a block for 'foo' which has 3 address ranges and an |
| # entry address of ENTRY_PC. |
| |
| proc build_and_test_ranged { suffix asm_file entry_pc } { |
| if { ![build_and_runto_main $suffix $asm_file] } { |
| return false |
| } |
| |
| gdb_test "maint info blocks foo_middle" \ |
| [multi_line \ |
| "\\\[\[^\]\]+\\\] $::r1_s\.\.$::r3_e" \ |
| " entry pc: $entry_pc" \ |
| " function: foo" \ |
| " address ranges:" \ |
| " $::r1_s\.\.$::r1_e" \ |
| " $::r2_s\.\.$::r2_e" \ |
| " $::r3_s\.\.$::r3_e" ] |
| } |
| |
| # The function's address range is defined using low/high bounds and |
| # the entry_pc attribute is not given. The function's entry PC will |
| # default to the low address. |
| |
| proc_with_prefix use_low_high_bounds_without_entry_pc { dwarf_vesion } { |
| set suffix [get_next_suffix] |
| |
| # Make some DWARF for the test. |
| set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] |
| Dwarf::assemble $asm_file { |
| global srcfile |
| |
| declare_labels lines_table |
| |
| 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} |
| } { |
| 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} |
| } |
| } |
| } |
| |
| lines {version 2} lines_table { |
| include_dir "$::srcdir/$::subdir" |
| file_name "$srcfile" 1 |
| } |
| } |
| |
| build_and_test_continuous $suffix $asm_file $::foo_start_addr |
| } |
| |
| # The function's address range is defined using low/high bounds and an |
| # entry_pc attribute is given (which contains an address), which will |
| # be used as the function's entry address. |
| |
| proc_with_prefix use_low_high_bounds_with_entry_pc { dwarf_version } { |
| set suffix [get_next_suffix] |
| |
| # Make some DWARF for the test. |
| set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] |
| Dwarf::assemble $asm_file { |
| global srcfile |
| |
| declare_labels lines_table |
| |
| 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} |
| } { |
| 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} |
| {entry_pc foo_middle addr} |
| } |
| } |
| } |
| |
| lines {version 2} lines_table { |
| include_dir "$::srcdir/$::subdir" |
| file_name "$srcfile" 1 |
| } |
| } |
| |
| build_and_test_continuous $suffix $asm_file $::foo_middle_addr |
| } |
| |
| # The function's address range is defined using low/high bounds and an |
| # entry_pc attribute is given (which contains an offset from the base |
| # address), which will be used to compute the function's entry address. |
| |
| proc_with_prefix use_low_high_bounds_with_entry_offset { dwarf_version } { |
| set suffix [get_next_suffix] |
| |
| # Make some DWARF for the test. |
| set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] |
| Dwarf::assemble $asm_file { |
| global srcfile |
| |
| declare_labels lines_table |
| |
| set foo_offset [expr $::foo_middle_addr - $::foo_start_addr] |
| |
| 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} |
| } { |
| 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} |
| {entry_pc $foo_offset data4} |
| } |
| } |
| } |
| |
| lines {version 2} lines_table { |
| include_dir "$::srcdir/$::subdir" |
| file_name "$srcfile" 1 |
| } |
| } |
| |
| build_and_test_continuous $suffix $asm_file $::foo_middle_addr |
| } |
| |
| # The function's address range is defined using range information. No |
| # entry_pc attribute is used. The entry PC for the function will |
| # default to the first address of the first range. |
| |
| proc_with_prefix use_ranges_without_entry_pc { dwarf_version } { |
| set suffix [get_next_suffix] |
| |
| # Make some DWARF for the test. |
| set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] |
| Dwarf::assemble $asm_file { |
| upvar dwarf_version dwarf_version |
| global srcfile |
| |
| declare_labels lines_table 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} |
| } { |
| subprogram { |
| {name foo} |
| {decl_file 1 data1} |
| {decl_line $::foo_decl_line data1} |
| {decl_column 1 data1} |
| {external 1 flag} |
| {ranges ${ranges_label} DW_FORM_sec_offset} |
| } |
| } |
| } |
| |
| lines {version 2} lines_table { |
| include_dir "$::srcdir/$::subdir" |
| file_name "$srcfile" 1 |
| } |
| |
| if { $dwarf_version == 5 } { |
| rnglists {} { |
| table {} { |
| ranges_label: list_ { |
| start_end foo_r1_s foo_r1_e |
| start_end foo_r2_s foo_r2_e |
| start_end foo_r3_s foo_r3_e |
| } |
| } |
| } |
| } else { |
| ranges { } { |
| ranges_label: sequence { |
| range foo_r1_s foo_r1_e |
| range foo_r2_s foo_r2_e |
| range foo_r3_s foo_r3_e |
| } |
| } |
| } |
| } |
| |
| build_and_test_ranged $suffix $asm_file $::r1_s |
| } |
| |
| # The function's address range is defined using range information and |
| # an entry_pc attribute (which is an address) is used, this will be |
| # the entry PC for the function. |
| |
| proc_with_prefix use_ranges_with_entry_pc { dwarf_version } { |
| set suffix [get_next_suffix] |
| |
| # Make some DWARF for the test. |
| set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] |
| Dwarf::assemble $asm_file { |
| upvar dwarf_version dwarf_version |
| global srcfile |
| |
| declare_labels lines_table 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} |
| } { |
| subprogram { |
| {name foo} |
| {decl_file 1 data1} |
| {decl_line $::foo_decl_line data1} |
| {decl_column 1 data1} |
| {external 1 flag} |
| {ranges ${ranges_label} DW_FORM_sec_offset} |
| {entry_pc foo_middle addr} |
| } |
| } |
| } |
| |
| lines {version 2} lines_table { |
| include_dir "$::srcdir/$::subdir" |
| file_name "$srcfile" 1 |
| } |
| |
| if { $dwarf_version == 5 } { |
| rnglists {} { |
| table {} { |
| ranges_label: list_ { |
| start_end foo_r1_s foo_r1_e |
| start_end foo_r2_s foo_r2_e |
| start_end foo_r3_s foo_r3_e |
| } |
| } |
| } |
| } else { |
| ranges { } { |
| ranges_label: sequence { |
| range foo_r1_s foo_r1_e |
| range foo_r2_s foo_r2_e |
| range foo_r3_s foo_r3_e |
| } |
| } |
| } |
| } |
| |
| build_and_test_ranged $suffix $asm_file $::foo_middle_addr |
| } |
| |
| # The function's address range is defined using range information and |
| # an entry_pc attribute (which is an offset) is used, this will be |
| # used to calculate the entry PC for the function. |
| |
| proc_with_prefix use_ranges_with_entry_offset { dwarf_version } { |
| set suffix [get_next_suffix] |
| |
| # Make some DWARF for the test. |
| set asm_file [standard_output_file "$::testfile-dw-$suffix.S"] |
| Dwarf::assemble $asm_file { |
| upvar dwarf_version dwarf_version |
| global srcfile |
| |
| declare_labels lines_table ranges_label |
| |
| set foo_offset [expr $::foo_middle_addr - $::r1_s] |
| |
| 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} |
| } { |
| subprogram { |
| {name foo} |
| {decl_file 1 data1} |
| {decl_line $::foo_decl_line data1} |
| {decl_column 1 data1} |
| {external 1 flag} |
| {ranges ${ranges_label} DW_FORM_sec_offset} |
| {entry_pc $foo_offset data4} |
| } |
| } |
| } |
| |
| lines {version 2} lines_table { |
| include_dir "$::srcdir/$::subdir" |
| file_name "$srcfile" 1 |
| } |
| |
| if { $dwarf_version == 5 } { |
| rnglists {} { |
| table {} { |
| ranges_label: list_ { |
| start_end foo_r1_s foo_r1_e |
| start_end foo_r2_s foo_r2_e |
| start_end foo_r3_s foo_r3_e |
| } |
| } |
| } |
| } else { |
| ranges { } { |
| ranges_label: sequence { |
| range foo_r1_s foo_r1_e |
| range foo_r2_s foo_r2_e |
| range foo_r3_s foo_r3_e |
| } |
| } |
| } |
| } |
| |
| build_and_test_ranged $suffix $asm_file $::foo_middle_addr |
| } |
| |
| # Run the tests. |
| foreach_with_prefix dwarf_version { 4 5 } { |
| use_low_high_bounds_without_entry_pc $dwarf_version |
| use_low_high_bounds_with_entry_offset $dwarf_version |
| use_low_high_bounds_with_entry_pc $dwarf_version |
| use_ranges_without_entry_pc $dwarf_version |
| use_ranges_with_entry_pc $dwarf_version |
| use_ranges_with_entry_offset $dwarf_version |
| } |