[PATCH 1/1] gdb, dwarf: create symbols for template tags without names

Bruno Larsen blarsen@redhat.com
Wed Jul 13 19:08:03 GMT 2022


On 7/7/22 11:17, Nils-Christian Kempke via Gdb-patches wrote:
> The following GDB behavior was also reported as a GDB bug in
> 
>    https://sourceware.org/bugzilla/show_bug.cgi?id=28396
> 
> I will reiterate the problem a bit and give some more information here.
> This patch closes the above mentioned bug.
> 
> The DWARF 5 standard 2.23 'Template Parameters' reads:
> 
>     A template type parameter is represented by a debugging information
>     entry with the tag DW_TAG_template_type_parameter.  A template value
>     parameter is represented by a debugging information entry with the tag
>     DW_TAG_template_value_parameter.  The actual template parameter entries
>     appear in the same order as the corresponding template formal
>     parameter declarations in the source progam.
> 
>     A type or value parameter entry may have a DW_AT_name attribute, whose
>     value is a null-terminated string containing the name of the
>     corresponding formal parameter.
> 
> So the DW_AT_name attribute for DW_TAG_template_type_parameter and
> DW_TAG_template_value_parameter is optional.
> 
> Within GDB, creating a new symbol from some read DIE usually requires the
> presence of a DW_AT_name for the DIE (an exception here is the case of
> unnamed namespaces or the existence of a linkage name).
> 
> This patch makes the presence of the DW_AT_name for template value/type
> tags optional, similar to the unnamed namespaces.
> 
> For unnamed namespaces dwarf2_name simply returns the constant string
> CP_ANONYMOUS_NAMESPACE_STR '(anonymous namespace)'.  For template tags a
> case was added to the switch statement calling the
> unnamed_template_tag_name helper.  Within the scope of parent which
> the template parameter is a child of, the helper counts the position
> of the template tag within the unnamed template tags and returns
> '<unnamedNUMBER>' where NUMBER is its position.  This way we end up with
> unique names within the respective scope of the function/class/struct
> (these are the only currenltly supported template kinds within GDB and
> usually the compilers) where we discovered the template tags in.
> 
> While I do not know of a way to bring GCC to emit template tags without
> names there is one for clang/icpx.  Consider the following example
> 
>    template<typename A, typename B, typename C>
>    class Foo {};
> 
>    template<typename, typename B, typename C>
>    class Foo;
> 
>    int main () {
>      Foo<double, int, float> f;
>      return 0;
>    }
> 
> The forward declaration for 'Foo' with the missing template type names
> 'A' and 'C' makes clang emit a bunch of template tags without names:
> 
>   ...
>    <2><43>: Abbrev Number: 3 (DW_TAG_variable)
>      <44>   DW_AT_location    : 2 byte block: 91 78      (DW_OP_fbreg: -8)
>      <47>   DW_AT_name        : (indirect string, offset: 0x63): f
>      <4b>   DW_AT_decl_file   : 1
>      <4c>   DW_AT_decl_line   : 8
>      <4d>   DW_AT_type        : <0x59>
>   ...
>   <1><59>: Abbrev Number: 5 (DW_TAG_class_type)
>      <5a>   DW_AT_calling_convention: 5  (pass by value)
>      <5b>   DW_AT_name        : (indirect string, offset: 0x74): Foo<double, int, float>
>      <5f>   DW_AT_byte_size   : 1
>      <60>   DW_AT_decl_file   : 1
>      <61>   DW_AT_decl_line   : 2
>   <2><62>: Abbrev Number: 6 (DW_TAG_template_type_param)
>      <63>   DW_AT_type        : <0x76>
>   <2><67>: Abbrev Number: 7 (DW_TAG_template_type_param)
>      <68>   DW_AT_type        : <0x52>
>      <6c>   DW_AT_name        : (indirect string, offset: 0x6c): B
>   <2><70>: Abbrev Number: 6 (DW_TAG_template_type_param)
>      <71>   DW_AT_type        : <0x7d>
>   ...
> 

Hello Nils,

Thanks for working on this.

I'm a bit confused with your example here. You mention that 'A' and 'C'
are missing in the forward declaration, but in the example C++ code you added 'C'. Is that a typo or
did I not understand the problem?


