[binutils-gdb] [gdb] Enable ptype /o for some dynamic types

Tom de Vries via Gdb-cvs <[email protected]>
Newsgroups gmane.comp.gdb.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=6f24c6d23f96ec1a520f43450ae51cfa982a37d3

commit 6f24c6d23f96ec1a520f43450ae51cfa982a37d3
Author: Tom de Vries <[email protected]>
Date:   Fri Apr 3 15:19:26 2026 +0200

    [gdb] Enable ptype /o for some dynamic types
    
    Printing the offsets of a struct containing a flexible array member using
    "ptype /o" currently fails:
    ...
    $ cat test.c
    struct s {
      int a;
      int b[];
    };
    struct s foo;
    $ gcc -g test.c -c
    $ gdb -q -batch test.o -ex "ptype /o struct s"
    warning: ptype/o does not work with dynamic types; disabling '/o'
    type = struct s {
        int a;
        int b[];
    }
    ...
    
    This has been the case since gdb 14, containing commit 0c1aa2a0953 ("Disable
    ptype/o for dynamic types").
    
    If we revert the commit, we get instead:
    ...
    $ gdb -q -batch test.o -ex "ptype /o struct s"
    /* offset      |    size */  type = struct s {
    /*      0      |       4 */    int a;
    /*      4      |       0 */    int b[];
    
                                   /* total size (bytes):    4 */
                                 }
    ...
    which is similar to what pahole prints:
    ...
    struct s {
            int                        a;                    /*     0     4 */
            int                        b[];                  /*     4     0 */
    
            /* size: 4, cachelines: 1, members: 2 */
            /* last cacheline: 4 bytes */
    };
    ...
    
    The problem is that the commit uses is_dynamic_type:
    ...
      if (flags.print_offsets && is_dynamic_type (type))
        {
          warning (_("ptype/o does not work with dynamic types; disabling '/o'"));
          flags.print_offsets = 0;
        }
    ...
    which is too restrictive.
    
    Fix this by using a new function cannot_print_offsets instead.
    
    Reviewed-By: Guinevere Larsen <[email protected]>
    
    Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33966

Diff:
---
 gdb/gdbtypes.c                             | 39 ++++++++++++++++--
 gdb/gdbtypes.h                             |  4 ++
 gdb/testsuite/gdb.ada/ptype-o.exp          |  2 +-
 gdb/testsuite/gdb.base/ptype-offsets-c.c   | 37 ++++++++++++++++++
 gdb/testsuite/gdb.base/ptype-offsets-c.exp | 63 ++++++++++++++++++++++++++++++
 gdb/typeprint.c                            |  4 +-
 6 files changed, 143 insertions(+), 6 deletions(-)

diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 48fe95c79ca..10a1fdd892e 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1910,10 +1910,11 @@ array_type_has_dynamic_stride (struct type *type)
   return prop != nullptr && prop->is_constant ();
 }
 
-/* Worker for is_dynamic_type.  */
+/* Worker for is_dynamic_type/cannot_print_offsets.  */
 
 static bool
-is_dynamic_type_internal_1 (struct type *type)
+is_dynamic_type_internal_1 (struct type *type,
+			    bool cannot_print_offsets_p = false)
 {
   type = check_typedef (type);
 
@@ -1988,7 +1989,24 @@ is_dynamic_type_internal_1 (struct type *type)
 	      continue;
 	    /* If the field has dynamic type, then so does TYPE.  */
 	    if (is_dynamic_type_internal_1 (f.type ()))
-	      return true;
+	      {
+		bool last_struct_field_p
+		  = (type->code () == TYPE_CODE_STRUCT
+		     && i == type->num_fields () - 1);
+		if (cannot_print_offsets_p && last_struct_field_p)
+		  {
+		    if (f.type ()->code () == TYPE_CODE_STRUCT)
+		      /* The last field is a dynamic type and a struct.  Check
+			 if we can print the offsets for the struct.  */
+		      return is_dynamic_type_internal_1 (f.type (), true);
+
+		    /* The last field is a dynamic type, this is ok to print
+		       offsets for.  */
+		    return false;
+		  }
+
+		return true;
+	      }
 	    /* If the field is at a fixed offset, then it is not
 	       dynamic.  */
 	    if (!f.loc_is_dwarf_block ())
@@ -2031,6 +2049,21 @@ is_dynamic_type (struct type *type)
   return is_dynamic_type_internal (type, true);
 }
 
