[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