> Befor this patch, GDB would not create any symbols for the read template
> tag DIEs and thus lose knowledge about them.  Breaking at the return
> statement and printing f's type would read
> 
>    (gdb) ptype f
>    type = class Foo<double, int, float> [with B = int] {
>        <no data fields>
>    }
> 
> After this patch GDB does generate symbols from the DWARF (with their
> artificial names:
> 
>    (gdb) ptype f
>    type = class Foo<double, int, float> [with <unnamed0> = double, B = int,
>    <unnamed1> = float] {
>        <no data fields>
>    }
> 
> The same principle theoretically applies to template functions.  Also
> here, GDB would not record unnamed template TAGs but I know of no visual
> way to trigger and test this changed behavior.  Template functions do
> not emit a '[with...]' list and their name generation also does not
> suffer from template tags without names.  GDB does not check whether or
> not a template tag has a name in 'dwarf2_compute_name' and thus, the
> names of the template functions are created independently of whether or
> not the template TAGs have a DW_TAT_name attribute.  A testcase has
> been added in the gdb.dwarf2 for template classes and structs.
> 
> Bug:  https://sourceware.org/bugzilla/show_bug.cgi?id=28396
> ---
>   gdb/dwarf2/read.c                             |  46 ++++-
>   .../missing-type-name-for-templates.exp       | 166 ++++++++++++++++++
>   2 files changed, 211 insertions(+), 1 deletion(-)
>   create mode 100644 gdb/testsuite/gdb.dwarf2/missing-type-name-for-templates.exp
> 
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 23fe5679cbd..44cca4b16b0 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -21870,6 +21870,42 @@ typename_concat (struct obstack *obs, const char *prefix, const char *suffix,
>       }
>   }
>   
> +/* Return a generic name for a DW_TAG_template_type_param or
> +   DW_TAG_template_value_param tag, missing a DW_AT_name attribute.  We do this
> +   per parent, so each function/class/struct template will have their own set
> +   of template parameters named <unnnamed0>, <unnamed1>, ... where the
> +   enumeration starts at 0 and represents the position of the template tag in
> +   the list of unnamed template tags for this parent, counting both, type and
> +   value tags.  */
> +
> +static const char *
> +unnamed_template_tag_name (die_info *die, dwarf2_cu *cu)
> +{
> +  if (die->parent == nullptr)
> +    return nullptr;
> +
> +  /* Count the parent types unnamed template type and value children until, we
> +     arrive at our entry.  */
> +  size_t nth_unnamed = 0;
> +
> +  die_info* child = die->parent->child;
> +  while (child != die && child != nullptr)
> +  {
> +    if (child->tag == DW_TAG_template_type_param
> +	|| child->tag == DW_TAG_template_value_param)
> +      {
> +	if (dwarf2_attr (child, DW_AT_name, cu) == nullptr)
> +	  ++nth_unnamed;
> +      }
> +    child = child->sibling;
> +  }
> +  gdb_assert (child == die);
> +
> +  const std::string name_str = "<unnamed" + std::to_string (nth_unnamed) + ">";
> +  return obstack_strndup (&cu->per_objfile->objfile->per_bfd->storage_obstack,
> +			  name_str.c_str (), name_str.size ());
> +}
> +
>   /* Get name of a die, return NULL if not found.  */
>   
>   static const char *
> @@ -21905,7 +21941,9 @@ dwarf2_name (struct die_info *die, struct dwarf2_cu *cu)
>         && die->tag != DW_TAG_interface_type
>         && die->tag != DW_TAG_structure_type
>         && die->tag != DW_TAG_namelist
> -      && die->tag != DW_TAG_union_type)
> +      && die->tag != DW_TAG_union_type
> +      && die->tag != DW_TAG_template_type_param
> +      && die->tag != DW_TAG_template_value_param)
>       return NULL;
>   
>     switch (die->tag)
> @@ -21925,6 +21963,12 @@ dwarf2_name (struct die_info *die, struct dwarf2_cu *cu)
>   	return attr_name;
>         return CP_ANONYMOUS_NAMESPACE_STR;
>   
> +    /* DWARF does not actually require template tags to have a name.  */
> +    case DW_TAG_template_type_param:
> +    case DW_TAG_template_value_param:
> +      if (attr_name == nullptr)
> +	return unnamed_template_tag_name (die, cu);
> +      /* FALLTHROUGH.  */
>       case DW_TAG_class_type:
>       case DW_TAG_interface_type:
>       case DW_TAG_structure_type:
> diff --git a/gdb/testsuite/gdb.dwarf2/missing-type-name-for-templates.exp b/gdb/testsuite/gdb.dwarf2/missing-type-name-for-templates.exp
> new file mode 100644
> index 00000000000..14bf3dc7175
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/missing-type-name-for-templates.exp
> @@ -0,0 +1,166 @@
> +# 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/>.
> +
> +# This tests GDB's handling of DW_TAG_template_type_parameter and
> +# DW_TAG_template_value_parameter tags that do not have a DW_AT_name attribute.
> +# The attribute is optional for the tags and a bug about GDB not recording
> +# the respective symbols (because of the missing name) was reported in PR
> +# gdb/28396.
> +
> +load_lib dwarf.exp
> +
> +if {![dwarf2_support]} {
> +    return 0
> +}
> +
> +standard_testfile main.c .S
> +
> +set asm_file [standard_output_file $srcfile2]
> +Dwarf::assemble $asm_file {
> +    cu {} {
> +	DW_TAG_compile_unit {
> +	    {DW_AT_language @DW_LANG_C_plus_plus}
> +	} {
> +	    declare_labels int float template_var1 template_var2 template_var3
> +
> +	    int: DW_TAG_base_type {
> +		{DW_AT_name "int"}
> +		{DW_AT_byte_size 4 DW_FORM_data1}
> +		{DW_AT_encoding @DW_ATE_signed}
> +	    }
> +
> +	    float: base_type {
> +		{DW_AT_name float}
> +		{DW_AT_byte_size 4 DW_FORM_sdata}
> +		{DW_AT_encoding @DW_ATE_float}
> +	    }
> +
> +	    DW_TAG_subprogram {
> +		{MACRO_AT_func "main"}
> +	    }
> +
> +	    # A variable whose type is a template instantiation with two
> +	    # template parameters, one unnamed.
> +	    template_var1: DW_TAG_structure_type {
> +		{DW_AT_name "template_var1<int, float>"}
> +	    } {
> +		DW_TAG_template_type_param {
> +		   {DW_AT_type :$int}
> +		}
> +		DW_TAG_template_type_param {
> +		    {DW_AT_name "second"}
> +		    {DW_AT_type :$float}
> +		}
> +		DW_TAG_member {
> +		    {DW_AT_name "me"}
> +		    {DW_AT_type :$int}
> +		}
> +		DW_TAG_member {
> +		    {DW_AT_name "me2"}
> +		    {DW_AT_type :$float}
> +		}
> +	    }
> +	    DW_TAG_variable {
> +		{DW_AT_name "var1"}
> +		{DW_AT_type :$template_var1}
> +	    }
> +
> +	    # A variable whose type is a template instantiation with two
> +	    # template parameters, both unnamed.
> +	    template_var2: DW_TAG_class_type {
> +		{DW_AT_name "template_var2<int, float>"}
> +	    } {
> +		DW_TAG_template_type_param {
> +		   {DW_AT_type :$int}
> +		}
> +		DW_TAG_member {
> +		    {DW_AT_name "me"}
> +		    {DW_AT_type :$int}
> +		}
> +		DW_TAG_template_type_param {
> +		    {DW_AT_type :$float}
> +		}
> +		DW_TAG_member {
> +		    {DW_AT_name "me2"}
> +		    {DW_AT_type :$float}
> +		}
> +	    }
> +	    DW_TAG_variable {
> +		{DW_AT_name "var2"}
> +		{DW_AT_type :$template_var2}
> +	    }
> +
> +	    # A variable whose type is a template instantiation with four
> +	    # template arguments, two types, two values, all unnamed.
> +	    template_var3: DW_TAG_structure_type {
> +		{DW_AT_name "template_var3"}
> +	    } {
> +		DW_TAG_member {
> +		    {DW_AT_name "me"}
> +		    {DW_AT_type :$int}
> +		}
> +		DW_TAG_template_value_param {
> +		   {DW_AT_type :$int}
> +		   {DW_AT_const_value 0 DW_FORM_sdata}
> +		}
> +		DW_TAG_template_type_param {
> +		    {DW_AT_type :$int}
> +		}
> +		DW_TAG_template_value_param {
> +		    {DW_AT_type :$float}
> +		    {DW_AT_const_value 11 DW_FORM_sdata}
> +		}
> +		DW_TAG_template_type_param {
> +		    {DW_AT_type :$float}
> +		}
> +		DW_TAG_member {
> +		    {DW_AT_name "me2"}
> +		    {DW_AT_type :$float}
> +		}
> +	    }
> +	    DW_TAG_variable {
> +		{DW_AT_name "var3"}
> +		{DW_AT_type :$template_var3}
> +	    }
> +	}
> +    }
> +}
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} \
> +	 [list $srcfile $asm_file] {nodebug}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    return -1
> +}
> +
> +gdb_test "ptype var1" [multi_line \
> +    "type = struct template_var1<int, float> \\\[with <unnamed0> = int, second = float\\\] {" \
> +    "    <unnamed0> me;" \
> +    "    second me2;" \
> +    "}"]
> +
> +gdb_test "ptype var2" [multi_line \
> +    "type = class template_var2<int, float> \\\[with <unnamed0> = int, <unnamed1> = float\\\] {" \
> +    "    <unnamed0> me;" \
> +    "    <unnamed1> me2;" \
> +    "}"]
> +
> +gdb_test "ptype var3" [multi_line \
> +    "type = struct template_var3<0, int, 11, float> \\\[with <unnamed1> = int, <unnamed3> = float\\\] {" \
> +    "    <unnamed1> me;" \
> +    "    <unnamed3> me2;" \
> +    "}"]

This last test is failing on my system, a RHEL 8.7, using both gcc 8.5.0 and gcc 12.1.1;
the failure looks like this:

```
ptype var3
type = struct template_var3<0, int, 1093664768, float> [with <unnamed1> = int, <unnamed3> = float] {
     <unnamed1> me;
     <unnamed3> me2;
}
(gdb) FAIL: gdb.dwarf2/missing-type-name-for-templates.exp: ptype var3
```

Notice the incorrect third parameter.

Finally, it would be nice if you could provide a .cc file that could be somewhat similar to the
DWARF you have described in the .exp file? It would help confirming that we (or at least me)
understand what you want to test, and makes it easier for future developers to understand your test.

Cheers!
Bruno Larsen



More information about the Gdb-patches mailing list