[gdb/testsuite] Add support for .debug_names in dwarf assembler
authorTom de Vries <tdevries@suse.de>
Sun, 7 Aug 2022 06:31:36 +0000 (08:31 +0200)
committerTom de Vries <tdevries@suse.de>
Sun, 7 Aug 2022 06:31:36 +0000 (08:31 +0200)
Add:
- support for a per-module .debug_names section in the dwarf assembler, and
- a test-case excercising this new functionality.

A per-module .debug_names section needs to have an entry in the CU list for
each CU in the module, which is made more difficult by two things:
- linking in other objects, which may contain additional CUs
  (typically the case on openSUSE), and
- adding dummy CUs in the dwarf assembler.
We handle this by:
- compiling with -nostartfiles (so the test-case contains _start rather than
  main), and
- disabling the dummy CU generation for the test-case.

I've kept things simple by having the test-case specify the hash value, rather
than adding that functionality in the dwarf assembler.

Also I've kept the bucket count to 1, which makes it trivial to satisfy the
requirement that "the symbol is entered into a bucket whose index is the hash
value modulo bucket_count".

The readelf dump of the .debug_names section from the test-case looks like:
...
Version 5
Augmentation string: 47 44 42 00  ("GDB")
CU table:
[  0] 0x0

TU table:

Foreign TU table:

Used 1 of 1 bucket.
Out of 2 items there are 1 bucket clashes (longest of 1 entries).

Symbol table:
[  0] #eddb6232 _start: <1> DW_TAG_subprogram DW_IDX_compile_unit=0
[  1] #0b888030 int: <2> DW_TAG_base_type DW_IDX_compile_unit=0
...

Tested on x86_64-linux.

gdb/testsuite/gdb.dwarf2/_start.c [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/debug-names.exp [new file with mode: 0644]
gdb/testsuite/lib/dwarf.exp

diff --git a/gdb/testsuite/gdb.dwarf2/_start.c b/gdb/testsuite/gdb.dwarf2/_start.c
new file mode 100644 (file)
index 0000000..cf74fe0
--- /dev/null
@@ -0,0 +1,27 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2022 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/>.  */
+
+/* Dummy _start function, to be used with -nostartfile.  */
+
+#include <stdlib.h>
+
+void
+_start (void)
+{
+  asm ("_start_label: .globl _start_label");
+  exit (0);
+}
diff --git a/gdb/testsuite/gdb.dwarf2/debug-names.exp b/gdb/testsuite/gdb.dwarf2/debug-names.exp
new file mode 100644 (file)
index 0000000..0af5a93
--- /dev/null
@@ -0,0 +1,79 @@
+# Copyright 2022 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/>.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+standard_testfile _start.c debug-names.S
+
+set func_info_vars \
+    [get_func_info _start [list debug additional_flags=-nostartfiles]]
+
+# Create the DWARF.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble {
+    filename $asm_file
+    add_dummy_cus 0
+} {
+    global func_info_vars
+    foreach var $func_info_vars {
+       global $var
+    }
+
+    cu { label cu_label } {
+       compile_unit {{language @DW_LANG_C}} {
+           subprogram {
+               {DW_AT_name _start}
+               {DW_AT_low_pc $_start_start DW_FORM_addr}
+               {DW_AT_high_pc $_start_end DW_FORM_addr}
+           }
+           base_type {
+               {name int}
+               {byte_size 4 sdata}
+               {encoding @DW_ATE_signed}
+           }
+       }
+    }
+
+    debug_names {} {
+       cu cu_label
+       name _start subprogram cu_label 0xEDDB6232
+       name int base_type cu_label 0xB888030
+    }
+}
+
+if [prepare_for_testing "failed to prepare" $testfile "${asm_file} ${srcfile}" \
+       [list additional_flags=-nostartfiles]] {
+    return -1
+}
+
+# Verify that .debug_names section is not ignored.
+set index [have_index $binfile]
+gdb_assert { [string equal $index "debug_names"] } ".debug_names used"
+
+# Verify that initially no symtab is expanded.
+gdb_test_no_output "maint info symtabs"
+
+if ![runto _start] {
+    return -1
+}
+
+# Verify that breaking on _start actually expanded a symtab, rather than
+# falling back on minimal symbols.
+gdb_test "maint info symtabs" "name <unknown>.*" "break _start expanded symtab"
index 356451bcaac9d79f9eb83b9fd510be543641c8ae..3d833e5cd5820ccc8a5803e8aeea68c629773f2d 100644 (file)
@@ -2925,9 +2925,215 @@ namespace eval Dwarf {
        }
     }
 
