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

Kempke, Nils-Christian nils-christian.kempke@intel.com
Thu Jul 14 07:33:12 GMT 2022


Hi Bruno,

Thanks for the review!

> -----Original Message-----
> From: Bruno Larsen <blarsen@redhat.com>
> Sent: Wednesday, July 13, 2022 9:08 PM
> To: Kempke, Nils-Christian <nils-christian.kempke@intel.com>; gdb-
> patches@sourceware.org
> Subject: Re: [PATCH 1/1] gdb, dwarf: create symbols for template tags
> without names
> 
> 
> 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?
> 

No, this is a typo. I intended to also drop 'C' from the second declaration ..
Then it also should make more sense with the DWARF below where only
B is still being emitted but C is missing.

> 
> > 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.
> 

Sure, I'll change the .cc file used! As it anyway seems to have a problem with
with my way of using the 3rd parameter (I wonder why I could not see it -
I'll double check again with other gcc's as well).

I'll send a v2 then!

Cheers,

Nils
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


More information about the Gdb-patches mailing list