[binutils-gdb] gdb: preserve type instance flags in 'x' command

Tankut Baris Aktemur 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=07519d531b1e858f665ff011d7f1002f38111ec8

commit 07519d531b1e858f665ff011d7f1002f38111ec8
Author: Tankut Baris Aktemur <[email protected]>
Date:   Tue Mar 24 16:56:05 2026 +0100

    gdb: preserve type instance flags in 'x' command
    
    The 'x' command takes an arbitrary expression as its argument,
    evaluates it to a value, and treats the value as the address to read
    from.  How many bytes to read from that address is decided based on
    the specified format, from which a type is derived.  One can see this
    in the `$_` convenience variable that holds the last address read
    from.
    
    For example, suppose we have an integer variable and we want to
    examine the memory where the variable is located.
    
        (gdb) list
        36      int main(int argc, char *argv[]) {
        37        int a = 42;
        38
        39        return 0;
        40      }
        (gdb) print &a
        $1 = (int *) 0x7fffffffdddc
        (gdb)
    
    Note that `&a` gives us an `int *`.  Let's now examine the memory at
    `&a`.
    
        (gdb) x/1dh &a
        0x7fffffffdddc: 42
        (gdb) print $_
        $2 = (int16_t *) 0x7fffffffdddc
        (gdb)
    
    Note the type of `$_`: `int16_t *`, not `int *`, although the argument
    was a `int *`.
    
    This happens because the 'x' command is a low-level command and GDB
    does not care much about the type of the argument; it is rather
    interested in using the evaluated value as an address.  GDB simply
    discards the type of the argument.  The format 'h' (half word)
    determines the type `int16_t`, with which GDB creates a lazy value.
    It is this lazy value with the `int16_t` type that fetches the data
    from the target.
    
    The problem with this is that if the argument type contains address
    class information, the information would be ignored.  Let's use GDB's
    builtin `@data` modifier:
    
        (gdb) x/1dh (@data int *)&a
        0x7fffffffdddc: 42
        (gdb) print $_
        $3 = (int16_t *) 0x7fffffffdddc
        (gdb)
    
    The `@data` modifier was dropped as can be seen in the type of `$_`.
    On an architecture where data and code pointers have to be
    distinguished, or where architecture-specific address classes are
    available, the 'x' command would not work.
    
    Address the problem by propagating the type instance flags of the
    argument to the type constructed from the format.
    
    With this patch, we get:
    
        (gdb) x/1dh (@data int *)&a
        0x7fffffffdddc: 42
        (gdb) print $_
        $3 = (@data int16_t *) 0x7fffffffdddc
        (gdb)
    
    Side note:
      The 'x' command remembers the next address and can be
      re-used without an argument:
    
        (gdb) x
        0x7fffffffddde: 0
        (gdb) x
        0x7fffffffdde0: 1
        (gdb) x
        0x7fffffffdde2: 0
        (gdb) x
        0x7fffffffdde4: 0
        (gdb) print $_
        $5 = (@data int16_t *) 0x7fffffffdde4
        (gdb)
    
      With this patch, it also becomes possible to use the "next address"
      feature of the 'x' command.
    End side note.
    
    A final remark: There exists a `pointer_to_address` gdbarch method.
    One can wonder whether it not already solves the problem.  The thing
    is, that gdbarch method would change the bits of the address and this
    may not be possible/meaningful on some architectures.
    
    Regression-tested on X86-64 Linux.
    
    Approved-By: Tom Tromey <[email protected]>

Diff:
---
 gdb/printcmd.c                                   | 19 +++++++++---
 gdb/testsuite/gdb.base/examine-address-class.c   | 23 +++++++++++++++
 gdb/testsuite/gdb.base/examine-address-class.exp | 37 ++++++++++++++++++++++++
 3 files changed, 75 insertions(+), 4 deletions(-)

diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 49e97ca336c..38a2e231d88 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -988,10 +988,11 @@ find_string_backward (struct gdbarch *gdbarch,
 }
 
 /* Given format FMT and architecture GDBARCH, return the corresponding
-   value type.  */
+   value type.  Annotate the resulting type with FLAGS as the type
+   instance flags.  */
 
 static type *
-format_to_type (format_data fmt, gdbarch *gdbarch)
+format_to_type (format_data fmt, gdbarch *gdbarch, type_instance_flags flags)
 {
   char format = fmt.format;
   char size = fmt.size;
@@ -1054,6 +1055,8 @@ format_to_type (format_data fmt, gdbarch *gdbarch)
     }
 
   gdb_assert (val_type != nullptr);
+  val_type = make_type_with_address_space (val_type, flags);
+
   return val_type;
 }
 
@@ -1884,7 +1887,11 @@ x_command (const char *exp, int from_tty)
       else
 	next_address = value_as_address (val);
 
-      next_type = format_to_type (fmt, expr->gdbarch);
+      type_instance_flags flags = 0;
+      if (val->type ()->is_pointer_or_reference ())
+	flags = val->type ()->target_type ()->instance_flags ();
+
+      next_type = format_to_type (fmt, expr->gdbarch, flags);
     }
 
   if (next_type == nullptr)
@@ -2167,7 +2174,11 @@ do_one_display (struct display *d)
 	  if (d->format.format == 'i')
 	    addr = gdbarch_addr_bits_remove (d->exp->gdbarch, addr);
 
-	  next_type = format_to_type (d->format, d->exp->gdbarch);
+	  type_instance_flags flags = 0;
+	  if (val->type ()->is_pointer_or_reference ())
+	    flags = val->type ()->target_type ()->instance_flags ();
+
+	  next_type = format_to_type (d->format, d->exp->gdbarch, flags);
 	  next_address = addr;
 	  do_examine_next_address (d->format);
 	}
diff --git a/gdb/testsuite/gdb.base/examine-address-class.c b/gdb/testsuite/gdb.base/examine-address-class.c
new file mode 100644
index 00000000000..c868b10119f
--- /dev/null
+++ b/gdb/testsuite/gdb.base/examine-address-class.c
@@ -0,0 +1,23 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2025-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/>.  */
+
+int
+main (void)
+{
+  int var = 42;
+  return 0; /* break-here.  */
+}
diff --git a/gdb/testsuite/gdb.base/examine-address-class.exp b/gdb/testsuite/gdb.base/examine-address-class.exp
new file mode 100644
index 00000000000..058b2d5e0b2
--- /dev/null
+++ b/gdb/testsuite/gdb.base/examine-address-class.exp
@@ -0,0 +1,37 @@
+# Copyright (C) 2025-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/>.
+
+# Verify that 'x' command preserves the type instance flags of its
+# argument.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} {
+    return
+}
+
+if {![runto_main]} {
+    return
+}
+
+set linenum [gdb_get_line_number "break-here"]
+gdb_test "until $linenum" "break-here.*" "run until the line"
+
+gdb_test "x/1dh (int *) &var" "$hex:\[ \t\]+42"
+gdb_test "print \$_" "\\(int16_t \\*\\) $hex" "type matches the format"
+
+gdb_test "x/1dh (@data int *) &var" "$hex:\[ \t\]+42"
+gdb_test "print \$_" "\\(@data int16_t \\*\\) $hex" \
+    "type instance flags are preserved"
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.