+    # Emit a DWARF .debug_names section.
+    #
+    # OPTIONS is a list with an even number of elements containing
+    # option-name and option-value pairs.
+    # Current options are:
+    # is_64 0|1 - boolean indicating if the section contains 64-bit DWARF.
+    #             default = 0 (32-bit)
+    # version n - section version.
+    #             default = 5.
+    #
+    # BODY is Tcl code that emits the parts which make up the body of
+    # the .debug_names section.  It is evaluated in the caller's context.
+    # The following commands are available for the BODY section:
+    #
+    #   cu <cu-label>
+    #     -- add a CU.
+    #
+    #   name <name> <tag> <cu> <hash>
+    #     -- add a name.
+
+    proc debug_names { options body } {
+       parse_options {
+           { is_64 0 }
+           { version 5 }
+       }
+
+       variable _debug_names_offset_size
+       if { $is_64 == 1 } {
+           set _debug_names_offset_size 8
+       } else {
+           set _debug_names_offset_size 4
+       }
+
+       # Section start.
+       set section ".debug_names"
+       _section $section
+
+       # Header - initial length.
+       declare_labels debug_names_start debug_names_end
+       set length "$debug_names_end - $debug_names_start"
+       set comment "Initial_length"
+       if { $is_64 } {
+           _op .4byte 0xffffffff
+           _op .8byte $length $comment
+       } else {
+           _op .4byte $length $comment
+       }
+
+       # Header - start label.
+       debug_names_start:
+
+       # Header - version + padding.
+       _op .2byte $version "Version"
+       _op .2byte 0 "Padding"
+
+       # Parse the body.
+       variable _debug_names_cus
+       set _debug_names_cus []
+       proc _debug_names_cu { cu } {
+           variable _debug_names_cus
+           lappend _debug_names_cus $cu
+       }
+       variable _debug_names
+       set _debug_names []
+       proc _debug_names_name { name tag cu hash } {
+           variable _debug_names
+           declare_labels entry_pool_offset
+           lappend _debug_names [list $name $tag $cu $hash $entry_pool_offset]
+       }
+       with_override Dwarf::cu Dwarf::_debug_names_cu {
+       with_override Dwarf::name Dwarf::_debug_names_name {
+           uplevel $body
+       }}
+
+       # Header - CU / TU / foreign TU count.
+       _op .4byte [llength $_debug_names_cus] "Comp_unit_count"
+       _op .4byte 0 "Local_type_unit_count"
+       _op .4byte 0 "Foreign_type_unit_count"
+
+       # Header - bucket count.
+       _op .4byte 1 "Bucket_count"
+
+       # Header - name count.
+       _op .4byte [llength $_debug_names] "Name_count"
+
+       # Header - abbreviation table size.
+       declare_labels debug_names_abbrev_table_start \
+           debug_names_abbrev_table_end
+       set abbrev_table_size \
+           "$debug_names_abbrev_table_end - $debug_names_abbrev_table_start"
+       _op .4byte $abbrev_table_size "Abbrev_table_size"
+
+       # Header - augmentation string.
+       _op .4byte 4 "Augmentation_string_size"
+       _op .ascii [_quote GDB] "Augmentation_string"
+
+       # List of CUs.
+       set comment "CU offset"
+       foreach cu $_debug_names_cus {
+           upvar $cu tmp
+           if { $is_64 } {
+               _op .8byte $tmp $comment
+           } else {
+               _op .4byte $tmp $comment
+           }
+       }
+
+       # List of Local TUs.
+       #
+
+       # List of Foreign TUs.
+       #
+
+       # Hash Lookup Table - array of buckets.
+       _op .4byte 1 "bucket: hash array index 1"
+
+       # Hash Lookup Table - array of hashes.
+       foreach idx $_debug_names {
+           set name [lindex $idx 0]
+           set hash [lindex $idx 3]
+           _op .4byte $hash "hash: $name"
+       }
+
+       # Name Table - array of string offsets.
+       foreach idx $_debug_names {
+           set name [lindex $idx 0]
+
+           variable _strings
+           if {![info exists _strings($name)]} {
+               set _strings($name) [new_label strp]
+               _defer_output .debug_str {
+                   define_label $_strings($name)
+                   _op .ascii [_quote $name]
+               }
+           }
+
+           _op_offset $_debug_names_offset_size $_strings($name) "name: $name"
+       }
+
+       # Name Table - array of entry offsets.
+       set base_label ""
+       foreach idx $_debug_names {
+           set name [lindex $idx 0]
+           set label [lindex $idx 4]
+           if { [string equal $base_label ""]} {
+               set base_label $label
+           }
+           _op_offset $_debug_names_offset_size "$label - $base_label" \
+               "entry pool offset: $name"
+       }
+
+       # Abbreviations Table.
+       debug_names_abbrev_table_start:
+       set abbrev 1
+       variable _constants
+       foreach idx $_debug_names {
+           set name [lindex $idx 0]
+           set tag [lindex $idx 1]
+           _op .byte $abbrev "abbrev $abbrev"
+           _op .uleb128 $_constants(DW_TAG_$tag) "DW_TAG_$tag"
+           _op .byte 1  "DW_IDX_compile_unit (attribute)"
+           _op .byte 0x0f "DW_FORM_udata (form)"
+           _op .byte 0  "abbrev terminator (attribute)"
+           _op .byte 0  "abbrev terminator (form)"
+           incr abbrev
+       }
+       _op .byte 0 "Abbreviations Table terminator"
+       debug_names_abbrev_table_end:
+
+       # Entry Pool
+       set abbrev 1
+       foreach idx $_debug_names {
+           set name [lindex $idx 0]
+           set cu [lindex $idx 2]
+           set label [lindex $idx 4]
+
+           set cu_index 0
+           foreach idx2 $_debug_names_cus {
+               if { $idx2 == $cu } {
+                   break
+               }
+               incr cu_index
+           }
+
+           define_label $label
+           _op .byte $abbrev "$name: abbrev"
+           _op .uleb128 $cu_index "$name: CU index"
+           _op .byte 0 "$name: terminator"
+           incr abbrev
+       }
+
+       # Section end.
+       debug_names_end:
+    }
+
     # The top-level interface to the DWARF assembler.
