Bug 33415 - [gdb/symtab] c++ regressions
Summary: [gdb/symtab] c++ regressions
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: symtab (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: 18.1
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 33557 (view as bug list)
Depends on:
Blocks:
 
Reported: 2025-09-11 06:33 UTC by Tom de Vries
Modified: 2025-12-03 08:10 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
Project(s) to access:
ssh public key:


Attachments
Tentative patch (973 bytes, patch)
2025-09-11 08:50 UTC, Tom de Vries
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tom de Vries 2025-09-11 06:33:12 UTC
On openSUSE Leap 15.6 x86_64, I see regressions with current trunk for c++ testcases:
- gdb.base/condbreak-multi-context.exp
- gdb.cp/m-static.exp
- gdb.cp/namespace.exp

First failure:
...
(gdb) print aaa^M
$3 = <optimized out>^M
(gdb) FAIL: gdb.base/condbreak-multi-context.exp: start_before=true: scenario_1: print aaa
...

Variable A::aaa is a static const int:
...
class A : public Base
{
public:
  static const int aaa = 10;

  void func () {}
};
...
with dwarf:
...
 <2><350>: Abbrev Number: 3 (DW_TAG_member)
    <351>   DW_AT_name        : aaa
    <35b>   DW_AT_external    : 1
    <35b>   DW_AT_accessibility: 1      (public)
    <35c>   DW_AT_declaration : 1
    <35c>   DW_AT_const_value : 4 byte block: a 0 0 0
...

Minimal example:
...
$ gdb -q -batch \
    outputs/gdb.base/condbreak-multi-context/condbreak-multi-context \
    -ex "p A::aaa"
$1 = <optimized out>
...

With system gdb (14.2):
...
$ gdb -q -batch \
    outputs/gdb.base/condbreak-multi-context/condbreak-multi-context \
    -ex "p A::aaa"
$1 = 10
...
Comment 1 Tom de Vries 2025-09-11 08:04:17 UTC
(In reply to Tom de Vries from comment #0)
> First failure:
> ...
> (gdb) print aaa^M
> $3 = <optimized out>^M
> (gdb) FAIL: gdb.base/condbreak-multi-context.exp: start_before=true:
> scenario_1: print aaa
> ...

Bisects to:
...
commit 86ac8c546235a67d6a6bb29476a3a9ac8f7a620a
Date:   Thu Jan 2 15:28:18 2025 -0700

    Convert lookup_symbol_in_objfile
...
Comment 2 Tom de Vries 2025-09-11 08:20:08 UTC
No regression on Tumbleweed with gcc 15.2.  There we have:
...
 <2><356>: Abbrev Number: 2 (DW_TAG_variable)
    <357>   DW_AT_name        : aaa
    <35c>   DW_AT_linkage_name: (indirect string, offset: 0x4ae): _ZN1A3aaaE
    <364>   DW_AT_external    : 1
    <364>   DW_AT_accessibility: 1      (public)
    <364>   DW_AT_declaration : 1
    <364>   DW_AT_const_value : 10
...
and there are entries in the cooked index representing aaa:
...
    [45] ((cooked_index_entry *) 0x7facf0004730)
    name:       _ZN1A3aaaE
    canonical:  _ZN1A3aaaE
    qualified:  _ZN1A3aaaE
    DWARF tag:  DW_TAG_variable
    flags:      0x4 [IS_LINKAGE]
    DIE offset: 0x356
    parent:     ((cooked_index_entry *) 0)
    [52] ((cooked_index_entry *) 0x7facf0004700)
    name:       aaa
    canonical:  aaa
    qualified:  A::aaa
    DWARF tag:  DW_TAG_variable
    flags:      0x0 []
    DIE offset: 0x356
    parent:     ((cooked_index_entry *) 0x7facf00046d0) [A]
...


So, maybe the problem is that the DW_TAG_member doesn't result in a cooked index entry.  Indeed, it's not listed in tag_interesting_for_index.
Comment 3 Tom de Vries 2025-09-11 08:50:34 UTC
Created attachment 16334 [details]
Tentative patch
Comment 5 Tom Tromey 2025-09-11 14:03:01 UTC
FWIW this is a compiler bug.
A C++ class variable should be emitted using DW_TAG_variable.

If we want to work around this bug, the workaround should
probably be a bit more targeted.  Like, only DW_TAG_member
with a DW_AT_const_value -- though I'm not sure if that is
sufficient, what does that compiler emit for an ordinary
class variable (one with a separate definition)?
Comment 6 Tom de Vries 2025-09-11 14:53:04 UTC
(In reply to Tom Tromey from comment #5)
> FWIW this is a compiler bug.
> A C++ class variable should be emitted using DW_TAG_variable.
> 
> If we want to work around this bug, the workaround should
> probably be a bit more targeted.  Like, only DW_TAG_member
> with a DW_AT_const_value -- though I'm not sure if that is
> sufficient, what does that compiler emit for an ordinary
> class variable (one with a separate definition)?

[ AFAIU, the affected compilers are gcc 7 - 15, with -gdwarf-4. ]

For:
...
class A
{
public:
  static int aaa;
};

int A::aaa = 10;

int
main (void)
{
  return A::aaa;
}
...
I get:
...
 <1><316>: Abbrev Number: 5 (DW_TAG_variable)
    <317>   DW_AT_specification: <0x302>
    <31b>   DW_AT_decl_line   : 7
    <31c>   DW_AT_linkage_name: (indirect string, offset: 0x4d7): _ZN1A3aaaE
    <320>   DW_AT_location    : 9 byte block: 3 10 20 40 0 0 0 0 0      (DW_OP_addr: 402010)
...
and:
...
 <2><302>: Abbrev Number: 3 (DW_TAG_member)
    <303>   DW_AT_name        : aaa
    <307>   DW_AT_decl_file   : 1
    <308>   DW_AT_decl_line   : 4
    <309>   DW_AT_type        : <0x30f>
    <30d>   DW_AT_external    : 1
    <30d>   DW_AT_accessibility: 1      (public)
    <30e>   DW_AT_declaration : 1
...
and that works fine without the fix proposed in this PR.

So I think the more targeted approach could work.

This works for me:
...
diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c
index ec173bb00ee..74cb44698ec 100644
--- a/gdb/dwarf2/abbrev.c
+++ b/gdb/dwarf2/abbrev.c
@@ -120,6 +120,7 @@ abbrev_table::read (struct dwarf2_section_info *section,
       bool has_name = false;
       bool has_linkage_name = false;
       bool has_external = false;
+      bool has_const_value = false;
 
       /* Now read in declarations.  */
       int num_attrs = 0;
@@ -177,6 +178,10 @@ abbrev_table::read (struct dwarf2_section_info *section,
 	      if (is_csize && cur_attr.form == DW_FORM_ref4)
 		sibling_offset = size;
 	      break;
+
+	    case DW_AT_const_value:
+	      has_const_value = true;
+	      break;
 	    }
 
 	  switch (cur_attr.form)
@@ -245,7 +250,7 @@ abbrev_table::read (struct dwarf2_section_info *section,
 	}
       else if (has_hardcoded_declaration
 	       && ((cur_abbrev->tag != DW_TAG_variable
-		    && cur_abbrev->tag != DW_TAG_member)
+		    && (cur_abbrev->tag != DW_TAG_member || !has_const_value))
 		   || !has_external))
 	{
 	  cur_abbrev->interesting = false;
...
Comment 7 Tom de Vries 2025-11-13 09:53:05 UTC
*** Bug 33557 has been marked as a duplicate of this bug. ***
Comment 8 Tom de Vries 2025-11-13 09:54:25 UTC
Setting target milestone to 18.1, as in PR33557.
Comment 9 Sourceware Commits 2025-12-03 08:07:40 UTC
The master branch has been updated by Tom de Vries <vries@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=d03293898d81e4273ac7f9163a8186dba46db0e5

commit d03293898d81e4273ac7f9163a8186dba46db0e5
Author: Tom de Vries <tdevries@suse.de>
Date:   Wed Dec 3 09:07:30 2025 +0100

    [gdb/symtab] Fix DW_TAG_member regression
    
    On openSUSE Leap 15.6 x86_64, with gcc 7 and test-case
    gdb.base/condbreak-multi-context.exp I run into:
    ...
    (gdb) print aaa^M
    $3 = <optimized out>^M
    (gdb) FAIL: $exp: start_before=true: scenario_1: print aaa
    ...
    
    This is a regression since commit 86ac8c54623 ("Convert
    lookup_symbol_in_objfile").
    
    Likewise in test-cases gdb.cp/m-static.exp and gdb.cp/namespace.exp.
    
    The failure is specific to using Dwarf v4:
    - using target board unix/gdb:debug_flags=-gdwarf-5 fixes it
    - using target board unix/gdb:debug_flags=-gdwarf-4 on Tumbleweed (with gcc 15
      and Dwarf v5 default) triggers it
    
    The variable we're trying to print, A::aaa is a static const int member:
    ...
    class A : public Base
    {
    public:
      static const int aaa = 10;
      ...
    };
    ...
    
    With Dwarf v5, we have this DIE:
    ...
    <2><356>: Abbrev Number: 2 (DW_TAG_variable)
        <357>   DW_AT_name        : aaa
        <35c>   DW_AT_linkage_name: _ZN1A3aaaE
        <364>   DW_AT_external    : 1
        <364>   DW_AT_accessibility: 1      (public)
        <364>   DW_AT_declaration : 1
        <364>   DW_AT_const_value : 10
    ...
    and the cooked index contains these corresponding entries:
    ...
        [45] ((cooked_index_entry *) 0x7facf0004730)
        name:       _ZN1A3aaaE
        canonical:  _ZN1A3aaaE
        qualified:  _ZN1A3aaaE
        DWARF tag:  DW_TAG_variable
        flags:      0x4 [IS_LINKAGE]
        DIE offset: 0x356
        parent:     ((cooked_index_entry *) 0)
        [52] ((cooked_index_entry *) 0x7facf0004700)
        name:       aaa
        canonical:  aaa
        qualified:  A::aaa
        DWARF tag:  DW_TAG_variable
        flags:      0x0 []
        DIE offset: 0x356
        parent:     ((cooked_index_entry *) 0x7facf00046d0) [A]
    ...
    
    With Dwarf v4, we have instead the following DIE:
    ...
     <2><350>: Abbrev Number: 3 (DW_TAG_member)
        <351>   DW_AT_name        : aaa
        <35b>   DW_AT_external    : 1
        <35b>   DW_AT_accessibility: 1      (public)
        <35c>   DW_AT_declaration : 1
        <35c>   DW_AT_const_value : 4 byte block: a 0 0 0
    ...
    and there are no corresponding entries.
    
    Fix this by adding an entry:
    ...
        [47] ((cooked_index_entry *) 0x7f5a24004660)
        name:       aaa
        canonical:  aaa
        qualified:  A::aaa
        DWARF tag:  DW_TAG_member
        flags:      0x0 []
        DIE offset: 0x350
        parent:     ((cooked_index_entry *) 0x7f5a24004630) [A]
    ...
    
    Add a regression test in the form of a dwarf assembly test-case printing the
    value of variable A::aaa.
    
    In the test-case, for A::aaa, DW_FORM_flag is used to encode
    DW_AT_declaration.  In v1 of this patch that mattered (because of using
    has_hardcoded_declaration in abbrev_table::read), but that's no longer the
    case.  Nevertheless, also add an A::bbb using FORM_flag_present for
    DW_AT_declaration (and while we're at it, DW_AT_external as well).
    
    Also add two variants, for which <optimized out> is printed:
    - A::ccc: a variant with DW_AT_location instead of DW_AT_const_value, and
    - A::ddd: a variant without external.
    This behavior is not part of the regression.  I've reproduced it using a
    system gdb based on 14.2.  It's also not specific to using the cooked index,
    it reproduces with -readnow as well.
    
    Tested on x86_64-linux.
    
    Approved-By: Tom Tromey <tom@tromey.com>
    
    PR symtab/33415
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33415
Comment 10 Tom de Vries 2025-12-03 08:10:01 UTC
Fixed.