+/* See gdbtypes.h.  */
+
+bool
+cannot_print_offsets (struct type *type)
+{
+  type = check_typedef (type);
+
+  /* We only want to recognize references and pointers at the outermost
+     level.  */
+  if (type->is_pointer_or_reference ())
+    type = check_typedef (type->target_type ());
+
+  return is_dynamic_type_internal_1 (type, true);
+}
+
 static struct type *resolve_dynamic_type_internal
   (struct type *type, const property_addr_info *addr_stack,
    const frame_info_ptr &frame, bool top_level);
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 139467cfd85..5ebf3a116e3 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -2616,6 +2616,10 @@ extern struct type *resolve_dynamic_type
    "dynamic".  */
 extern bool is_dynamic_type (struct type *type);
 
+/* Return true if TYPE cannot be printed using ptype /o.  */
+
+extern bool cannot_print_offsets (struct type *type);
+
 /* Resolve any dynamic components of FIELD.  FIELD is updated.
    ADDR_STACK and FRAME are used where necessary to supply information
    for the resolution process; see resolve_dynamic_type.
diff --git a/gdb/testsuite/gdb.ada/ptype-o.exp b/gdb/testsuite/gdb.ada/ptype-o.exp
index 3bac7930762..03de262ecc1 100644
--- a/gdb/testsuite/gdb.ada/ptype-o.exp
+++ b/gdb/testsuite/gdb.ada/ptype-o.exp
@@ -37,7 +37,7 @@ foreach_gnat_encoding scenario flags {all minimal} {
 	"Warning: the current language does not match this frame."
 
     if {$scenario == "minimal"} {
-	set exp "ptype/o does not work with dynamic types.*"
+	set exp "ptype/o does not work with this dynamic type.*"
     } else {
 	# In "all" mode this prints nonsense, but at least does not
 	# crash.
diff --git a/gdb/testsuite/gdb.base/ptype-offsets-c.c b/gdb/testsuite/gdb.base/ptype-offsets-c.c
new file mode 100644
index 00000000000..74ac9ed7b37
--- /dev/null
+++ b/gdb/testsuite/gdb.base/ptype-offsets-c.c
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 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/>.  */
+
+struct flexible_array_member
+{
+  int an_int;
+  int fam[];
+};
+
+struct nested_flexible_array_member
+{
+  int another_int;
+  struct flexible_array_member sfam;
+};
+
+int
+main (void)
+{
+  struct flexible_array_member fam;
+  struct nested_flexible_array_member nfam;
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/ptype-offsets-c.exp b/gdb/testsuite/gdb.base/ptype-offsets-c.exp
new file mode 100644
index 00000000000..cffcbbeb2f7
--- /dev/null
+++ b/gdb/testsuite/gdb.base/ptype-offsets-c.exp
@@ -0,0 +1,63 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2026 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 testcase exercises the "ptype /o" feature, which can be used to
+# print the offsets and sizes of each field of a struct/union.
+#
+# This is similar to ptype-offsets.exp, which uses C++ instead of C.
+
+standard_testfile .c
+
+# Test only works on LP64 targets.  That's how we guarantee that the
+# expected holes will be present in the struct.
+if { ![is_lp64_target] } {
+    untested "test work only on lp64 targets"
+    return 0
+}
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
+    return -1
+}
+
+# Tests handling flexible array member.  Regression tests for PR gdb/33966.
+set l {
+    "ptype /o struct flexible_array_member"
+    "/* offset      |    size */  type = struct flexible_array_member {"
+    "/*      0      |       4 */    int an_int;"
+    "/*      4      |       0 */    int fam[];"
+    ""
+    "                               /* total size (bytes):    4 */"
+    "                             }"
+}
+gdb_test "ptype /o struct flexible_array_member" \
+    [string_to_regexp [multi_line {*}$l]]
+
+set l {
+    "/* offset      |    size */  type = struct nested_flexible_array_member {"
+    "/*      0      |       4 */    int another_int;"
+    "/*      4      |       4 */    struct flexible_array_member {"
+    "/*      4      |       4 */        int an_int;"
+    "/*      8      |       0 */        int fam[];"
+    ""
+    "                                   /* total size (bytes):    4 */"
+    "                               } sfam;"
+    ""
+    "                               /* total size (bytes):    8 */"
+    "                             }"
+}
+gdb_test "ptype /o struct nested_flexible_array_member" \
+    [string_to_regexp [multi_line {*}$l]]
diff --git a/gdb/typeprint.c b/gdb/typeprint.c
index e53a2b21af5..31a9c1814a3 100644
--- a/gdb/typeprint.c
+++ b/gdb/typeprint.c
@@ -451,9 +451,9 @@ whatis_exp (const char *exp, int show)
       type = val->type ();
     }
 
-  if (flags.print_offsets && is_dynamic_type (type))
+  if (flags.print_offsets && cannot_print_offsets (type))
     {
-      warning (_("ptype/o does not work with dynamic types; disabling '/o'"));
+      warning (_("ptype/o does not work with this dynamic type; disabling '/o'"));
       flags.print_offsets = 0;
     }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.