-    # FILENAME is the name of the file where the generated assembly
-    # code is written.
+    # OPTIONS is a list with an even number of elements containing
+    # option-name and option-value pairs.
+    # Current options are:
+    # filename <string>
+    #           - the name of the file where the generated assembly
+    #             code is written.
+    #             default = "".
+    #  add_dummy_cus <0|1>
+    #           - Whether to add dummy CUs before and after the CUs
+    #             added in the BODY.
+    #             default = 1.
+    # As a special case, if OPTIONS is a list of length 1, it's
+    # interpreted as specifing the filename.
     # BODY is Tcl code to emit the assembly.  It is evaluated via
     # "eval" -- not uplevel as you might expect, because it is
     # important to run the body in the Dwarf namespace.
@@ -2943,7 +3149,7 @@ namespace eval Dwarf {
     #        ...
     #        }
     #    }
-    proc assemble {filename body} {
+    proc assemble {options body} {
        variable _initialized
        variable _output_file
        variable _deferred_output
@@ -2956,6 +3162,15 @@ namespace eval Dwarf {
        variable _debug_ranges_64_bit
        variable _debug_addr_index
 
+       if { [llength $options] == 1 } {
+           set options [list filename [lindex $options 0]]
+       }
+
+       parse_options {
+           { filename "" }
+           { add_dummy_cus 1 }
+       }
+
        if {!$_initialized} {
            _read_constants
            set _initialized 1
@@ -2975,7 +3190,9 @@ namespace eval Dwarf {
 
        # Dummy CU at the start to ensure that the first CU in $body is not
        # the first in .debug_info.
-       dummy_cu
+       if { $add_dummy_cus } {
+           dummy_cu
+       }
 
        with_shared_gdb {
            # Not "uplevel" here, because we want to evaluate in this
@@ -2986,7 +3203,9 @@ namespace eval Dwarf {
 
        # Dummy CU at the end to ensure that the last CU in $body is not
        # the last in .debug_info.
-       dummy_cu
+       if { $add_dummy_cus } {
+           dummy_cu
+       }
 
        _write_deferred_output