From 962effa79018f57766d9aee21493f616801778dc Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Tue, 2 Feb 2021 10:40:52 -0500 Subject: [PATCH] gdb/testsuite: add .debug_rnglists tests Add tests for the various issues fixed in the previous patches. Add a new "rnglists" procedure to the DWARF assembler, to allow generating .debug_rnglists sections. A trivial change is required to support the DWARF 5 CU header layout. gdb/testsuite/ChangeLog: PR gdb/26813 * lib/dwarf.exp (_handle_DW_FORM): Handle DW_FORM_rnglistx. (cu): Generate header for DWARF 5. (rnglists): New proc. * gdb.dwarf2/rnglists-multiple-cus.exp: New. * gdb.dwarf2/rnglists-sec-offset.exp: New. Change-Id: I5b297e59c370c60cf671dec19796a6c3b9a9f632 --- gdb/testsuite/ChangeLog | 9 + .../gdb.dwarf2/rnglists-multiple-cus.exp | 102 ++++++++++ .../gdb.dwarf2/rnglists-sec-offset.exp | 80 ++++++++ gdb/testsuite/lib/dwarf.exp | 187 +++++++++++++++++- 4 files changed, 375 insertions(+), 3 deletions(-) create mode 100644 gdb/testsuite/gdb.dwarf2/rnglists-multiple-cus.exp create mode 100644 gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 0f3f445b8f2..ec385e5df8d 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2021-02-02 Simon Marchi + + PR gdb/26813 + * lib/dwarf.exp (_handle_DW_FORM): Handle DW_FORM_rnglistx. + (cu): Generate header for DWARF 5. + (rnglists): New proc. + * gdb.dwarf2/rnglists-multiple-cus.exp: New. + * gdb.dwarf2/rnglists-sec-offset.exp: New. + 2021-02-02 Tom de Vries PR symtab/24620 diff --git a/gdb/testsuite/gdb.dwarf2/rnglists-multiple-cus.exp b/gdb/testsuite/gdb.dwarf2/rnglists-multiple-cus.exp new file mode 100644 index 00000000000..e09cd4e8fe7 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/rnglists-multiple-cus.exp @@ -0,0 +1,102 @@ +# Copyright 2020 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 . + +# Test to reproduce the crash described in PR 26813. +# +# When reading a list in any table in the .debug_rnglists section, GDB would +# read the header at offset 0 in the section (the header of the first table). +# When the index of the list we read was greater than the number of lists of +# the first table, GDB would erroneously report that the index is invalid. +# +# So this test creates a .debug_rnglists section with two tables. The second +# table has more lists than the first one and we try to read a high index in +# the second table. + +load_lib dwarf.exp + +if {![dwarf2_support]} { + return 0 +} + +# Test with 32-bit and 64-bit DWARF. +foreach_with_prefix is_64 {false true} { + if { $is_64 } { + standard_testfile main.c -dw64.S + set testfile ${testfile}-dw64 + } else { + standard_testfile main.c -dw32.S + set testfile ${testfile}-dw32 + } + + set asm_file [standard_output_file $srcfile2] + Dwarf::assemble $asm_file { + global is_64 + + # The CU uses the DW_FORM_rnglistx form to refer to the .debug_rnglists + # section. + cu { + version 5 + is_64 $is_64 + } { + DW_TAG_compile_unit { + {DW_AT_ranges 1 DW_FORM_rnglistx} + {DW_AT_rnglists_base cu_table DW_FORM_sec_offset} + } { + # This tests a DW_AT_ranges attribute of form DW_FORM_rnglistx on a + # function, which was buggy at some point. + DW_TAG_subprogram { + {DW_AT_name "foo"} + {DW_AT_ranges 2 DW_FORM_rnglistx} + } + } + } + + rnglists -is-64 $is_64 { + # This table is unused, but exists so that the used table is not at + # the beginning of the section. + table { + list_ { + start_end 0x1000 0x2000 + } + } + + # The lists in this table are accessed by index (DW_FORM_rnglistx). + table -post-header-label cu_table { + # This list is unused, but exists to offset the next ones. + list_ { + start_end 0x2000 0x3000 + } + + # For the CU. + list_ { + start_end 0x3000 0x4000 + } + + # For function foo. + list_ { + start_end 0x3000 0x3010 + } + } + } + } + + if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 + } + + # Sanity checks to make sure GDB slurped the symbols correctly. + gdb_test "p/x &foo" " = 0x3000" +} diff --git a/gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp b/gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp new file mode 100644 index 00000000000..d898d11c0dc --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/rnglists-sec-offset.exp @@ -0,0 +1,80 @@ +# Copyright 2020 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 . + +# Test DW_AT_ranges attributes referencing the .debug_rnglists section using the +# DW_FORM_sec_offset form. + +load_lib dwarf.exp + +if {![dwarf2_support]} { + return 0 +} + +foreach_with_prefix is_64 {false true} { + if { $is_64 } { + standard_testfile main.c -dw64.S + set testfile ${testfile}-dw64 + } else { + standard_testfile main.c -dw32.S + set testfile ${testfile}-dw32 + } + + set asm_file [standard_output_file $srcfile2] + Dwarf::assemble $asm_file { + global is_64 + + declare_labels cu_range_list foo_range_list + + # This CU uses the DW_FORM_sec_offset form to refer to the .debug_rnglists + # section. + cu { + version 5 + is_64 $is_64 + } { + DW_TAG_compile_unit { + {DW_AT_ranges $cu_range_list DW_FORM_sec_offset} + } { + DW_TAG_subprogram { + {DW_AT_name "foo"} + {DW_AT_ranges $foo_range_list DW_FORM_sec_offset} + } + } + } + + rnglists -is-64 $is_64 { + # The lists in this table are accessed by direct offset + # (DW_FORM_sec_offset). + table { + # For the CU. + cu_range_list: list_ { + start_end 0x4000 0x5000 + } + + # For function foo. + foo_range_list: list_ { + start_end 0x4000 0x4010 + } + } + } + } + + if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 + } + + # Sanity checks to make sure GDB slurped the symbols correctly. + gdb_test "p/x &foo" " = 0x4000" +} diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index 7462890ef9d..aba4afba224 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -473,7 +473,8 @@ namespace eval Dwarf { } DW_FORM_ref_udata - - DW_FORM_udata { + DW_FORM_udata - + DW_FORM_rnglistx { _op .uleb128 $value } @@ -1115,8 +1116,16 @@ namespace eval Dwarf { } define_label $start_label _op .2byte $_cu_version Version - _op .${_cu_offset_size}byte $my_abbrevs Abbrevs - _op .byte $_cu_addr_size "Pointer size" + + # The CU header for DWARF 4 and 5 are slightly different. + if { $_cu_version == 5 } { + _op .byte 0x1 "DW_UT_compile" + _op .byte $_cu_addr_size "Pointer size" + _op .${_cu_offset_size}byte $my_abbrevs Abbrevs + } else { + _op .${_cu_offset_size}byte $my_abbrevs Abbrevs + _op .byte $_cu_addr_size "Pointer size" + } _defer_output $_abbrev_section { define_label $my_abbrevs @@ -1306,6 +1315,178 @@ namespace eval Dwarf { uplevel $body } + # Emit a DWARF .debug_rnglists section. + # + # The target address size is based on the current target's address size. + # + # There is one mandatory positional argument, BODY, which must be Tcl code + # that emits the content of the section. It is evaluated in the caller's + # context. + # + # The following option can be used: + # + # - -is-64 true|false: Whether to use 64-bit DWARF instead of 32-bit DWARF. + # The default is 32-bit. + + proc rnglists { args } { + variable _debug_rnglists_addr_size + variable _debug_rnglists_offset_size + variable _debug_rnglists_is_64_dwarf + + parse_args {{"is-64" "false"}} + + if { [llength $args] != 1 } { + error "rnglists proc expects one positional argument (body)" + } + + lassign $args body + + if [is_64_target] { + set _debug_rnglists_addr_size 8 + } else { + set _debug_rnglists_addr_size 4 + } + + if { ${is-64} } { + set _debug_rnglists_offset_size 8 + set _debug_rnglists_is_64_dwarf true + } else { + set _debug_rnglists_offset_size 4 + set _debug_rnglists_is_64_dwarf false + } + + _section ".debug_rnglists" + + # Count of tables in the section. + variable _debug_rnglists_table_count 0 + + # Compute the label name for list at index LIST_IDX, for the current + # table. + + proc _compute_list_label { list_idx } { + variable _debug_rnglists_table_count + + return ".Lrnglists_table_${_debug_rnglists_table_count}_list_${list_idx}" + } + + # Generate one table (header + offset array + range lists). + # + # Accepts one positional argument, BODY. BODY may call the LIST_ + # procedure to generate rnglists. + # + # The -post-header-label option can be used to define a label just after + # the header of the table. This is the label that a DW_AT_rnglists_base + # attribute will usually refer to. + + proc table { args } { + variable _debug_rnglists_table_count + variable _debug_rnglists_addr_size + variable _debug_rnglists_offset_size + variable _debug_rnglists_is_64_dwarf + + parse_args {{post-header-label ""}} + + if { [llength $args] != 1 } { + error "table proc expects one positional argument (body)" + } + + lassign $args body + + # Generate one range list. + # + # BODY may call the various procs defined below to generate list entries. + # They correspond to the range list entry kinds described in section 2.17.3 + # of the DWARF 5 spec. + # + # To define a label pointing to the beginning of the list, use + # the conventional way of declaring and defining labels: + # + # declare_labels the_list + # + # the_list: list_ { + # ... + # } + + proc list_ { body } { + variable _debug_rnglists_list_count + + # Define a label for this list. It is used to build the offset + # array later. + set list_label [_compute_list_label $_debug_rnglists_list_count] + define_label $list_label + + # Emit a DW_RLE_start_end entry. + + proc start_end { start end } { + variable _debug_rnglists_addr_size + + _op .byte 0x06 "DW_RLE_start_end" + _op .${_debug_rnglists_addr_size}byte $start "start" + _op .${_debug_rnglists_addr_size}byte $end "end" + } + + uplevel $body + + # Emit end of list. + _op .byte 0x00 "DW_RLE_end_of_list" + + incr _debug_rnglists_list_count + } + + # Count of lists in the table. + variable _debug_rnglists_list_count 0 + + # Generate the lists ops first, because we need to know how many + # lists there are to generate the header and offset table. + set lists_ops [_defer_to_string { + uplevel $body + }] + + set post_unit_len_label \ + [_compute_label "rnglists_table_${_debug_rnglists_table_count}_post_unit_len"] + set post_header_label \ + [_compute_label "rnglists_table_${_debug_rnglists_table_count}_post_header"] + set table_end_label \ + [_compute_label "rnglists_table_${_debug_rnglists_table_count}_end"] + + # Emit the table header. + if { $_debug_rnglists_is_64_dwarf } { + _op .4byte 0xffffffff "unit length 1/2" + _op .8byte "$table_end_label - $post_unit_len_label" "unit length 2/2" + } else { + _op .4byte "$table_end_label - $post_unit_len_label" "unit length" + } + + define_label $post_unit_len_label + + _op .2byte 5 "dwarf version" + _op .byte $_debug_rnglists_addr_size "address size" + _op .byte 0 "segment selector size" + _op .4byte "$_debug_rnglists_list_count" "offset entry count" + + define_label $post_header_label + + # Define the user post-header label, if provided. + if { ${post-header-label} != "" } { + define_label ${post-header-label} + } + + # Emit the offset array. + for {set list_idx 0} {$list_idx < $_debug_rnglists_list_count} {incr list_idx} { + set list_label [_compute_list_label $list_idx] + _op .${_debug_rnglists_offset_size}byte "$list_label - $post_header_label" "offset of list $list_idx" + } + + # Emit the actual list data. + _emit "$lists_ops" + + define_label $table_end_label + + incr _debug_rnglists_table_count + } + + uplevel $body + } # Emit a DWARF .debug_line unit. # OPTIONS is a list with an even number of elements containing -- 2.30.2