public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
@ 2023-11-08  5:00 Simon Marchi
  2023-11-08  5:00 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
                   ` (26 more replies)
  0 siblings, 27 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

This series fixes reading/writing pseudo registers from/to non-current
frames (that is, frames other than frame 0).  Currently, we get this:

    (gdb) frame 0
    #0  break_here_asm () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:38
    38              pop %rbx
    (gdb) p/x $rbx
    $1 = 0x2021222324252627
    (gdb) p/x $ebx
    $2 = 0x24252627
    (gdb) frame 1
    #1  0x000055555555517d in caller () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:58
    58              call callee
    (gdb) p/x $rbx
    $3 = 0x1011121314151617
    (gdb) p/x $ebx
    $4 = 0x24252627

This is a bit surprising, we would expect the last value to be
0x14151617, the bottom half of the rbx value from frame 1 (the currently
selected frame at that point).  Instead, we got the bottom half of the
rbx value from frame 0.  This is because pseudo registers are always
read/written from/to the current thread's regcache.

This series fixes this (as well as writing to pseudo registers) by
making it so pseudo registers are read/written using a frame.

Simon Marchi (24):
  gdb: don't handle i386 k registers as pseudo registers
  gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h
  gdb: make store_integer take an array_view
  gdb: simplify conditions in
    regcache::{read,write,raw_collect,raw_supply}_part
  gdb: change regcache interface to use array_view
  gdb: fix bugs in {get,put}_frame_register_bytes
  gdb: make put_frame_register take an array_view
  gdb: change value_of_register and value_of_register_lazy to take the
    next frame
  gdb: remove frame_register
  gdb: make put_frame_register take the next frame
  gdb: make put_frame_register_bytes take the next frame
  gdb: make get_frame_register_bytes take the next frame
  gdb: add value::allocate_register
  gdb: read pseudo register through frame
  gdb: change parameter name in frame_unwind_register_unsigned
    declaration
  gdb: rename gdbarch_pseudo_register_write to
    gdbarch_deprecated_pseudo_register_write
  gdb: add gdbarch_pseudo_register_write that takes a frame
  gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write
  gdb: make aarch64_za_offsets_from_regnum return za_offsets
  gdb: add missing raw register read in
    aarch64_sme_pseudo_register_write
  gdb: migrate aarch64 to new gdbarch_pseudo_register_write
  gdb: migrate arm to gdbarch_pseudo_register_read_value
  gdb: migrate arm to new gdbarch_pseudo_register_write
  gdb/testsuite: add tests for unwinding of pseudo registers

 gdb/aarch64-tdep.c                            | 297 +++++-----
 gdb/alpha-tdep.c                              |  11 +-
 gdb/amd64-tdep.c                              |  82 +--
 gdb/arch/arm-get-next-pcs.c                   |   6 +-
 gdb/arch/arm-get-next-pcs.h                   |   5 +-
 gdb/arch/arm.c                                |   2 +-
 gdb/arch/arm.h                                |   4 +-
 gdb/arm-linux-tdep.c                          |  11 +-
 gdb/arm-tdep.c                                | 145 +++--
 gdb/avr-tdep.c                                |   3 +-
 gdb/bfin-tdep.c                               |   3 +-
 gdb/csky-tdep.c                               |   4 +-
 gdb/defs.h                                    |  39 +-
 gdb/dwarf2/expr.c                             |  22 +-
 gdb/dwarf2/frame.c                            |   5 +-
 gdb/eval.c                                    |   3 +-
 gdb/findvar.c                                 |  50 +-
 gdb/frame-unwind.c                            |   3 +-
 gdb/frame.c                                   | 174 +++---
 gdb/frame.h                                   |  28 +-
 gdb/frv-tdep.c                                |   3 +-
 gdb/gdbarch-gen.h                             |  28 +-
 gdb/gdbarch.c                                 |  40 +-
 gdb/gdbarch_components.py                     |  29 +-
 gdb/guile/scm-frame.c                         |   3 +-
 gdb/h8300-tdep.c                              |   3 +-
 gdb/i386-tdep.c                               | 380 ++++--------
 gdb/i386-tdep.h                               |  15 +-
 gdb/i387-tdep.c                               |  16 +-
 gdb/ia64-tdep.c                               |  18 +-
 gdb/infcmd.c                                  |   6 +-
 gdb/loongarch-tdep.c                          |   3 +-
 gdb/m32c-tdep.c                               |   3 +-
 gdb/m68hc11-tdep.c                            |   3 +-
 gdb/m68k-tdep.c                               |  17 +-
 gdb/mep-tdep.c                                |   3 +-
 gdb/mi/mi-main.c                              |   3 +-
 gdb/mips-tdep.c                               |  29 +-
 gdb/msp430-tdep.c                             |   3 +-
 gdb/nat/aarch64-hw-point.c                    |   3 +-
 gdb/nat/aarch64-scalable-linux-ptrace.c       |  20 +-
 gdb/nat/linux-btrace.c                        |   3 +-
 gdb/nds32-tdep.c                              |   8 +-
 gdb/python/py-frame.c                         |   3 +-
 gdb/python/py-unwind.c                        |   4 +-
 gdb/regcache.c                                | 548 +++++++++++-------
 gdb/regcache.h                                | 113 +++-
 gdb/riscv-tdep.c                              |  13 +-
 gdb/rl78-tdep.c                               |   3 +-
 gdb/rs6000-tdep.c                             |  21 +-
 gdb/s12z-tdep.c                               |   2 +-
 gdb/s390-tdep.c                               |   3 +-
 gdb/sh-tdep.c                                 |   9 +-
 gdb/sparc-tdep.c                              |   3 +-
 gdb/sparc64-tdep.c                            |   3 +-
 gdb/std-regs.c                                |  11 +-
 .../gdb.arch/aarch64-pseudo-unwind-asm.S      |  82 +++
 .../gdb.arch/aarch64-pseudo-unwind.c          |  33 ++
 .../gdb.arch/aarch64-pseudo-unwind.exp        |  90 +++
 .../gdb.arch/amd64-pseudo-unwind-asm.S        |  66 +++
 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c  |  33 ++
 .../gdb.arch/amd64-pseudo-unwind.exp          |  91 +++
 .../gdb.arch/arm-pseudo-unwind-asm.S          |  81 +++
 .../gdb.arch/arm-pseudo-unwind-legacy-asm.S   |  84 +++
 .../gdb.arch/arm-pseudo-unwind-legacy.c       |  33 ++
 .../gdb.arch/arm-pseudo-unwind-legacy.exp     |  86 +++
 gdb/testsuite/gdb.arch/arm-pseudo-unwind.c    |  33 ++
 gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp  |  88 +++
 gdb/valops.c                                  |  31 +-
 gdb/value.c                                   | 149 +++++
 gdb/value.h                                   |  64 +-
 gdb/xtensa-tdep.c                             |   3 +-
 gdbserver/linux-arm-low.cc                    |   4 +-
 gdbserver/regcache.cc                         |  69 ++-
 gdbserver/regcache.h                          |   6 +-
 gdbsupport/common-regcache.cc                 |   2 +-
 gdbsupport/common-regcache.h                  |  58 +-
 gdbsupport/rsp-low.cc                         |   8 +
 gdbsupport/rsp-low.h                          |   2 +
 79 files changed, 2324 insertions(+), 1144 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.c
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.exp
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.c
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.exp
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.c
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp


base-commit: 1185b5b79a12ba67eb60bee3f75babf7a222fde0
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-11 19:29   ` John Baldwin
  2023-11-08  5:00 ` [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h Simon Marchi
                   ` (25 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

I think that i386 k registers are raw registers, and therefore shouldn't
be handled in the various functions handling pseudo registers.

What tipped me off is the code in i386_pseudo_register_read_into_value:

      else if (i386_k_regnum_p (gdbarch, regnum))
	{
	  regnum -= tdep->k0_regnum;

	  /* Extract (always little endian).  */
	  status = regcache->raw_read (tdep->k0_regnum + regnum, raw_buf);

We take regnum (the pseudo register number we want to read), subtract
k0_regnum, add k0_regnum, and pass the result to raw_read.  So we would
end up calling raw_read with the same regnum as the function received
which is supposedly a pseudo register number.

Other hints are:

 - The command `maint print raw-registers` shows the k registers.
 - Printing $k0 doesn't cause i386_pseudo_register_read_into_value to be
   called.
 - There's code in i387-tdep.c to save/restore the k registers.

Remove handling of the k registers from:

 - i386_pseudo_register_read_into_value
 - i386_pseudo_register_write
 - i386_ax_pseudo_register_collect

Change-Id: Ic97956ed59af6099fef6d36a0b61464172694562
---
 gdb/i386-tdep.c | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 1ddca2410fdf..2759c1a558c5 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -3464,17 +3464,6 @@ i386_pseudo_register_read_into_value (struct gdbarch *gdbarch,
 	      memcpy (buf + size, &upper, size);
 	    }
 	}
-      else if (i386_k_regnum_p (gdbarch, regnum))
-	{
-	  regnum -= tdep->k0_regnum;
-
-	  /* Extract (always little endian).  */
-	  status = regcache->raw_read (tdep->k0_regnum + regnum, raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (0, 8);
-	  else
-	    memcpy (buf, raw_buf, 8);
-	}
       else if (i386_zmm_regnum_p (gdbarch, regnum))
 	{
 	  regnum -= tdep->zmm0_regnum;
@@ -3656,12 +3645,6 @@ i386_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
 
 	  regcache->raw_write (I387_BND0R_REGNUM (tdep) + regnum, raw_buf);
 	}
-      else if (i386_k_regnum_p (gdbarch, regnum))
-	{
-	  regnum -= tdep->k0_regnum;
-
-	  regcache->raw_write (tdep->k0_regnum + regnum, buf);
-	}
       else if (i386_zmm_regnum_p (gdbarch, regnum))
 	{
 	  regnum -= tdep->zmm0_regnum;
@@ -3758,12 +3741,6 @@ i386_ax_pseudo_register_collect (struct gdbarch *gdbarch,
       ax_reg_mask (ax, I387_BND0R_REGNUM (tdep) + regnum);
       return 0;
     }
-  else if (i386_k_regnum_p (gdbarch, regnum))
-    {
-      regnum -= tdep->k0_regnum;
-      ax_reg_mask (ax, tdep->k0_regnum + regnum);
-      return 0;
-    }
   else if (i386_zmm_regnum_p (gdbarch, regnum))
     {
       regnum -= tdep->zmm0_regnum;
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
  2023-11-08  5:00 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-11 19:42   ` John Baldwin
  2023-11-08  5:00 ` [PATCH 03/24] gdb: make store_integer take an array_view Simon Marchi
                   ` (24 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Right now, gdbsupport/common-regcache.h contains two abstractons for a
regcache.  An opaque type `regcache` (gdb and gdbserver both have their
own regcache that is the concrete version of this) and an abstract base
class `reg_buffer_common`, that is the base of regcaches on both sides.
These abstractions allow code to be written for both gdb and gdbserver,
for instance in the gdb/arch sub-directory.

However, having two
different abstractions is impractical.  If some common code has a regcache,
and wants to use an operation defined on reg_buffer_common, it can't.
It would be better to have just one.  Change all instances of `regcache
*` in gdbsupport/common-regcache.h to be `reg_buffer_common *`, then fix
fallouts.

Implementations in gdb and gdbserver now need to down-cast (using
gdb::checked_static_cast) from reg_buffer_common to their concrete
regcache type.  Some of them could be avoided by changing free functions
(like regcache_register_size) to be virtual methods on
reg_buffer_common.  I tried it, it seems to work, but I did not include
it in this series to avoid adding unnecessary changes.

Change-Id: Ia5503adb6b5509a0f4604bd2a68b4642cc5283fd
---
 gdb/arch/arm-get-next-pcs.c   |  6 +++---
 gdb/arch/arm-get-next-pcs.h   |  5 +++--
 gdb/arch/arm.c                |  2 +-
 gdb/arch/arm.h                |  4 ++--
 gdb/arm-linux-tdep.c          | 11 ++++++-----
 gdb/arm-tdep.c                |  5 +++--
 gdb/nat/aarch64-hw-point.c    |  3 +--
 gdb/nat/linux-btrace.c        |  3 +--
 gdb/regcache.c                | 17 ++++++++++-------
 gdbserver/linux-arm-low.cc    |  4 +++-
 gdbserver/regcache.cc         | 19 ++++++++++++-------
 gdbsupport/common-regcache.cc |  2 +-
 gdbsupport/common-regcache.h  | 17 ++++++++++-------
 13 files changed, 56 insertions(+), 42 deletions(-)

diff --git a/gdb/arch/arm-get-next-pcs.c b/gdb/arch/arm-get-next-pcs.c
index dcbb5a5e2e69..6b033993304b 100644
--- a/gdb/arch/arm-get-next-pcs.c
+++ b/gdb/arch/arm-get-next-pcs.c
@@ -32,7 +32,7 @@ arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
 		       int byte_order,
 		       int byte_order_for_code,
 		       int has_thumb2_breakpoint,
-		       struct regcache *regcache)
+		       reg_buffer_common *regcache)
 {
   self->ops = ops;
   self->byte_order = byte_order;
@@ -273,7 +273,7 @@ thumb_get_next_pcs_raw (struct arm_get_next_pcs *self)
   unsigned short inst1;
   CORE_ADDR nextpc = pc + 2;		/* Default is next instruction.  */
   ULONGEST status, itstate;
-  struct regcache *regcache = self->regcache;
+  reg_buffer_common *regcache = self->regcache;
   std::vector<CORE_ADDR> next_pcs;
 
   nextpc = MAKE_THUMB_ADDR (nextpc);
@@ -653,7 +653,7 @@ arm_get_next_pcs_raw (struct arm_get_next_pcs *self)
   unsigned long this_instr = 0;
   unsigned long status;
   CORE_ADDR nextpc;
-  struct regcache *regcache = self->regcache;
+  reg_buffer_common *regcache = self->regcache;
   CORE_ADDR pc = regcache_read_pc (self->regcache);
   std::vector<CORE_ADDR> next_pcs;
 
diff --git a/gdb/arch/arm-get-next-pcs.h b/gdb/arch/arm-get-next-pcs.h
index e6bb8d832286..ec347f01b4fd 100644
--- a/gdb/arch/arm-get-next-pcs.h
+++ b/gdb/arch/arm-get-next-pcs.h
@@ -24,6 +24,7 @@
 
 /* Forward declaration.  */
 struct arm_get_next_pcs;
+struct reg_buffer_common;
 
 /* get_next_pcs operations.  */
 struct arm_get_next_pcs_ops
@@ -50,7 +51,7 @@ struct arm_get_next_pcs
      not.  */
   int has_thumb2_breakpoint;
   /* Registry cache.  */
-  struct regcache *regcache;
+  reg_buffer_common *regcache;
 };
 
 /* Initialize arm_get_next_pcs.  */
@@ -59,7 +60,7 @@ void arm_get_next_pcs_ctor (struct arm_get_next_pcs *self,
 			    int byte_order,
 			    int byte_order_for_code,
 			    int has_thumb2_breakpoint,
-			    struct regcache *regcache);
+			    reg_buffer_common *regcache);
 
 /* Find the next possible PCs after the current instruction executes.  */
 std::vector<CORE_ADDR> arm_get_next_pcs (struct arm_get_next_pcs *self);
diff --git a/gdb/arch/arm.c b/gdb/arch/arm.c
index 4720c201c532..88737fc357f8 100644
--- a/gdb/arch/arm.c
+++ b/gdb/arch/arm.c
@@ -322,7 +322,7 @@ thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2)
 /* See arm.h.  */
 
 unsigned long
-shifted_reg_val (struct regcache *regcache, unsigned long inst,
+shifted_reg_val (reg_buffer_common *regcache, unsigned long inst,
 		 int carry, unsigned long pc_val, unsigned long status_reg)
 {
   unsigned long res, shift;
diff --git a/gdb/arch/arm.h b/gdb/arch/arm.h
index c64a15600de3..b6c316191877 100644
--- a/gdb/arch/arm.h
+++ b/gdb/arch/arm.h
@@ -188,7 +188,7 @@ enum system_register_address : CORE_ADDR
   ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
 
 /* Forward declaration.  */
-struct regcache;
+struct reg_buffer_common;
 
 /* Return the size in bytes of the complete Thumb instruction whose
    first halfword is INST1.  */
@@ -213,7 +213,7 @@ int thumb_advance_itstate (unsigned int itstate);
 
 /* Decode shifted register value.  */
 
-unsigned long shifted_reg_val (struct regcache *regcache,
+unsigned long shifted_reg_val (reg_buffer_common *regcache,
 			       unsigned long inst,
 			       int carry,
 			       unsigned long pc_val,
diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index dfa816990ff5..909a20324fd5 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -869,8 +869,10 @@ static CORE_ADDR
 arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
 {
   CORE_ADDR next_pc = 0;
-  CORE_ADDR pc = regcache_read_pc (self->regcache);
-  int is_thumb = arm_is_thumb (self->regcache);
+  regcache *regcache
+    = gdb::checked_static_cast<struct regcache *> (self->regcache);
+  CORE_ADDR pc = regcache_read_pc (regcache);
+  int is_thumb = arm_is_thumb (regcache);
   ULONGEST svc_number = 0;
 
   if (is_thumb)
@@ -880,7 +882,7 @@ arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
     }
   else
     {
-      struct gdbarch *gdbarch = self->regcache->arch ();
+      struct gdbarch *gdbarch = regcache->arch ();
       enum bfd_endian byte_order_for_code = 
 	gdbarch_byte_order_for_code (gdbarch);
       unsigned long this_instr = 
@@ -903,8 +905,7 @@ arm_linux_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
     {
       /* SIGRETURN or RT_SIGRETURN may affect the arm thumb mode, so
 	 update IS_THUMB.   */
-      next_pc = arm_linux_sigreturn_next_pc (self->regcache, svc_number,
-					     &is_thumb);
+      next_pc = arm_linux_sigreturn_next_pc (regcache, svc_number, &is_thumb);
     }
 
   /* Addresses for calling Thumb functions have the bit 0 set.  */
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index d4047ddbb868..2018f4ed0edd 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -7255,7 +7255,8 @@ CORE_ADDR
 arm_get_next_pcs_addr_bits_remove (struct arm_get_next_pcs *self,
 				   CORE_ADDR val)
 {
-  return gdbarch_addr_bits_remove (self->regcache->arch (), val);
+  return gdbarch_addr_bits_remove
+    (gdb::checked_static_cast<regcache *> (self->regcache)->arch (), val);
 }
 
 /* Wrapper over syscall_next_pc for use in get_next_pcs.  */
@@ -7271,7 +7272,7 @@ arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
 int
 arm_get_next_pcs_is_thumb (struct arm_get_next_pcs *self)
 {
-  return arm_is_thumb (self->regcache);
+  return arm_is_thumb (gdb::checked_static_cast<regcache *> (self->regcache));
 }
 
 /* single_step() is called just before we want to resume the inferior,
diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c
index 6747e61e0265..8e9a532cd2a4 100644
--- a/gdb/nat/aarch64-hw-point.c
+++ b/gdb/nat/aarch64-hw-point.c
@@ -137,8 +137,7 @@ aarch64_point_is_aligned (ptid_t ptid, int is_watchpoint, CORE_ADDR addr,
     alignment = AARCH64_HWP_ALIGNMENT;
   else
     {
-      struct regcache *regcache
-	= get_thread_regcache_for_ptid (ptid);
+      reg_buffer_common *regcache = get_thread_regcache_for_ptid (ptid);
 
       /* Set alignment to 2 only if the current process is 32-bit,
 	 since thumb instruction can be 2-byte aligned.  Otherwise, set
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 3c217daa4886..7c4b40e40b8a 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -284,13 +284,12 @@ perf_event_read_bts (btrace_target_info *tinfo, const uint8_t *begin,
   struct perf_event_sample sample;
   size_t read = 0;
   struct btrace_block block = { 0, 0 };
-  struct regcache *regcache;
 
   gdb_assert (begin <= start);
   gdb_assert (start <= end);
 
   /* The first block ends at the current pc.  */
-  regcache = get_thread_regcache_for_ptid (tinfo->ptid);
+  reg_buffer_common *regcache = get_thread_regcache_for_ptid (tinfo->ptid);
   block.end = regcache_read_pc (regcache);
 
   /* The buffer may contain a partial record as its last entry (i.e. when the
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 2e48c0258091..11a6ecc3469e 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -180,9 +180,10 @@ register_size (struct gdbarch *gdbarch, int regnum)
 /* See gdbsupport/common-regcache.h.  */
 
 int
-regcache_register_size (const struct regcache *regcache, int n)
+regcache_register_size (const reg_buffer_common *regcache, int n)
 {
-  return register_size (regcache->arch (), n);
+  return register_size
+    ( gdb::checked_static_cast<const struct regcache *> (regcache)->arch (), n);
 }
 
 reg_buffer::reg_buffer (gdbarch *gdbarch, bool has_pseudo)
@@ -435,7 +436,7 @@ get_current_regcache (void)
 
 /* See gdbsupport/common-regcache.h.  */
 
-struct regcache *
+reg_buffer_common *
 get_thread_regcache_for_ptid (ptid_t ptid)
 {
   /* This function doesn't take a process_stratum_target parameter
@@ -648,11 +649,12 @@ readable_regcache::raw_read (int regnum, T *val)
 }
 
 enum register_status
-regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
+regcache_raw_read_unsigned (reg_buffer_common *regcache, int regnum,
 			    ULONGEST *val)
 {
   gdb_assert (regcache != NULL);
-  return regcache->raw_read (regnum, val);
+  return gdb::checked_static_cast<struct regcache *> (regcache)->raw_read
+    (regnum, val);
 }
 
 void
@@ -1332,8 +1334,9 @@ reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
 /* Special handling for register PC.  */
 
 CORE_ADDR
-regcache_read_pc (struct regcache *regcache)
+regcache_read_pc (reg_buffer_common *reg_buf)
 {
+  regcache *regcache = gdb::checked_static_cast<struct regcache *> (reg_buf);
   struct gdbarch *gdbarch = regcache->arch ();
 
   CORE_ADDR pc_val;
@@ -1360,7 +1363,7 @@ regcache_read_pc (struct regcache *regcache)
 /* See gdbsupport/common-regcache.h.  */
 
 CORE_ADDR
-regcache_read_pc_protected (regcache *regcache)
+regcache_read_pc_protected (reg_buffer_common *regcache)
 {
   CORE_ADDR pc;
   try
diff --git a/gdbserver/linux-arm-low.cc b/gdbserver/linux-arm-low.cc
index 5975b44af0ae..0a6f3622695a 100644
--- a/gdbserver/linux-arm-low.cc
+++ b/gdbserver/linux-arm-low.cc
@@ -24,6 +24,7 @@
 #include "linux-aarch32-low.h"
 #include "linux-aarch32-tdesc.h"
 #include "linux-arm-tdesc.h"
+#include "gdbsupport/gdb-checked-static-cast.h"
 
 #include <sys/uio.h>
 /* Don't include elf.h if linux/elf.h got included by gdb_proc_service.h.
@@ -913,7 +914,8 @@ get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self)
   CORE_ADDR pc = regcache_read_pc (self->regcache);
   int is_thumb = arm_is_thumb_mode ();
   ULONGEST svc_number = 0;
-  struct regcache *regcache = self->regcache;
+  regcache *regcache
+    = gdb::checked_static_cast<struct regcache *> (self->regcache);
 
   if (is_thumb)
     {
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 0b1141662ac6..4ac0fb659c3b 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -21,6 +21,8 @@
 #include "gdbthread.h"
 #include "tdesc.h"
 #include "gdbsupport/rsp-low.h"
+#include "gdbsupport/gdb-checked-static-cast.h"
+
 #ifndef IN_PROCESS_AGENT
 
 struct regcache *
@@ -64,7 +66,7 @@ get_thread_regcache (struct thread_info *thread, int fetch)
 
 /* See gdbsupport/common-regcache.h.  */
 
-struct regcache *
+reg_buffer_common *
 get_thread_regcache_for_ptid (ptid_t ptid)
 {
   return get_thread_regcache (find_thread_ptid (ptid), 1);
@@ -307,9 +309,10 @@ register_size (const struct target_desc *tdesc, int n)
 /* See gdbsupport/common-regcache.h.  */
 
 int
-regcache_register_size (const struct regcache *regcache, int n)
+regcache_register_size (const reg_buffer_common *regcache, int n)
 {
-  return register_size (regcache->tdesc, n);
+  return register_size
+    (gdb::checked_static_cast<const struct regcache *> (regcache)->tdesc, n);
 }
 
 static unsigned char *
@@ -437,13 +440,14 @@ regcache::raw_collect (int n, void *buf) const
 }
 
 enum register_status
-regcache_raw_read_unsigned (struct regcache *regcache, int regnum,
+regcache_raw_read_unsigned (reg_buffer_common *reg_buf, int regnum,
 			    ULONGEST *val)
 {
   int size;
+  regcache *regcache = gdb::checked_static_cast<struct regcache *> (reg_buf);
 
   gdb_assert (regcache != NULL);
-
+  
   size = register_size (regcache->tdesc, regnum);
 
   if (size > (int) sizeof (ULONGEST))
@@ -486,9 +490,10 @@ collect_register_by_name (struct regcache *regcache,
 /* Special handling for register PC.  */
 
 CORE_ADDR
-regcache_read_pc (struct regcache *regcache)
+regcache_read_pc (reg_buffer_common *regcache)
 {
-  return the_target->read_pc (regcache);
+  return the_target->read_pc
+    (gdb::checked_static_cast<struct regcache *> (regcache));
 }
 
 void
diff --git a/gdbsupport/common-regcache.cc b/gdbsupport/common-regcache.cc
index 3515bedb3830..b6f02bac16e2 100644
--- a/gdbsupport/common-regcache.cc
+++ b/gdbsupport/common-regcache.cc
@@ -23,7 +23,7 @@
 /* Return the register's value or throw if it's not available.  */
 
 ULONGEST
-regcache_raw_get_unsigned (struct regcache *regcache, int regnum)
+regcache_raw_get_unsigned (reg_buffer_common *regcache, int regnum)
 {
   ULONGEST value;
   enum register_status status;
diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
index e462f532407b..6d98ca8c92ed 100644
--- a/gdbsupport/common-regcache.h
+++ b/gdbsupport/common-regcache.h
@@ -20,6 +20,8 @@
 #ifndef COMMON_COMMON_REGCACHE_H
 #define COMMON_COMMON_REGCACHE_H
 
+struct reg_buffer_common;
+
 /* This header is a stopgap until we have an independent regcache.  */
 
 enum register_status : signed char
@@ -44,28 +46,29 @@ enum register_status : signed char
    thread specified by PTID.  This function must be provided by
    the client.  */
 
-extern struct regcache *get_thread_regcache_for_ptid (ptid_t ptid);
+extern reg_buffer_common *get_thread_regcache_for_ptid (ptid_t ptid);
 
 /* Return the size of register numbered N in REGCACHE.  This function
    must be provided by the client.  */
 
-extern int regcache_register_size (const struct regcache *regcache, int n);
+extern int regcache_register_size (const reg_buffer_common *regcache, int n);
 
 /* Read the PC register.  This function must be provided by the
    client.  */
 
-extern CORE_ADDR regcache_read_pc (struct regcache *regcache);
+extern CORE_ADDR regcache_read_pc (reg_buffer_common *regcache);
 
 /* Read the PC register.  If PC cannot be read, return 0.
    This is a wrapper around 'regcache_read_pc'.  */
 
-extern CORE_ADDR regcache_read_pc_protected (regcache *regcache);
+extern CORE_ADDR regcache_read_pc_protected (reg_buffer_common *regcache);
 
 /* Read a raw register into a unsigned integer.  */
-extern enum register_status regcache_raw_read_unsigned
-  (struct regcache *regcache, int regnum, ULONGEST *val);
+extern enum register_status
+regcache_raw_read_unsigned (reg_buffer_common *regcache, int regnum,
+			    ULONGEST *val);
 
-ULONGEST regcache_raw_get_unsigned (struct regcache *regcache, int regnum);
+ULONGEST regcache_raw_get_unsigned (reg_buffer_common *regcache, int regnum);
 
 struct reg_buffer_common
 {
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 03/24] gdb: make store_integer take an array_view
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
  2023-11-08  5:00 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
  2023-11-08  5:00 ` [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 04/24] gdb: simplify conditions in regcache::{read,write,raw_collect,raw_supply}_part Simon Marchi
                   ` (23 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Change store_integer, store_signed_integer and store_unsigned_integer to
accept an array_view.  Add some backwards compatibility overloads to
avoid changing all callers at once.

Change-Id: Ibb1381228ab1cb65fc7e2e4b92cf9ab1047cdc03
---
 gdb/defs.h    | 39 +++++++++++++++++++++++++++++++--------
 gdb/findvar.c | 16 +++++++---------
 2 files changed, 38 insertions(+), 17 deletions(-)

diff --git a/gdb/defs.h b/gdb/defs.h
index b8612e1ac6d3..8170ec53c87c 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -501,21 +501,44 @@ extern CORE_ADDR extract_typed_address (const gdb_byte *buf,
    target-format integer at ADDR which is LEN bytes long.  */
 
 template<typename T, typename = RequireLongest<T>>
-extern void store_integer (gdb_byte *addr, int len, enum bfd_endian byte_order,
-			   T val);
+extern void store_integer (gdb::array_view<gdb_byte> dst,
+			   bfd_endian byte_order, T val);
+
+template<typename T>
+static inline void
+store_integer (gdb_byte *addr, int len, bfd_endian byte_order, T val)
+{
+  return store_integer (gdb::make_array_view (addr, len), byte_order, val);
+}
+
+static inline void
+store_signed_integer (gdb::array_view<gdb_byte> dst, bfd_endian byte_order,
+		      LONGEST val)
+{
+  return store_integer (dst, byte_order, val);
+}
+
+static inline void
+store_signed_integer (gdb_byte *addr, int len, bfd_endian byte_order,
+		      LONGEST val)
+{
+  return store_signed_integer (gdb::make_array_view (addr, len), byte_order,
+			       val);
+}
 
 static inline void
-store_signed_integer (gdb_byte *addr, int len,
-		      enum bfd_endian byte_order, LONGEST val)
+store_unsigned_integer (gdb::array_view<gdb_byte> dst, bfd_endian byte_order,
+			ULONGEST val)
 {
-  return store_integer (addr, len, byte_order, val);
+  return store_integer (dst, byte_order, val);
 }
 
 static inline void
-store_unsigned_integer (gdb_byte *addr, int len,
-			enum bfd_endian byte_order, ULONGEST val)
+store_unsigned_integer (gdb_byte *addr, int len, bfd_endian byte_order,
+			LONGEST val)
 {
-  return store_integer (addr, len, byte_order, val);
+  return store_unsigned_integer (gdb::make_array_view (addr, len), byte_order,
+				 val);
 }
 
 extern void store_typed_address (gdb_byte *buf, struct type *type,
diff --git a/gdb/findvar.c b/gdb/findvar.c
index 4e992ecdcc73..edb61fb27317 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -160,12 +160,12 @@ extract_typed_address (const gdb_byte *buf, struct type *type)
    target-format integer at ADDR which is LEN bytes long.  */
 template<typename T, typename>
 void
-store_integer (gdb_byte *addr, int len, enum bfd_endian byte_order,
+store_integer (gdb::array_view<gdb_byte> dst, enum bfd_endian byte_order,
 	       T val)
 {
   gdb_byte *p;
-  gdb_byte *startaddr = addr;
-  gdb_byte *endaddr = startaddr + len;
+  gdb_byte *startaddr = dst.data ();
+  gdb_byte *endaddr = startaddr + dst.size ();
 
   /* Start at the least significant end of the integer, and work towards
      the most significant.  */
@@ -188,13 +188,11 @@ store_integer (gdb_byte *addr, int len, enum bfd_endian byte_order,
 }
 
 /* Explicit instantiations.  */
-template void store_integer (gdb_byte *addr, int len,
-			     enum bfd_endian byte_order,
-			     LONGEST val);
+template void store_integer (gdb::array_view<gdb_byte> dst,
+			     bfd_endian byte_order, LONGEST val);
 
-template void store_integer (gdb_byte *addr, int len,
-			     enum bfd_endian byte_order,
-			     ULONGEST val);
+template void store_integer (gdb::array_view<gdb_byte> dst,
+			     bfd_endian byte_order, ULONGEST val);
 
 /* Store the address ADDR as a pointer of type TYPE at BUF, in target
    form.  */
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 04/24] gdb: simplify conditions in regcache::{read,write,raw_collect,raw_supply}_part
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (2 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 03/24] gdb: make store_integer take an array_view Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 05/24] gdb: change regcache interface to use array_view Simon Marchi
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Make a few simplifications in these functions.

1. When checking if we need to do nothing, if the length is 0, we don't
   need to do anything, regardless of the value of offset.  Remove the
   offset check.

2. When check if transferring the whole register, if the length is equal
   to the register size, then we transfer the whole register, no need to
   check the offset.  Remove the offset check.

3. In the gdb_asserts, it is unnecessary to check for:

     offset <= reg_size

   given that right after we check for:

     len >= 0 && offset + len <= reg_size

   If `offset + len` is <= reg_size and len is >= 0, then necessarily
   offset is <= reg_size.  Remove the `offset <= reg_size` check.

Change-Id: I30a73acdc7bf432c45a07f5f177224d1cdc298e8
---
 gdb/regcache.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/gdb/regcache.c b/gdb/regcache.c
index 11a6ecc3469e..ae5411ba5563 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -891,16 +891,16 @@ readable_regcache::read_part (int regnum, int offset, int len,
   int reg_size = register_size (arch (), regnum);
 
   gdb_assert (out != NULL);
-  gdb_assert (offset >= 0 && offset <= reg_size);
+  gdb_assert (offset >= 0);
   gdb_assert (len >= 0 && offset + len <= reg_size);
 
-  if (offset == 0 && len == 0)
+  if (len == 0)
     {
       /* Nothing to do.  */
       return REG_VALID;
     }
 
-  if (offset == 0 && len == reg_size)
+  if (len == reg_size)
     {
       /* Read the full register.  */
       return (is_raw) ? raw_read (regnum, out) : cooked_read (regnum, out);
@@ -928,16 +928,16 @@ reg_buffer::raw_collect_part (int regnum, int offset, int len,
   int reg_size = register_size (arch (), regnum);
 
   gdb_assert (out != nullptr);
-  gdb_assert (offset >= 0 && offset <= reg_size);
+  gdb_assert (offset >= 0);
   gdb_assert (len >= 0 && offset + len <= reg_size);
 
-  if (offset == 0 && len == 0)
+  if (len == 0)
     {
       /* Nothing to do.  */
       return;
     }
 
-  if (offset == 0 && len == reg_size)
+  if (len == reg_size)
     {
       /* Collect the full register.  */
       return raw_collect (regnum, out);
@@ -958,16 +958,16 @@ regcache::write_part (int regnum, int offset, int len,
   int reg_size = register_size (arch (), regnum);
 
   gdb_assert (in != NULL);
-  gdb_assert (offset >= 0 && offset <= reg_size);
+  gdb_assert (offset >= 0);
   gdb_assert (len >= 0 && offset + len <= reg_size);
 
-  if (offset == 0 && len == 0)
+  if (len == 0)
     {
       /* Nothing to do.  */
       return REG_VALID;
     }
 
-  if (offset == 0 && len == reg_size)
+  if (len == reg_size)
     {
       /* Write the full register.  */
       (is_raw) ? raw_write (regnum, in) : cooked_write (regnum, in);
@@ -997,16 +997,16 @@ reg_buffer::raw_supply_part (int regnum, int offset, int len,
   int reg_size = register_size (arch (), regnum);
 
   gdb_assert (in != nullptr);
-  gdb_assert (offset >= 0 && offset <= reg_size);
+  gdb_assert (offset >= 0);
   gdb_assert (len >= 0 && offset + len <= reg_size);
 
-  if (offset == 0 && len == 0)
+  if (len == 0)
     {
       /* Nothing to do.  */
       return;
     }
 
-  if (offset == 0 && len == reg_size)
+  if (len == reg_size)
     {
       /* Supply the full register.  */
       return raw_supply (regnum, in);
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 05/24] gdb: change regcache interface to use array_view
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (3 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 04/24] gdb: simplify conditions in regcache::{read,write,raw_collect,raw_supply}_part Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-13 13:43   ` Andrew Burgess
  2023-11-08  5:00 ` [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes Simon Marchi
                   ` (21 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Change most of regcache (and base classes) to use array_view when
possible, instead of raw pointers.  By propagating the use of array_view
further, it enables having some runtime checks to make sure the what we
read from or write to regcaches has the expected length (such as the one
in the `copy(array_view, array_view)` function.  It also integrates well
when connecting with other APIs already using gdb::array_view.

Add some overloads of the methods using raw pointers to avoid having to
change all call sites at once (which is both a lot of work and risky).

I tried to do this change in small increments, but since many of these
functions use each other, it ended up simpler to do it in one shot than
having a lot of intermediary / transient changes.

This change extends into gdbserver as well, because there is some part
of the regcache interface that is shared.

Changing the reg_buffer_common interface to use array_view caused some
build failures in nat/aarch64-scalable-linux-ptrace.c.  That file
currently "takes advantage" of the fact that
reg_buffer_common::{raw_supply,raw_collect} operates on `void *`, which
IMO is dangerous.  It uses raw_supply/raw_collect directly on
uint64_t's, which I guess is fine because it is expected that native
code will have the same endianness as the debugged process.  To
accomodate that, add some overloads of raw_collect and raw_supply that
work on uint64_t.

This file also uses raw_collect and raw_supply on `char` pointers.
Change it to use `gdb_byte` pointers instead.  Add overloads of
raw_collect and raw_supply that work on `gdb_byte *` and make an
array_view on the fly using the register's size.  Those call sites could
be converted to use array_view with not much work, in which case these
overloads could be removed, but I didn't want to do it in this patch, to
avoid starting to dig in arch-specific code.

Change-Id: I9005f04114543ddff738949e12d85a31855304c2
---
 gdb/frame.c                             |   4 +-
 gdb/nat/aarch64-scalable-linux-ptrace.c |  20 +-
 gdb/regcache.c                          | 478 ++++++++++++++----------
 gdb/regcache.h                          | 113 ++++--
 gdbserver/regcache.cc                   |  50 +--
 gdbserver/regcache.h                    |   6 +-
 gdbsupport/common-regcache.h            |  41 +-
 gdbsupport/rsp-low.cc                   |   8 +
 gdbsupport/rsp-low.h                    |   2 +
 9 files changed, 451 insertions(+), 271 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index 7077016ccba4..afadb8ac4e73 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1107,9 +1107,9 @@ get_frame_func (frame_info_ptr this_frame)
 std::unique_ptr<readonly_detached_regcache>
 frame_save_as_regcache (frame_info_ptr this_frame)
 {
-  auto cooked_read = [this_frame] (int regnum, gdb_byte *buf)
+  auto cooked_read = [this_frame] (int regnum, gdb::array_view<gdb_byte> buf)
     {
-      if (!deprecated_frame_register_read (this_frame, regnum, buf))
+      if (!deprecated_frame_register_read (this_frame, regnum, buf.data ()))
 	return REG_UNAVAILABLE;
       else
 	return REG_VALID;
diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c
index dc0e45fa91ee..b8fb317edaca 100644
--- a/gdb/nat/aarch64-scalable-linux-ptrace.c
+++ b/gdb/nat/aarch64-scalable-linux-ptrace.c
@@ -613,7 +613,7 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
 {
   gdb::byte_vector sve_state = aarch64_fetch_sve_regset (tid);
 
-  char *base = (char *) sve_state.data ();
+  gdb_byte *base = sve_state.data ();
   struct user_sve_header *header
     = (struct user_sve_header *) sve_state.data ();
 
@@ -684,8 +684,10 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
 	  reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg);
 	}
 
-      reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
-      reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
+      reg_buf->raw_supply (AARCH64_FPSR_REGNUM,
+			   (const gdb_byte *) &fpsimd->fpsr);
+      reg_buf->raw_supply (AARCH64_FPCR_REGNUM,
+			   (const gdb_byte *) &fpsimd->fpcr);
 
       /* Clear the SVE only registers.  */
       memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
@@ -720,7 +722,7 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
   gdb::byte_vector new_state (SVE_PT_SIZE (32, SVE_PT_REGS_SVE), 0);
   memcpy (new_state.data (), sve_state.data (), sve_state.size ());
   header = (struct user_sve_header *) new_state.data ();
-  char *base = (char *) new_state.data ();
+  gdb_byte *base = new_state.data ();
 
   /* Sanity check the data in the header.  */
   if (!sve_vl_valid (header->vl)
@@ -805,9 +807,11 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
 	    }
 
 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM))
-	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
+	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM,
+				  (gdb_byte *) &fpsimd->fpsr);
 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM))
-	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
+	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM,
+				  (gdb_byte *) &fpsimd->fpcr);
 
 	  /* At this point we have collected all the data from the register
 	     cache and we are ready to update the FPSIMD register content
@@ -894,7 +898,7 @@ aarch64_za_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf,
   /* Sanity check.  */
   gdb_assert (!za_state.empty ());
 
-  char *base = (char *) za_state.data ();
+  gdb_byte *base = za_state.data ();
   struct user_za_header *header = (struct user_za_header *) base;
 
   /* If we have ZA state, read it.  Otherwise, make the contents of ZA
@@ -1027,7 +1031,7 @@ aarch64_za_regs_copy_from_reg_buf (int tid,
       /* Fetch the current ZA state from the thread.  */
       gdb::byte_vector za_state = aarch64_fetch_za_regset (tid);
 
-      char *base = (char *) za_state.data ();
+      gdb_byte *base = za_state.data ();
       struct user_za_header *za_header = (struct user_za_header *) base;
       uint64_t svq = sve_vq_from_vl (za_header->vl);
 
diff --git a/gdb/regcache.c b/gdb/regcache.c
index ae5411ba5563..4f3881386f34 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -221,10 +221,9 @@ regcache::regcache (inferior *inf_for_target_calls, gdbarch *gdbarch,
 
 readonly_detached_regcache::readonly_detached_regcache (regcache &src)
   : readonly_detached_regcache (src.arch (),
-				[&src] (int regnum, gdb_byte *buf)
-				  {
-				    return src.cooked_read (regnum, buf);
-				  })
+				[&src] (int regnum,
+					gdb::array_view<gdb_byte> buf)
+				  { return src.cooked_read (regnum, buf, 1.0f); })
 {
 }
 
@@ -234,19 +233,36 @@ reg_buffer::arch () const
   return m_descr->gdbarch;
 }
 
-/* Return  a pointer to register REGNUM's buffer cache.  */
+template<typename ElemType>
+gdb::array_view<ElemType>
+reg_buffer::register_buffer (int regnum) const
+{
+  assert_regnum (regnum);
+  ElemType *start = &m_registers[m_descr->register_offset[regnum]];
+  int size = m_descr->sizeof_register[regnum];
+  return gdb::array_view<ElemType> (start, size);
+}
 
-gdb_byte *
+/* See regcache.h.  */
+
+gdb::array_view<const gdb_byte>
 reg_buffer::register_buffer (int regnum) const
 {
-  return m_registers.get () + m_descr->register_offset[regnum];
+  return register_buffer<const gdb_byte> (regnum);
+}
+
+/* See regcache.h.  */
+
+gdb::array_view<gdb_byte>
+reg_buffer::register_buffer (int regnum)
+{
+  return register_buffer<gdb_byte> (regnum);
 }
 
 void
 reg_buffer::save (register_read_ftype cooked_read)
 {
   struct gdbarch *gdbarch = m_descr->gdbarch;
-  int regnum;
 
   /* It should have pseudo registers.  */
   gdb_assert (m_has_pseudo);
@@ -257,17 +273,17 @@ reg_buffer::save (register_read_ftype cooked_read)
      save_reggroup) and mark them as valid.  The full [0 .. gdbarch_num_regs +
      gdbarch_num_pseudo_regs) range is checked since some architectures need
      to save/restore `cooked' registers that live in memory.  */
-  for (regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
+  for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
     {
       if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
 	{
-	  gdb_byte *dst_buf = register_buffer (regnum);
-	  enum register_status status = cooked_read (regnum, dst_buf);
+	  gdb::array_view<gdb_byte> dst_buf = register_buffer (regnum);
+	  register_status status = cooked_read (regnum, dst_buf);
 
 	  gdb_assert (status != REG_UNKNOWN);
 
 	  if (status != REG_VALID)
-	    memset (dst_buf, 0, register_size (gdbarch, regnum));
+	    memset (dst_buf.data (), 0, dst_buf.size ());
 
 	  m_register_status[regnum] = status;
 	}
@@ -294,7 +310,7 @@ regcache::restore (readonly_detached_regcache *src)
       if (gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup))
 	{
 	  if (src->m_register_status[regnum] == REG_VALID)
-	    cooked_write (regnum, src->register_buffer (regnum));
+	    cooked_write (regnum, src->register_buffer (regnum), 1.0f);
 	}
     }
 }
@@ -610,21 +626,30 @@ regcache::raw_update (int regnum)
     }
 }
 
-enum register_status
-readable_regcache::raw_read (int regnum, gdb_byte *buf)
+register_status
+readable_regcache::raw_read (int regnum, gdb::array_view<gdb_byte> dst, float)
 {
-  gdb_assert (buf != NULL);
+  assert_regnum (regnum);
+  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+
   raw_update (regnum);
 
   if (m_register_status[regnum] != REG_VALID)
-    memset (buf, 0, m_descr->sizeof_register[regnum]);
+    memset (dst.data (), 0, dst.size ());
   else
-    memcpy (buf, register_buffer (regnum),
-	    m_descr->sizeof_register[regnum]);
+    copy (register_buffer (regnum), dst);
 
   return m_register_status[regnum];
 }
 
+register_status
+readable_regcache::raw_read (int regnum, gdb_byte *dst)
+{
+  assert_regnum (regnum);
+  int size = m_descr->sizeof_register[regnum];
+  return raw_read (regnum, gdb::make_array_view (dst, size), 1.0f);
+}
+
 enum register_status
 regcache_raw_read_signed (struct regcache *regcache, int regnum, LONGEST *val)
 {
@@ -639,12 +664,14 @@ readable_regcache::raw_read (int regnum, T *val)
   assert_regnum (regnum);
   size_t len = m_descr->sizeof_register[regnum];
   gdb_byte *buf = (gdb_byte *) alloca (len);
-  register_status status = raw_read (regnum, buf);
+  auto view = gdb::make_array_view (buf, len);
+  register_status status = raw_read (regnum, view, 1.0f);
+
   if (status == REG_VALID)
-    *val = extract_integer<T> ({buf, len},
-			       gdbarch_byte_order (m_descr->gdbarch));
+    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
   else
     *val = 0;
+
   return status;
 }
 
@@ -668,13 +695,13 @@ template<typename T, typename>
 void
 regcache::raw_write (int regnum, T val)
 {
-  gdb_byte *buf;
-
   assert_regnum (regnum);
-  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
-  store_integer (buf, m_descr->sizeof_register[regnum],
-		 gdbarch_byte_order (m_descr->gdbarch), val);
-  raw_write (regnum, buf);
+
+  int len = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
+  auto view = gdb::make_array_view (buf, len);
+  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
+  raw_write (regnum, view, 1.0f);
 }
 
 void
@@ -698,47 +725,61 @@ regcache_raw_get_signed (struct regcache *regcache, int regnum)
   return value;
 }
 
-enum register_status
-readable_regcache::cooked_read (int regnum, gdb_byte *buf)
+/* See regcache.h.  */
+
+register_status
+readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst,
+				float)
 {
   gdb_assert (regnum >= 0);
   gdb_assert (regnum < m_descr->nr_cooked_registers);
+
   if (regnum < num_raw_registers ())
-    return raw_read (regnum, buf);
-  else if (m_has_pseudo
-	   && m_register_status[regnum] != REG_UNKNOWN)
+    return raw_read (regnum, dst, 1.0f);
+
+  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+
+  if (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
     {
       if (m_register_status[regnum] == REG_VALID)
-	memcpy (buf, register_buffer (regnum),
-		m_descr->sizeof_register[regnum]);
+	copy (register_buffer (regnum), dst);
       else
-	memset (buf, 0, m_descr->sizeof_register[regnum]);
+	memset (dst.data (), 0, dst.size ());
 
       return m_register_status[regnum];
     }
   else if (gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
     {
-      struct value *computed;
-      enum register_status result = REG_VALID;
-
+      register_status result = REG_VALID;
       scoped_value_mark mark;
+      value *computed
+	= gdbarch_pseudo_register_read_value (m_descr->gdbarch, this, regnum);
 
-      computed = gdbarch_pseudo_register_read_value (m_descr->gdbarch,
-						     this, regnum);
       if (computed->entirely_available ())
-	memcpy (buf, computed->contents_raw ().data (),
-		m_descr->sizeof_register[regnum]);
+	copy (computed->contents_raw (), dst);
       else
 	{
-	  memset (buf, 0, m_descr->sizeof_register[regnum]);
+	  memset (dst.data (), 0, dst.size ());
 	  result = REG_UNAVAILABLE;
 	}
 
       return result;
     }
   else
-    return gdbarch_pseudo_register_read (m_descr->gdbarch, this,
-					 regnum, buf);
+    return gdbarch_pseudo_register_read (m_descr->gdbarch, this, regnum,
+					 dst.data ());
+}
+
+/* See regcache.h.  */
+
+register_status
+readable_regcache::cooked_read (int regnum, gdb_byte *dst)
+{
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+  int size = m_descr->sizeof_register[regnum];
+  return cooked_read (regnum, gdb::make_array_view (dst, size), 1.0f);
 }
 
 struct value *
@@ -760,8 +801,8 @@ readable_regcache::cooked_read_value (int regnum)
       /* It is more efficient in general to do this delegation in this
 	 direction than in the other one, even though the value-based
 	 API is preferred.  */
-      if (cooked_read (regnum,
-		       result->contents_raw ().data ()) == REG_UNAVAILABLE)
+      if (cooked_read (regnum, result->contents_raw (), 1.0f)
+	  == REG_UNAVAILABLE)
 	result->mark_bytes_unavailable (0,
 					result->type ()->length ());
 
@@ -787,10 +828,10 @@ readable_regcache::cooked_read (int regnum, T *val)
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
   size_t len = m_descr->sizeof_register[regnum];
   gdb_byte *buf = (gdb_byte *) alloca (len);
-  register_status status = cooked_read (regnum, buf);
+  auto view = gdb::make_array_view (buf, len);
+  register_status status = cooked_read (regnum, view, 1.0f);
   if (status == REG_VALID)
-    *val = extract_integer<T> ({buf, len},
-			       gdbarch_byte_order (m_descr->gdbarch));
+    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
   else
     *val = 0;
   return status;
@@ -816,13 +857,14 @@ template<typename T, typename>
 void
 regcache::cooked_write (int regnum, T val)
 {
-  gdb_byte *buf;
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
 
-  gdb_assert (regnum >=0 && regnum < m_descr->nr_cooked_registers);
-  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
-  store_integer (buf, m_descr->sizeof_register[regnum],
-		 gdbarch_byte_order (m_descr->gdbarch), val);
-  cooked_write (regnum, buf);
+  int size = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (size);
+  auto view = gdb::make_array_view (buf, size);
+  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
+  cooked_write (regnum, view, 1.0f);
 }
 
 void
@@ -834,11 +876,10 @@ regcache_cooked_write_unsigned (struct regcache *regcache, int regnum,
 }
 
 void
-regcache::raw_write (int regnum, const gdb_byte *buf)
+regcache::raw_write (int regnum, gdb::array_view<const gdb_byte> src, float)
 {
-
-  gdb_assert (buf != NULL);
   assert_regnum (regnum);
+  gdb_assert (src.size () == m_descr->sizeof_register[regnum]);
 
   /* On the sparc, writing %g0 is a no-op, so we don't even want to
      change the registers array if something writes to this register.  */
@@ -848,15 +889,15 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
   /* If we have a valid copy of the register, and new value == old
      value, then don't bother doing the actual store.  */
   if (get_register_status (regnum) == REG_VALID
-      && (memcmp (register_buffer (regnum), buf,
-		  m_descr->sizeof_register[regnum]) == 0))
+      && (memcmp (register_buffer (regnum).data (), src.data (), src.size ())
+	  == 0))
     return;
 
   gdb::optional<scoped_restore_current_thread> maybe_restore_thread
     = maybe_switch_inferior (m_inf_for_target_calls);
 
   target_prepare_to_store (this);
-  raw_supply (regnum, buf);
+  raw_supply (regnum, src, 1.0f);
 
   /* Invalidate the register after it is written, in case of a
      failure.  */
@@ -871,211 +912,248 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
 }
 
 void
-regcache::cooked_write (int regnum, const gdb_byte *buf)
+regcache::raw_write (int regnum, const gdb_byte *src)
+{
+  assert_regnum (regnum);
+
+  int size = m_descr->sizeof_register[regnum];
+  raw_write (regnum, gdb::make_array_view (src, size), 1.0f);
+}
+
+/* See regcache.h.  */
+
+void
+regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float)
 {
   gdb_assert (regnum >= 0);
   gdb_assert (regnum < m_descr->nr_cooked_registers);
+
   if (regnum < num_raw_registers ())
-    raw_write (regnum, buf);
+    raw_write (regnum, src, 1.0f);
   else
-    gdbarch_pseudo_register_write (m_descr->gdbarch, this,
-				   regnum, buf);
+    gdbarch_pseudo_register_write (m_descr->gdbarch, this, regnum,
+				   src.data ());
 }
 
 /* See regcache.h.  */
 
-enum register_status
-readable_regcache::read_part (int regnum, int offset, int len,
-			      gdb_byte *out, bool is_raw)
+void
+regcache::cooked_write (int regnum, const gdb_byte *src)
+{
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+  int size = m_descr->sizeof_register[regnum];
+  return cooked_write (regnum, gdb::make_array_view (src, size), 1.0f);
+}
+
+/* See regcache.h.  */
+
+register_status
+readable_regcache::read_part (int regnum, int offset,
+			      gdb::array_view<gdb_byte> dst, bool is_raw)
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (out != NULL);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + dst.size () <= reg_size);
 
-  if (len == 0)
+  if (dst.size () == 0)
     {
       /* Nothing to do.  */
       return REG_VALID;
     }
 
-  if (len == reg_size)
+  if (dst.size () == reg_size)
     {
       /* Read the full register.  */
-      return (is_raw) ? raw_read (regnum, out) : cooked_read (regnum, out);
+      if (is_raw)
+	return raw_read (regnum, dst, 1.0f);
+      else
+	return cooked_read (regnum, dst, 1.0f);
     }
 
-  enum register_status status;
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
-
   /* Read full register to buffer.  */
-  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
+  register_status status;
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
+
+  if (is_raw)
+    status = raw_read (regnum, reg, 1.0f);
+  else
+    status = cooked_read (regnum, reg, 1.0f);
+
   if (status != REG_VALID)
     return status;
 
   /* Copy out.  */
-  memcpy (out, reg + offset, len);
+  copy (reg.slice (offset, dst.size ()), dst);
   return REG_VALID;
 }
 
 /* See regcache.h.  */
 
 void
-reg_buffer::raw_collect_part (int regnum, int offset, int len,
-			      gdb_byte *out) const
+reg_buffer::raw_collect_part (int regnum, int offset,
+			      gdb::array_view<gdb_byte> dst) const
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (out != nullptr);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + dst.size () <= reg_size);
 
-  if (len == 0)
+  if (dst.size () == 0)
     {
       /* Nothing to do.  */
       return;
     }
 
-  if (len == reg_size)
+  if (dst.size () == reg_size)
     {
       /* Collect the full register.  */
-      return raw_collect (regnum, out);
+      return raw_collect (regnum, dst, 1.0f);
     }
 
   /* Read to buffer, then write out.  */
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
-  raw_collect (regnum, reg);
-  memcpy (out, reg + offset, len);
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
+  raw_collect (regnum, reg, 1.0f);
+  copy (reg.slice (offset, dst.size ()), dst);
 }
 
 /* See regcache.h.  */
 
-enum register_status
-regcache::write_part (int regnum, int offset, int len,
-		      const gdb_byte *in, bool is_raw)
+register_status
+regcache::write_part (int regnum, int offset,
+		      gdb::array_view<const gdb_byte> src, bool is_raw)
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (in != NULL);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + src.size () <= reg_size);
 
-  if (len == 0)
+  if (src.size () == 0)
     {
       /* Nothing to do.  */
       return REG_VALID;
     }
 
-  if (len == reg_size)
+  if (src.size () == reg_size)
     {
       /* Write the full register.  */
-      (is_raw) ? raw_write (regnum, in) : cooked_write (regnum, in);
+      if (is_raw)
+	raw_write (regnum, src, 1.0f);
+      else
+	cooked_write (regnum, src, 1.0f);
+
       return REG_VALID;
     }
 
-  enum register_status status;
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
-
   /* Read existing register to buffer.  */
-  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
+  register_status status;
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
+
+  if (is_raw)
+    status = raw_read (regnum, reg, 1.0f);
+  else
+    status = cooked_read (regnum, reg, 1.0f);
+
   if (status != REG_VALID)
     return status;
 
   /* Update buffer, then write back to regcache.  */
-  memcpy (reg + offset, in, len);
-  is_raw ? raw_write (regnum, reg) : cooked_write (regnum, reg);
+  copy (src, reg.slice (offset, src.size ()));
+
+  if (is_raw)
+    raw_write (regnum, reg, 1.0f);
+  else
+    cooked_write (regnum, reg, 1.0f);
+
   return REG_VALID;
 }
 
 /* See regcache.h.  */
 
 void
-reg_buffer::raw_supply_part (int regnum, int offset, int len,
-			     const gdb_byte *in)
+reg_buffer::raw_supply_part (int regnum, int offset,
+			     gdb::array_view<const gdb_byte> src)
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (in != nullptr);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + src.size () <= reg_size);
 
-  if (len == 0)
+  if (src.size () == 0)
     {
       /* Nothing to do.  */
       return;
     }
 
-  if (len == reg_size)
+  if (src.size () == reg_size)
     {
       /* Supply the full register.  */
-      return raw_supply (regnum, in);
+      return raw_supply (regnum, src, 1.0f);
     }
 
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
-
   /* Read existing value to buffer.  */
-  raw_collect (regnum, reg);
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
+  raw_collect (regnum, reg, 1.0f);
 
   /* Write to buffer, then write out.  */
-  memcpy (reg + offset, in, len);
-  raw_supply (regnum, reg);
+  copy (src, reg.slice (offset, src.size ()));
+  raw_supply (regnum, reg, 1.0f);
 }
 
-enum register_status
-readable_regcache::raw_read_part (int regnum, int offset, int len,
-				  gdb_byte *buf)
+register_status
+readable_regcache::raw_read_part (int regnum, int offset,
+				  gdb::array_view<gdb_byte> dst)
 {
   assert_regnum (regnum);
-  return read_part (regnum, offset, len, buf, true);
+  return read_part (regnum, offset, dst, true);
 }
 
 /* See regcache.h.  */
 
 void
-regcache::raw_write_part (int regnum, int offset, int len,
-			  const gdb_byte *buf)
+regcache::raw_write_part (int regnum, int offset,
+			  gdb::array_view<const gdb_byte> src)
 {
   assert_regnum (regnum);
-  write_part (regnum, offset, len, buf, true);
+  write_part (regnum, offset, src, true);
 }
 
 /* See regcache.h.  */
 
-enum register_status
-readable_regcache::cooked_read_part (int regnum, int offset, int len,
-				     gdb_byte *buf)
+register_status
+readable_regcache::cooked_read_part (int regnum, int offset,
+				     gdb::array_view<gdb_byte> dst)
 {
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
-  return read_part (regnum, offset, len, buf, false);
+  return read_part (regnum, offset, dst, false);
 }
 
 /* See regcache.h.  */
 
 void
-regcache::cooked_write_part (int regnum, int offset, int len,
-			     const gdb_byte *buf)
+regcache::cooked_write_part (int regnum, int offset,
+			     gdb::array_view<const gdb_byte> src)
 {
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
-  write_part (regnum, offset, len, buf, false);
+  write_part (regnum, offset, src, false);
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-reg_buffer::raw_supply (int regnum, const void *buf)
+reg_buffer::raw_supply (int regnum, gdb::array_view<const gdb_byte> src, float)
 {
-  void *regbuf;
-  size_t size;
-
-  assert_regnum (regnum);
-
-  regbuf = register_buffer (regnum);
-  size = m_descr->sizeof_register[regnum];
+  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
 
-  if (buf)
+  if (src.data () != nullptr)
     {
-      memcpy (regbuf, buf, size);
+      copy (src, dst);
       m_register_status[regnum] = REG_VALID;
     }
   else
@@ -1083,7 +1161,7 @@ reg_buffer::raw_supply (int regnum, const void *buf)
       /* This memset not strictly necessary, but better than garbage
 	 in case the register value manages to escape somewhere (due
 	 to a bug, no less).  */
-      memset (regbuf, 0, size);
+      memset (dst.data (), 0, dst.size ());
       m_register_status[regnum] = REG_UNAVAILABLE;
     }
 }
@@ -1091,19 +1169,25 @@ reg_buffer::raw_supply (int regnum, const void *buf)
 /* See regcache.h.  */
 
 void
-reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
-				int addr_len, bool is_signed)
+reg_buffer::raw_supply (int regnum, const void *src)
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
-  gdb_byte *regbuf;
-  size_t regsize;
-
   assert_regnum (regnum);
 
-  regbuf = register_buffer (regnum);
-  regsize = m_descr->sizeof_register[regnum];
+  int size = m_descr->sizeof_register[regnum];
+  raw_supply (regnum, gdb::make_array_view ((const gdb_byte *) src, size),
+	      1.0f);
+}
+
+/* See regcache.h.  */
+
+void
+reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr, int addr_len,
+				bool is_signed)
+{
+  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
+  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
 
-  copy_integer_to_size (regbuf, regsize, addr, addr_len, is_signed,
+  copy_integer_to_size (dst.data (), dst.size (), addr, addr_len, is_signed,
 			byte_order);
   m_register_status[regnum] = REG_VALID;
 }
@@ -1113,32 +1197,31 @@ reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
 void
 reg_buffer::raw_supply_zeroed (int regnum)
 {
-  void *regbuf;
-  size_t size;
-
-  assert_regnum (regnum);
-
-  regbuf = register_buffer (regnum);
-  size = m_descr->sizeof_register[regnum];
-
-  memset (regbuf, 0, size);
+  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
+  memset (dst.data (), 0, dst.size ());
   m_register_status[regnum] = REG_VALID;
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-reg_buffer::raw_collect (int regnum, void *buf) const
+reg_buffer::raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
+			 float) const
 {
-  const void *regbuf;
-  size_t size;
+  gdb::array_view<const gdb_byte> src = register_buffer (regnum);
+  copy (src, dst);
+}
 
-  gdb_assert (buf != NULL);
+/* See regcache.h.  */
+
+void
+reg_buffer::raw_collect (int regnum, void *dst) const
+{
   assert_regnum (regnum);
 
-  regbuf = register_buffer (regnum);
-  size = m_descr->sizeof_register[regnum];
-  memcpy (buf, regbuf, size);
+  int size = m_descr->sizeof_register[regnum];
+  return raw_collect (regnum, gdb::make_array_view ((gdb_byte *) dst, size),
+		      1.0f);
 }
 
 /* See regcache.h.  */
@@ -1147,16 +1230,9 @@ void
 reg_buffer::raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
 				 bool is_signed) const
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
-  const gdb_byte *regbuf;
-  size_t regsize;
-
-  assert_regnum (regnum);
-
-  regbuf = register_buffer (regnum);
-  regsize = m_descr->sizeof_register[regnum];
-
-  copy_integer_to_size (addr, addr_len, regbuf, regsize, is_signed,
+  gdb::array_view<const gdb_byte> dst = register_buffer (regnum);
+  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
+  copy_integer_to_size (addr, addr_len, dst.data (), dst.size (), is_signed,
 			byte_order);
 }
 
@@ -1175,7 +1251,8 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
 
   if (out_buf != nullptr)
     {
-      raw_collect_part (regnum, 0, reg_size, out_buf + offs);
+      raw_collect_part (regnum, 0,
+			gdb::make_array_view (out_buf + offs, reg_size));
 
       /* Ensure any additional space is cleared.  */
       if (slot_size > reg_size)
@@ -1186,12 +1263,14 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
       /* Zero-extend the register value if the slot is smaller than the register.  */
       if (slot_size < register_size (gdbarch, regnum))
 	out_regcache->raw_supply_zeroed (regnum);
-      out_regcache->raw_supply_part (regnum, 0, reg_size, in_buf + offs);
+      out_regcache->raw_supply_part (regnum, 0,
+				     gdb::make_array_view (in_buf + offs,
+							   reg_size));
     }
   else
     {
       /* Invalidate the register.  */
-      out_regcache->raw_supply (regnum, nullptr);
+      out_regcache->raw_supply (regnum, {});
     }
 }
 
@@ -1322,13 +1401,12 @@ bool
 reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
 {
   gdb_assert (buf != NULL);
-  assert_regnum (regnum);
 
-  const char *regbuf = (const char *) register_buffer (regnum);
-  size_t size = m_descr->sizeof_register[regnum];
-  gdb_assert (size >= offset);
+  gdb::array_view<const gdb_byte> regbuf = register_buffer (regnum);
+  gdb_assert (offset < regbuf.size ());
+  regbuf = regbuf.slice (offset);
 
-  return (memcmp (buf, regbuf + offset, size - offset) == 0);
+  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
 }
 
 /* Special handling for register PC.  */
@@ -1417,17 +1495,15 @@ regcache::debug_print_register (const char *func,  int regno)
   if (regno >= 0 && regno < gdbarch_num_regs (gdbarch))
     {
       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-      int size = register_size (gdbarch, regno);
-      gdb_byte *buf = register_buffer (regno);
+      gdb::array_view<gdb_byte> buf = register_buffer (regno);
 
       gdb_printf (gdb_stdlog, " = ");
-      for (int i = 0; i < size; i++)
-	{
-	  gdb_printf (gdb_stdlog, "%02x", buf[i]);
-	}
-      if (size <= sizeof (LONGEST))
+      for (gdb_byte byte : buf)
+	gdb_printf (gdb_stdlog, "%02x", byte);
+
+      if (buf.size () <= sizeof (LONGEST))
 	{
-	  ULONGEST val = extract_unsigned_integer (buf, size, byte_order);
+	  ULONGEST val = extract_unsigned_integer (buf, byte_order);
 
 	  gdb_printf (gdb_stdlog, " %s %s",
 		      core_addr_to_string_nz (val), plongest (val));
@@ -1889,7 +1965,7 @@ cooked_read_test (struct gdbarch *gdbarch)
   readwrite.set_ptid (mockctx.mock_ptid);
   gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum));
 
-  readwrite.raw_read (nonzero_regnum, buf.data ());
+  readwrite.raw_read (nonzero_regnum, buf, 1.0f);
 
   /* raw_read calls target_fetch_registers.  */
   SELF_CHECK (mockctx.mock_target.fetch_registers_called > 0);
@@ -1910,9 +1986,8 @@ cooked_read_test (struct gdbarch *gdbarch)
 
       gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
 
-      SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum,
-						      inner_buf.data ()));
-
+      SELF_CHECK (REG_VALID
+		  == readwrite.cooked_read (regnum, inner_buf, 1.0f));
       SELF_CHECK (mockctx.mock_target.fetch_registers_called == 0);
       SELF_CHECK (mockctx.mock_target.store_registers_called == 0);
       SELF_CHECK (mockctx.mock_target.xfer_partial_called == 0);
@@ -1932,8 +2007,7 @@ cooked_read_test (struct gdbarch *gdbarch)
 	continue;
 
       gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
-      enum register_status status = readonly.cooked_read (regnum,
-							  inner_buf.data ());
+      register_status status = readonly.cooked_read (regnum, inner_buf, 1.0f);
 
       if (regnum < gdbarch_num_regs (gdbarch))
 	{
@@ -2023,8 +2097,8 @@ cooked_write_test (struct gdbarch *gdbarch)
 	      && regnum <= gdbarch_num_regs (gdbarch) + 4))
 	continue;
 
-      std::vector<gdb_byte> expected (register_size (gdbarch, regnum), 0);
-      std::vector<gdb_byte> buf (register_size (gdbarch, regnum), 0);
+      gdb::byte_vector expected (register_size (gdbarch, regnum), 0);
+      gdb::byte_vector buf (register_size (gdbarch, regnum), 0);
       const auto type = register_type (gdbarch, regnum);
 
       if (type->code () == TYPE_CODE_FLT
@@ -2079,9 +2153,9 @@ cooked_write_test (struct gdbarch *gdbarch)
 	  SELF_CHECK (0);
 	}
 
-      readwrite.cooked_write (regnum, expected.data ());
+      readwrite.cooked_write (regnum, expected, 1.0f);
 
-      SELF_CHECK (readwrite.cooked_read (regnum, buf.data ()) == REG_VALID);
+      SELF_CHECK (readwrite.cooked_read (regnum, buf, 1.0f) == REG_VALID);
       SELF_CHECK (expected == buf);
     }
 }
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 57ddac465f09..123c9248c345 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -20,6 +20,7 @@
 #ifndef REGCACHE_H
 #define REGCACHE_H
 
+#include "gdbsupport/array-view.h"
 #include "gdbsupport/common-regcache.h"
 #include "gdbsupport/function-view.h"
 
@@ -172,8 +173,8 @@ extern struct type *register_type (struct gdbarch *gdbarch, int regnum);
    
 extern int register_size (struct gdbarch *gdbarch, int regnum);
 
-typedef gdb::function_view<register_status (int regnum, gdb_byte *buf)>
-  register_read_ftype;
+using register_read_ftype
+  = gdb::function_view<register_status (int, gdb::array_view<gdb_byte>)>;
 
 /* A (register_number, register_value) pair.  */
 
@@ -199,7 +200,11 @@ class reg_buffer : public reg_buffer_common
   enum register_status get_register_status (int regnum) const override;
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_collect (int regnum, void *buf) const override;
+  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
+		    float) const override;
+
+  /* Deprecated overload of the above.  */
+  void raw_collect (int regnum, void *dst) const;
 
   /* Collect register REGNUM from REGCACHE.  Store collected value as an integer
      at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
@@ -209,17 +214,24 @@ class reg_buffer : public reg_buffer_common
   void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
 			    bool is_signed) const;
 
-  /* Collect register REGNUM from REGCACHE, starting at OFFSET in register,
-     reading only LEN.  */
-  void raw_collect_part (int regnum, int offset, int len, gdb_byte *out) const;
+  /* Collect part of register REGNUM from this register buffer.  Start at OFFSET
+     in register.  The size is given by the size of DST.  */
+  void raw_collect_part (int regnum, int offset,
+			 gdb::array_view<gdb_byte> dst) const;
+
+  /* Deprecated overload of the above.  */
+  void raw_collect_part (int regnum, int offset, int len, gdb_byte *dst) const
+  { raw_collect_part (regnum, offset, gdb::make_array_view (dst, len)); }
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_supply (int regnum, const void *buf) override;
+  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
+		   float) override;
+
+  /* Deprecated overload of the above.  */
+  void raw_supply (int regnum, const void *src);
 
   void raw_supply (int regnum, const reg_buffer &src)
-  {
-    raw_supply (regnum, src.register_buffer (regnum));
-  }
+  { raw_supply (regnum, src.register_buffer (regnum), 1.0f); }
 
   /* Supply register REGNUM to REGCACHE.  Value to supply is an integer stored
      at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
@@ -234,9 +246,11 @@ class reg_buffer : public reg_buffer_common
      unavailable).  */
   void raw_supply_zeroed (int regnum);
 
-  /* Supply register REGNUM to REGCACHE, starting at OFFSET in register, writing
-     only LEN, without editing the rest of the register.  */
-  void raw_supply_part (int regnum, int offset, int len, const gdb_byte *in);
+  /* Supply part of register REGNUM to this register buffer.  Start at OFFSET in
+     the register.  The size is given by the size of SRC.  The rest of the
+     register left untouched.  */
+  void raw_supply_part (int regnum, int offset,
+			gdb::array_view<const gdb_byte> src);
 
   void invalidate (int regnum);
 
@@ -251,7 +265,11 @@ class reg_buffer : public reg_buffer_common
 
   int num_raw_registers () const;
 
-  gdb_byte *register_buffer (int regnum) const;
+  /* Return a view on register REGNUM's buffer cache.  */
+  template <typename ElemType>
+  gdb::array_view<ElemType> register_buffer (int regnum) const;
+  gdb::array_view<const gdb_byte> register_buffer (int regnum) const;
+  gdb::array_view<gdb_byte> register_buffer (int regnum);
 
   /* Save a register cache.  The set of registers saved into the
      regcache determined by the save_reggroup.  COOKED_READ returns
@@ -281,27 +299,42 @@ class readable_regcache : public reg_buffer
 
   /* Transfer a raw register [0..NUM_REGS) from core-gdb to this regcache,
      return its value in *BUF and return its availability status.  */
+  register_status raw_read (int regnum, gdb::array_view<gdb_byte> dst, float);
+
+  /* Deprecated overload of the above.  */
+  register_status raw_read (int regnum, gdb_byte *dst);
 
-  enum register_status raw_read (int regnum, gdb_byte *buf);
   template<typename T, typename = RequireLongest<T>>
-  enum register_status raw_read (int regnum, T *val);
+  register_status raw_read (int regnum, T *val);
 
   /* Partial transfer of raw registers.  Return the status of the register.  */
-  enum register_status raw_read_part (int regnum, int offset, int len,
-				      gdb_byte *buf);
+  register_status raw_read_part (int regnum, int offset,
+				 gdb::array_view<gdb_byte> dst);
+
+  /* Deprecated overload of the above.  */
+  register_status raw_read_part (int regnum, int offset, int len,
+				 gdb_byte *dst)
+  { return raw_read_part (regnum, offset, gdb::make_array_view (dst, len)); }
 
   /* Make certain that the register REGNUM is up-to-date.  */
   virtual void raw_update (int regnum) = 0;
 
   /* Transfer a raw register [0..NUM_REGS+NUM_PSEUDO_REGS) from core-gdb to
-     this regcache, return its value in *BUF and return its availability status.  */
-  enum register_status cooked_read (int regnum, gdb_byte *buf);
+     this regcache, return its value in DST and return its availability status.  */
+  register_status cooked_read (int regnum, gdb::array_view<gdb_byte> dst,
+			       float);
+  register_status cooked_read (int regnum, gdb_byte *dst);
+
   template<typename T, typename = RequireLongest<T>>
-  enum register_status cooked_read (int regnum, T *val);
+  register_status cooked_read (int regnum, T *val);
 
   /* Partial transfer of a cooked register.  */
-  enum register_status cooked_read_part (int regnum, int offset, int len,
-					 gdb_byte *buf);
+  register_status cooked_read_part (int regnum, int offset,
+				    gdb::array_view<gdb_byte> dst);
+
+  /* Deprecated overload of the above.  */
+  register_status cooked_read_part (int regnum, int offset, int len, gdb_byte *src)
+  { return cooked_read_part (regnum, offset, gdb::make_array_view (src, len)); }
 
   /* Read register REGNUM from the regcache and return a new value.  This
      will call mark_value_bytes_unavailable as appropriate.  */
@@ -311,8 +344,8 @@ class readable_regcache : public reg_buffer
 
   /* Perform a partial register transfer using a read, modify, write
      operation.  Will fail if register is currently invalid.  */
-  enum register_status read_part (int regnum, int offset, int len,
-				  gdb_byte *out, bool is_raw);
+  register_status read_part (int regnum, int offset,
+			     gdb::array_view<gdb_byte> dst, bool is_raw);
 };
 
 /* Buffer of registers, can be read and written.  */
@@ -354,13 +387,19 @@ class regcache : public detached_regcache
   /* Update the value of raw register REGNUM (in the range [0..NUM_REGS)) and
      transfer its value to core-gdb.  */
 
-  void raw_write (int regnum, const gdb_byte *buf);
+  void raw_write (int regnum, gdb::array_view<const gdb_byte> src, float);
+
+  /* Deprecated overload of the above.  */
+  void raw_write (int regnum, const gdb_byte *src);
 
   template<typename T, typename = RequireLongest<T>>
   void raw_write (int regnum, T val);
 
   /* Transfer of pseudo-registers.  */
-  void cooked_write (int regnum, const gdb_byte *buf);
+  void cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float);
+
+  /* Deprecated overload of the above.  */
+  void cooked_write (int regnum, const gdb_byte *src);
 
   template<typename T, typename = RequireLongest<T>>
   void cooked_write (int regnum, T val);
@@ -369,12 +408,21 @@ class regcache : public detached_regcache
 
   /* Partial transfer of raw registers.  Perform read, modify, write style
      operations.  */
-  void raw_write_part (int regnum, int offset, int len, const gdb_byte *buf);
+  void raw_write_part (int regnum, int offset,
+		       gdb::array_view<const gdb_byte> src);
+
+  /* Deprecated overload of the above.  */
+  void raw_write_part (int regnum, int offset, int len, const gdb_byte *src)
+  { raw_write_part (regnum, offset, gdb::make_array_view (src, len)); }
 
   /* Partial transfer of a cooked register.  Perform read, modify, write style
      operations.  */
-  void cooked_write_part (int regnum, int offset, int len,
-			  const gdb_byte *buf);
+  void cooked_write_part (int regnum, int offset,
+			  gdb::array_view<const gdb_byte> src);
+
+  /* Deprecated overload of the above.  */
+  void cooked_write_part (int regnum, int offset, int len, const gdb_byte *src)
+  { cooked_write_part (regnum, offset, gdb::make_array_view (src, len)); }
 
   /* Transfer a set of registers (as described by REGSET) between
      REGCACHE and BUF.  If REGNUM == -1, transfer all registers
@@ -442,8 +490,9 @@ class regcache : public detached_regcache
 
   /* Perform a partial register transfer using a read, modify, write
      operation.  */
-  enum register_status write_part (int regnum, int offset, int len,
-				   const gdb_byte *in, bool is_raw);
+  register_status write_part (int regnum, int offset,
+			      gdb::array_view<const gdb_byte> src,
+			      bool is_raw);
 
   /* The address space of this register cache (for registers where it
      makes sense, like PC or SP).  */
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 4ac0fb659c3b..e1c174b56cb6 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -315,27 +315,33 @@ regcache_register_size (const reg_buffer_common *regcache, int n)
     (gdb::checked_static_cast<const struct regcache *> (regcache)->tdesc, n);
 }
 
-static unsigned char *
+static gdb::array_view<gdb_byte>
 register_data (const struct regcache *regcache, int n)
 {
-  return (regcache->registers
-	  + find_register_by_number (regcache->tdesc, n).offset / 8);
+  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
+  return gdb::make_array_view (regcache->registers + reg.offset / 8,
+			       reg.size / 8);
 }
 
 void
-supply_register (struct regcache *regcache, int n, const void *buf)
+supply_register (struct regcache *regcache, int n, const void *vbuf)
 {
-  return regcache->raw_supply (n, buf);
+  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
+  const gdb_byte *buf = static_cast<const gdb_byte *> (vbuf);
+  return regcache->raw_supply (n, gdb::make_array_view (buf, reg.size / 8),
+			       1.0f);
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-regcache::raw_supply (int n, const void *buf)
+regcache::raw_supply (int n, gdb::array_view<const gdb_byte> src, float)
 {
-  if (buf)
+  auto dst = register_data (this, n);
+
+  if (src.data () != nullptr)
     {
-      memcpy (register_data (this, n), buf, register_size (tdesc, n));
+      copy (src, dst);
 #ifndef IN_PROCESS_AGENT
       if (register_status != NULL)
 	register_status[n] = REG_VALID;
@@ -343,7 +349,7 @@ regcache::raw_supply (int n, const void *buf)
     }
   else
     {
-      memset (register_data (this, n), 0, register_size (tdesc, n));
+      memset (dst.data (), 0, dst.size ());
 #ifndef IN_PROCESS_AGENT
       if (register_status != NULL)
 	register_status[n] = REG_UNAVAILABLE;
@@ -356,8 +362,8 @@ regcache::raw_supply (int n, const void *buf)
 void
 supply_register_zeroed (struct regcache *regcache, int n)
 {
-  memset (register_data (regcache, n), 0,
-	  register_size (regcache->tdesc, n));
+  auto dst = register_data (regcache, n);
+  memset (dst.data (), 0, dst.size ());
 #ifndef IN_PROCESS_AGENT
   if (regcache->register_status != NULL)
     regcache->register_status[n] = REG_VALID;
@@ -426,17 +432,20 @@ supply_register_by_name (struct regcache *regcache,
 #endif
 
 void
-collect_register (struct regcache *regcache, int n, void *buf)
+collect_register (struct regcache *regcache, int n, void *vbuf)
 {
-  regcache->raw_collect (n, buf);
+  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
+  gdb_byte *buf = static_cast<gdb_byte *> (vbuf);
+  regcache->raw_collect (n, gdb::make_array_view (buf, reg.size / 8), 1.0f);
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-regcache::raw_collect (int n, void *buf) const
+regcache::raw_collect (int n, gdb::array_view<gdb_byte> dst, float) const
 {
-  memcpy (buf, register_data (this, n), register_size (tdesc, n));
+  auto src = register_data (this, n);
+  copy (src, dst);
 }
 
 enum register_status
@@ -476,8 +485,7 @@ regcache_raw_get_unsigned_by_name (struct regcache *regcache,
 void
 collect_register_as_string (struct regcache *regcache, int n, char *buf)
 {
-  bin2hex (register_data (regcache, n), buf,
-	   register_size (regcache->tdesc, n));
+  bin2hex (register_data (regcache, n), buf);
 }
 
 void
@@ -524,9 +532,9 @@ regcache::raw_compare (int regnum, const void *buf, int offset) const
 {
   gdb_assert (buf != NULL);
 
-  const unsigned char *regbuf = register_data (this, regnum);
-  int size = register_size (tdesc, regnum);
-  gdb_assert (size >= offset);
+  gdb::array_view<const gdb_byte> regbuf = register_data (this, regnum);
+  gdb_assert (offset < regbuf.size ());
+  regbuf = regbuf.slice (offset);
 
-  return (memcmp (buf, regbuf + offset, size - offset) == 0);
+  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
 }
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 7248bcf5808a..ba093c97c1f0 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -50,10 +50,12 @@ struct regcache : public reg_buffer_common
   enum register_status get_register_status (int regnum) const override;
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_supply (int regnum, const void *buf) override;
+  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
+		   float) override;
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_collect (int regnum, void *buf) const override;
+  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
+		    float) const override;
 
   /* See gdbsupport/common-regcache.h.  */
   bool raw_compare (int regnum, const void *buf, int offset) const override;
diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
index 6d98ca8c92ed..75d209f7cbc0 100644
--- a/gdbsupport/common-regcache.h
+++ b/gdbsupport/common-regcache.h
@@ -78,11 +78,44 @@ struct reg_buffer_common
      buffer.  */
   virtual register_status get_register_status (int regnum) const = 0;
 
-  /* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE.  */
-  virtual void raw_supply (int regnum, const void *buf) = 0;
+  /* Supply register REGNUM, whose contents are stored in SRC, to this register
+     buffer.  */
+  virtual void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
+			   float) = 0;
+
+  void raw_supply (int regnum, const uint64_t *src)
+  {
+    raw_supply (regnum,
+		gdb::make_array_view ((const gdb_byte *) src, sizeof (*src)),
+		1.0f);
+  }
+
+  void raw_supply (int regnum, const gdb_byte *src)
+  {
+    raw_supply (regnum,
+		gdb::make_array_view (src,
+				      regcache_register_size (this, regnum)),
+		1.0f);
+  }
+
+  /* Collect register REGNUM from this register buffer and store its contents in
+     DST.  */
+  virtual void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
+			    float) const = 0;
+
+  void raw_collect (int regnum, uint64_t *dst) const
+  {
+    raw_collect (regnum,
+		 gdb::make_array_view ((gdb_byte *) dst, sizeof (*dst)), 1.0f);
+  };
 
-  /* Collect register REGNUM from REGCACHE and store its contents in BUF.  */
-  virtual void raw_collect (int regnum, void *buf) const = 0;
+  void raw_collect (int regnum, gdb_byte *dst)
+  {
+    raw_collect (regnum,
+		 gdb::make_array_view (dst,
+				       regcache_register_size (this, regnum)),
+		 1.0f);
+  }
 
   /* Compare the contents of the register stored in the regcache (ignoring the
      first OFFSET bytes) to the contents of BUF (without any offset).  Returns
diff --git a/gdbsupport/rsp-low.cc b/gdbsupport/rsp-low.cc
index 3d8c2002956e..632be265c00c 100644
--- a/gdbsupport/rsp-low.cc
+++ b/gdbsupport/rsp-low.cc
@@ -143,6 +143,14 @@ bin2hex (const gdb_byte *bin, char *hex, int count)
 
 /* See rsp-low.h.  */
 
+int
+bin2hex (gdb::array_view<gdb_byte> bin, char *hex)
+{
+  return bin2hex (bin.data (), hex, bin.size ());
+}
+
+/* See rsp-low.h.  */
+
 std::string
 bin2hex (const gdb_byte *bin, int count)
 {
diff --git a/gdbsupport/rsp-low.h b/gdbsupport/rsp-low.h
index 327d5f3a0947..1fc2572a7f5c 100644
--- a/gdbsupport/rsp-low.h
+++ b/gdbsupport/rsp-low.h
@@ -54,6 +54,8 @@ extern std::string hex2str (const char *hex, int count);
 
 extern int bin2hex (const gdb_byte *bin, char *hex, int count);
 
+extern int bin2hex (gdb::array_view<gdb_byte> bin, char *hex);
+
 /* Overloaded version of bin2hex that returns a std::string.  */
 
 extern std::string bin2hex (const gdb_byte *bin, int count);
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (4 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 05/24] gdb: change regcache interface to use array_view Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-13 15:00   ` Andrew Burgess
  2023-11-08  5:00 ` [PATCH 07/24] gdb: make put_frame_register take an array_view Simon Marchi
                   ` (20 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

I found this only by inspection: the myaddr pointer in
{get,put}_frame_register_bytes is reset to `buffer.data ()` at each
iteration.  This means that we will always use the bytes at the
beginning of `buffer` to read or write to the registers, instead of
progressing in `buffer`.

Fix this by re-writing the functions to chip away the beginning of the
buffer array_view as we progress in reading or writing the data.

These bugs was introduced almost 3 years ago [1], and yet nobody
complained.  I'm wondering which architecture relies on that register
"overflow" behavior (reading or writing multiple consecutive registers
with one {get,put}_frame_register_bytes calls), and in which situation.
I find these functions a bit dangerous, if a caller mis-calculates
things, it could end up silently reading or writing to the next
register, even if it's not the intent.

If I could change it, I would prefer to have functions specifically made
for that ({get,put}_frame_register_bytes_consecutive or something like
that) and make {get,put}_frame_register_bytes only able to write within
a single register (which I presume represents most of the use cases of
the current {get,put}_frame_register_bytes).  If a caller mis-calculates
things and an overflow occurs while calling
{get,put}_frame_register_bytes, it would hit an assert.  The problem is
knowing which callers rely on the overflow behavior and which don't.

[1] https://gitlab.com/gnutools/binutils-gdb/-/commit/bdec2917b1e94c7198ba39919f45060067952f43

Change-Id: I43bd4a9f7fa8419d388a2b20bdc57d652688ddf8
---
 gdb/frame.c | 63 +++++++++++++++++++----------------------------------
 1 file changed, 23 insertions(+), 40 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index afadb8ac4e73..b3d99163b4dc 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1482,9 +1482,6 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
 			  int *optimizedp, int *unavailablep)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
-  int i;
-  int maxsize;
-  int numregs;
 
   /* Skip registers wholly inside of OFFSET.  */
   while (offset >= register_size (gdbarch, regnum))
@@ -1495,9 +1492,9 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
 
   /* Ensure that we will not read beyond the end of the register file.
      This can only ever happen if the debug information is bad.  */
-  maxsize = -offset;
-  numregs = gdbarch_num_cooked_regs (gdbarch);
-  for (i = regnum; i < numregs; i++)
+  int maxsize = -offset;
+  int numregs = gdbarch_num_cooked_regs (gdbarch);
+  for (int i = regnum; i < numregs; i++)
     {
       int thissize = register_size (gdbarch, i);
 
@@ -1506,20 +1503,15 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
       maxsize += thissize;
     }
 
-  int len = buffer.size ();
-  if (len > maxsize)
+  if (buffer.size () > maxsize)
     error (_("Bad debug information detected: "
-	     "Attempt to read %d bytes from registers."), len);
+	     "Attempt to read %zu bytes from registers."), buffer.size ());
 
   /* Copy the data.  */
-  while (len > 0)
+  while (!buffer.empty ())
     {
-      int curr_len = register_size (gdbarch, regnum) - offset;
-
-      if (curr_len > len)
-	curr_len = len;
-
-      gdb_byte *myaddr = buffer.data ();
+      int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
+				    buffer.size ());
 
       if (curr_len == register_size (gdbarch, regnum))
 	{
@@ -1527,8 +1519,8 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
 	  CORE_ADDR addr;
 	  int realnum;
 
-	  frame_register (frame, regnum, optimizedp, unavailablep,
-			  &lval, &addr, &realnum, myaddr);
+	  frame_register (frame, regnum, optimizedp, unavailablep, &lval,
+			  &addr, &realnum, buffer.data ());
 	  if (*optimizedp || *unavailablep)
 	    return false;
 	}
@@ -1547,13 +1539,12 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
 	      return false;
 	    }
 
-	  memcpy (myaddr, value->contents_all ().data () + offset,
-		  curr_len);
+	  copy (value->contents_all ().slice (offset, curr_len),
+		buffer.slice (0, curr_len));
 	  release_value (value);
 	}
 
-      myaddr += curr_len;
-      len -= curr_len;
+      buffer = buffer.slice (curr_len);
       offset = 0;
       regnum++;
     }
@@ -1578,36 +1569,28 @@ put_frame_register_bytes (frame_info_ptr frame, int regnum,
       regnum++;
     }
 
-  int len = buffer.size ();
   /* Copy the data.  */
-  while (len > 0)
+  while (!buffer.empty ())
     {
-      int curr_len = register_size (gdbarch, regnum) - offset;
+      int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
+				    buffer.size ());
 
-      if (curr_len > len)
-	curr_len = len;
-
-      const gdb_byte *myaddr = buffer.data ();
       if (curr_len == register_size (gdbarch, regnum))
-	{
-	  put_frame_register (frame, regnum, myaddr);
-	}
+	put_frame_register (frame, regnum, buffer.data ());
       else
 	{
-	  struct value *value
+	  value *value
 	    = frame_unwind_register_value (frame_info_ptr (frame->next),
 					   regnum);
-	  gdb_assert (value != NULL);
+	  gdb_assert (value != nullptr);
 
-	  memcpy ((char *) value->contents_writeable ().data () + offset,
-		  myaddr, curr_len);
-	  put_frame_register (frame, regnum,
-			      value->contents_raw ().data ());
+	  copy (buffer.slice (0, curr_len),
+		value->contents_writeable ().slice (offset, curr_len));
+	  put_frame_register (frame, regnum, value->contents_raw ().data ());
 	  release_value (value);
 	}
 
-      myaddr += curr_len;
-      len -= curr_len;
+      buffer = buffer.slice (curr_len);
       offset = 0;
       regnum++;
     }
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 07/24] gdb: make put_frame_register take an array_view
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (5 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 08/24] gdb: change value_of_register and value_of_register_lazy to take the next frame Simon Marchi
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Change put_frame_register to take an array_view instead of a raw
pointer.

Add an assertion to verify that the number of bytes we try to write
matches the length of the register.

Change-Id: Ib75a9c8a12b47e203097621643eaa2c1830591ae
---
 gdb/alpha-tdep.c  | 12 ++++++------
 gdb/frame.c       | 13 ++++++++-----
 gdb/frame.h       |  2 +-
 gdb/i386-tdep.c   |  3 ++-
 gdb/i387-tdep.c   |  6 ++++--
 gdb/ia64-tdep.c   |  6 ++++--
 gdb/m68k-tdep.c   |  7 ++++---
 gdb/mips-tdep.c   |  5 +++--
 gdb/rs6000-tdep.c |  7 ++++---
 9 files changed, 36 insertions(+), 25 deletions(-)

diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c
index 9fb973597fc4..1901b9449e6c 100644
--- a/gdb/alpha-tdep.c
+++ b/gdb/alpha-tdep.c
@@ -263,14 +263,14 @@ static void
 alpha_value_to_register (frame_info_ptr frame, int regnum,
 			 struct type *valtype, const gdb_byte *in)
 {
-  gdb_byte out[ALPHA_REGISTER_SIZE];
-
+  int reg_size = register_size (get_frame_arch (frame), regnum);
   gdb_assert (valtype->length () == 4);
-  gdb_assert (register_size (get_frame_arch (frame), regnum)
-	      <= ALPHA_REGISTER_SIZE);
-  alpha_lds (get_frame_arch (frame), out, in);
+  gdb_assert (reg_size <= ALPHA_REGISTER_SIZE);
 
-  put_frame_register (frame, regnum, out);
+  gdb_byte out[ALPHA_REGISTER_SIZE];
+  alpha_lds (get_frame_arch (frame), out, in);
+  auto out_view = gdb::make_array_view (out, reg_size);
+  put_frame_register (frame, regnum, out_view);
 }
 
 \f
diff --git a/gdb/frame.c b/gdb/frame.c
index b3d99163b4dc..84ef8461b835 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1423,7 +1423,7 @@ read_frame_register_unsigned (frame_info_ptr frame, int regnum,
 
 void
 put_frame_register (frame_info_ptr frame, int regnum,
-		    const gdb_byte *buf)
+		    gdb::array_view<const gdb_byte> buf)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
   int realnum;
@@ -1431,6 +1431,9 @@ put_frame_register (frame_info_ptr frame, int regnum,
   int unavail;
   enum lval_type lval;
   CORE_ADDR addr;
+  int size = register_size (gdbarch, regnum);
+
+  gdb_assert (buf.size () == size);
 
   frame_register (frame, regnum, &optim, &unavail,
 		  &lval, &addr, &realnum, NULL);
@@ -1440,11 +1443,11 @@ put_frame_register (frame_info_ptr frame, int regnum,
     {
     case lval_memory:
       {
-	write_memory (addr, buf, register_size (gdbarch, regnum));
+	write_memory (addr, buf.data (), size);
 	break;
       }
     case lval_register:
-      get_current_regcache ()->cooked_write (realnum, buf);
+      get_current_regcache ()->cooked_write (realnum, buf, 1.0f);
       break;
     default:
       error (_("Attempt to assign to an unmodifiable value."));
@@ -1576,7 +1579,7 @@ put_frame_register_bytes (frame_info_ptr frame, int regnum,
 				    buffer.size ());
 
       if (curr_len == register_size (gdbarch, regnum))
-	put_frame_register (frame, regnum, buffer.data ());
+	put_frame_register (frame, regnum, buffer.slice (0, curr_len));
       else
 	{
 	  value *value
@@ -1586,7 +1589,7 @@ put_frame_register_bytes (frame_info_ptr frame, int regnum,
 
 	  copy (buffer.slice (0, curr_len),
 		value->contents_writeable ().slice (offset, curr_len));
-	  put_frame_register (frame, regnum, value->contents_raw ().data ());
+	  put_frame_register (frame, regnum, value->contents_raw ());
 	  release_value (value);
 	}
 
diff --git a/gdb/frame.h b/gdb/frame.h
index 1d7422cac32b..e3393cdd972c 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -724,7 +724,7 @@ extern bool read_frame_register_unsigned (frame_info_ptr frame,
    frame.  Note: this call makes the frame's state undefined.  The
    register and frame caches must be flushed.  */
 extern void put_frame_register (frame_info_ptr frame, int regnum,
-				const gdb_byte *buf);
+				gdb::array_view<const gdb_byte> buf);
 
 /* Read LEN bytes from one or multiple registers starting with REGNUM
    in frame FRAME, starting at OFFSET, into BUF.  If the register
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 2759c1a558c5..7f6ea5ba06ec 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -3917,7 +3917,8 @@ i386_value_to_register (frame_info_ptr frame, int regnum,
       gdb_assert (regnum != -1);
       gdb_assert (register_size (get_frame_arch (frame), regnum) == 4);
 
-      put_frame_register (frame, regnum, from);
+      auto from_view = gdb::make_array_view (from, 4);
+      put_frame_register (frame, regnum, from_view);
       regnum = i386_next_regnum (regnum);
       len -= 4;
       from += 4;
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index 47667da21c7d..74481f5aeb3c 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -397,8 +397,10 @@ i387_value_to_register (frame_info_ptr frame, int regnum,
     }
 
   /* Convert from TYPE.  */
-  target_float_convert (from, type, to, i387_ext_type (gdbarch));
-  put_frame_register (frame, regnum, to);
+  struct type *to_type = i387_ext_type (gdbarch);
+  target_float_convert (from, type, to, to_type);
+  auto to_view = gdb::make_array_view (to, to_type->length ());
+  put_frame_register (frame, regnum, to_view);
 }
 \f
 
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index cdd5553cf5d3..113d384e8649 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -1245,8 +1245,10 @@ ia64_value_to_register (frame_info_ptr frame, int regnum,
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
   gdb_byte out[IA64_FP_REGISTER_SIZE];
-  target_float_convert (in, valtype, out, ia64_ext_type (gdbarch));
-  put_frame_register (frame, regnum, out);
+  type *to_type = ia64_ext_type (gdbarch);
+  target_float_convert (in, valtype, out, to_type);
+  auto out_view = gdb::make_array_view (out, to_type->length ());
+  put_frame_register (frame, regnum, out_view);
 }
 
 
diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c
index 5b2a29a350e5..5a80ecb94390 100644
--- a/gdb/m68k-tdep.c
+++ b/gdb/m68k-tdep.c
@@ -236,8 +236,8 @@ m68k_value_to_register (frame_info_ptr frame, int regnum,
 			struct type *type, const gdb_byte *from)
 {
   gdb_byte to[M68K_MAX_REGISTER_SIZE];
-  struct type *fpreg_type = register_type (get_frame_arch (frame),
-					   M68K_FP0_REGNUM);
+  gdbarch *arch = get_frame_arch (frame);
+  struct type *fpreg_type = register_type (arch, M68K_FP0_REGNUM);
 
   /* We only support floating-point values.  */
   if (type->code () != TYPE_CODE_FLT)
@@ -249,7 +249,8 @@ m68k_value_to_register (frame_info_ptr frame, int regnum,
 
   /* Convert from TYPE.  */
   target_float_convert (from, type, to, fpreg_type);
-  put_frame_register (frame, regnum, to);
+  auto to_view = gdb::make_array_view (to, fpreg_type->length ());
+  put_frame_register (frame, regnum, to_view);
 }
 
 \f
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index d40d28e85de5..6bd0a66a6284 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -990,8 +990,9 @@ mips_value_to_register (frame_info_ptr frame, int regnum,
 
   if (mips_convert_register_float_case_p (gdbarch, regnum, type))
     {
-      put_frame_register (frame, regnum + 0, from + 4);
-      put_frame_register (frame, regnum + 1, from + 0);
+      auto from_view = gdb::make_array_view (from, 8);
+      put_frame_register (frame, regnum, from_view.slice (4));
+      put_frame_register (frame, regnum + 1, from_view.slice (0, 4));
     }
   else if (mips_convert_register_gpreg_case_p (gdbarch, regnum, type))
     {
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index bae6737852d8..9fb3f326bd07 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -2715,9 +2715,10 @@ rs6000_value_to_register (frame_info_ptr frame,
 
   gdb_assert (type->code () == TYPE_CODE_FLT);
 
-  target_float_convert (from, type,
-			to, builtin_type (gdbarch)->builtin_double);
-  put_frame_register (frame, regnum, to);
+  struct type *to_type = builtin_type (gdbarch)->builtin_double;
+  target_float_convert (from, type, to, to_type);
+  auto to_view = gdb::make_array_view (to, to_type->length ());
+  put_frame_register (frame, regnum, to_view);
 }
 
  /* The type of a function that moves the value of REG between CACHE
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 08/24] gdb: change value_of_register and value_of_register_lazy to take the next frame
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (6 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 07/24] gdb: make put_frame_register take an array_view Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 09/24] gdb: remove frame_register Simon Marchi
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Some functions related to the handling of registers in frames accept
"this frame", for which we want to read or write the register values,
while other functions accept "the next frame", which is the frame next
to that.  The later is needed because we sometimes need to read register
values for a frame that does not exist yet (usually when trying to
unwind that frame-to-be).

value_of_register and value_of_register_lazy both take "this frame",
even if what they ultimately want internally is "the next frame".  This
is annoying if you are in a spot that currently has "the next frame" and
need to call one of these functions (which happens later in this
series).  You need to get the previous frame only for those functions to
get the next frame again.  This is more manipulations, more chances of
mistake.

I propose to change these functions (and a few more functions in the
subsequent patches) to operate on "the next frame".  Things become a bit
less awkward when all these functions agree on which frame they take.

So, in this patch, change value_of_register_lazy and value_of_register
to take "the next frame" instead of "this frame".  This adds a lot of
get_next_frame_sentinel_okay, but if we convert the user registers API
to also use "the next frame" instead of "this frame", it will get simple
again.

Change-Id: Iaa24815e648fbe5ae3c214c738758890a91819cd
---
 gdb/aarch64-tdep.c     |  3 +--
 gdb/arm-tdep.c         |  4 ++--
 gdb/eval.c             |  3 ++-
 gdb/findvar.c          | 34 ++++++++++++----------------------
 gdb/frame-unwind.c     |  3 ++-
 gdb/guile/scm-frame.c  |  3 ++-
 gdb/infcmd.c           |  6 +++---
 gdb/loongarch-tdep.c   |  3 ++-
 gdb/mi/mi-main.c       |  3 ++-
 gdb/mips-tdep.c        |  2 +-
 gdb/nds32-tdep.c       |  5 +++--
 gdb/python/py-frame.c  |  3 ++-
 gdb/python/py-unwind.c |  4 ++--
 gdb/riscv-tdep.c       |  4 ++--
 gdb/s12z-tdep.c        |  2 +-
 gdb/std-regs.c         | 11 +++++++----
 gdb/value.h            |  9 +++++++--
 17 files changed, 53 insertions(+), 49 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index eaae2d91047b..50bfaa41eee2 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3449,9 +3449,8 @@ value_of_aarch64_user_reg (frame_info_ptr frame, const void *baton)
 {
   const int *reg_p = (const int *) baton;
 
-  return value_of_register (*reg_p, frame);
+  return value_of_register (*reg_p, get_next_frame_sentinel_okay (frame));
 }
-\f
 
 /* Implement the "software_single_step" gdbarch method, needed to
    single step through atomic sequences on AArch64.  */
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 2018f4ed0edd..5c8c4cd4e94b 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -9954,9 +9954,9 @@ static struct value *
 value_of_arm_user_reg (frame_info_ptr frame, const void *baton)
 {
   const int *reg_p = (const int *) baton;
-  return value_of_register (*reg_p, frame);
+  return value_of_register (*reg_p, get_next_frame_sentinel_okay (frame));
 }
-\f
+
 static enum gdb_osabi
 arm_elf_osabi_sniffer (bfd *abfd)
 {
diff --git a/gdb/eval.c b/gdb/eval.c
index b859e825925b..16206d92702a 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -1134,7 +1134,8 @@ eval_op_register (struct type *expect_type, struct expression *exp,
       && regno < gdbarch_num_cooked_regs (exp->gdbarch))
     val = value::zero (register_type (exp->gdbarch, regno), not_lval);
   else
-    val = value_of_register (regno, get_selected_frame (NULL));
+    val = value_of_register
+      (regno, get_next_frame_sentinel_okay (get_selected_frame ()));
   if (val == NULL)
     error (_("Value of register %s not available."), name);
   else
diff --git a/gdb/findvar.c b/gdb/findvar.c
index edb61fb27317..6ee31e780932 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -244,42 +244,32 @@ copy_integer_to_size (gdb_byte *dest, int dest_size, const gdb_byte *source,
     }
 }
 
-/* Return a `value' with the contents of (virtual or cooked) register
-   REGNUM as found in the specified FRAME.  The register's type is
-   determined by register_type ().  */
+/* See value.h.  */
 
-struct value *
-value_of_register (int regnum, frame_info_ptr frame)
+value *
+value_of_register (int regnum, frame_info_ptr next_frame)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct value *reg_val;
+  gdbarch *gdbarch = frame_unwind_arch (next_frame);
 
   /* User registers lie completely outside of the range of normal
      registers.  Catch them early so that the target never sees them.  */
   if (regnum >= gdbarch_num_cooked_regs (gdbarch))
-    return value_of_user_reg (regnum, frame);
+    return value_of_user_reg (regnum, get_prev_frame_always (next_frame));
 
-  reg_val = value_of_register_lazy (frame, regnum);
+  value *reg_val = value_of_register_lazy (next_frame, regnum);
   reg_val->fetch_lazy ();
   return reg_val;
 }
 
-/* Return a `value' with the contents of (virtual or cooked) register
-   REGNUM as found in the specified FRAME.  The register's type is
-   determined by register_type ().  The value is not fetched.  */
+/* See value.h.  */
 
-struct value *
-value_of_register_lazy (frame_info_ptr frame, int regnum)
+value *
+value_of_register_lazy (frame_info_ptr next_frame, int regnum)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
-  struct value *reg_val;
-  frame_info_ptr next_frame;
+  gdbarch *gdbarch = frame_unwind_arch (next_frame);
 
   gdb_assert (regnum < gdbarch_num_cooked_regs (gdbarch));
-
-  gdb_assert (frame != NULL);
-
-  next_frame = get_next_frame_sentinel_okay (frame);
+  gdb_assert (next_frame != nullptr);
 
   /* In some cases NEXT_FRAME may not have a valid frame-id yet.  This can
      happen if we end up trying to unwind a register as part of the frame
@@ -292,7 +282,7 @@ value_of_register_lazy (frame_info_ptr frame, int regnum)
   /* We should have a valid next frame.  */
   gdb_assert (frame_id_p (get_frame_id (next_frame)));
 
-  reg_val = value::allocate_lazy (register_type (gdbarch, regnum));
+  value *reg_val = value::allocate_lazy (register_type (gdbarch, regnum));
   reg_val->set_lval (lval_register);
   VALUE_REGNUM (reg_val) = regnum;
   VALUE_NEXT_FRAME_ID (reg_val) = get_frame_id (next_frame);
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index 927b6256b0b3..89ae3d70b200 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -278,7 +278,8 @@ struct value *
 frame_unwind_got_register (frame_info_ptr frame,
 			   int regnum, int new_regnum)
 {
-  return value_of_register_lazy (frame, new_regnum);
+  return value_of_register_lazy (get_next_frame_sentinel_okay (frame),
+				 new_regnum);
 }
 
 /* Return a value which indicates that FRAME saved REGNUM in memory at
diff --git a/gdb/guile/scm-frame.c b/gdb/guile/scm-frame.c
index 45863c587c1d..830acb8b53ed 100644
--- a/gdb/guile/scm-frame.c
+++ b/gdb/guile/scm-frame.c
@@ -835,7 +835,8 @@ gdbscm_frame_read_register (SCM self, SCM register_scm)
 						register_str,
 						strlen (register_str));
 	  if (regnum >= 0)
-	    value = value_of_register (regnum, frame);
+	    value = value_of_register (regnum,
+				       get_next_frame_sentinel_okay (frame));
 	}
     }
   catch (const gdb_exception &ex)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index cf8cd5279558..63b700c1e669 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2318,9 +2318,9 @@ default_print_registers_info (struct gdbarch *gdbarch,
       if (*(gdbarch_register_name (gdbarch, i)) == '\0')
 	continue;
 
-      default_print_one_register_info (file,
-				       gdbarch_register_name (gdbarch, i),
-				       value_of_register (i, frame));
+      default_print_one_register_info
+	(file, gdbarch_register_name (gdbarch, i),
+	 value_of_register (i, get_next_frame_sentinel_okay (frame)));
     }
 }
 
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index c65e2414bf8a..6d862dcec044 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -392,7 +392,8 @@ loongarch_software_single_step (struct regcache *regcache)
 static struct value *
 value_of_loongarch_user_reg (frame_info_ptr frame, const void *baton)
 {
-  return value_of_register ((long long) baton, frame);
+  return value_of_register ((long long) baton,
+			    get_next_frame_sentinel_okay (frame));
 }
 
 /* Implement the frame_align gdbarch method.  */
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 944c9116731f..dd5079f87c6c 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -1101,7 +1101,8 @@ output_register (frame_info_ptr frame, int regnum, int format,
 		 int skip_unavailable)
 {
   struct ui_out *uiout = current_uiout;
-  struct value *val = value_of_register (regnum, frame);
+  value *val
+    = value_of_register (regnum, get_next_frame_sentinel_okay (frame));
   struct value_print_options opts;
 
   if (skip_unavailable && !val->entirely_available ())
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 6bd0a66a6284..ac90aa1f2c5f 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -8074,7 +8074,7 @@ static struct value *
 value_of_mips_user_reg (frame_info_ptr frame, const void *baton)
 {
   const int *reg_p = (const int *) baton;
-  return value_of_register (*reg_p, frame);
+  return value_of_register (*reg_p, get_next_frame_sentinel_okay (frame));
 }
 
 static struct gdbarch *
diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c
index a81b5fd1fae1..7618a81fc256 100644
--- a/gdb/nds32-tdep.c
+++ b/gdb/nds32-tdep.c
@@ -267,9 +267,10 @@ static const struct
 static struct value *
 value_of_nds32_reg (frame_info_ptr frame, const void *baton)
 {
-  return value_of_register ((int) (intptr_t) baton, frame);
+  return value_of_register ((int) (intptr_t) baton,
+			    get_next_frame_sentinel_okay (frame));
 }
-\f
+
 /* Implement the "frame_align" gdbarch method.  */
 
 static CORE_ADDR
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 1a55e514e396..e6e245520323 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -260,7 +260,8 @@ frapy_read_register (PyObject *self, PyObject *args, PyObject *kw)
 	return nullptr;
 
       gdb_assert (regnum >= 0);
-      struct value *val = value_of_register (regnum, frame);
+      value *val
+	= value_of_register (regnum, get_next_frame_sentinel_okay (frame));
 
       if (val == NULL)
 	PyErr_SetString (PyExc_ValueError, _("Can't read register."));
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index ee50c51b531d..f1162f22290f 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -467,8 +467,8 @@ pending_framepy_read_register (PyObject *self, PyObject *args, PyObject *kw)
 	 which maps to a real register.  In the past,
 	 get_frame_register_value() was used here, which did not
 	 handle the user register case.  */
-      struct value *val = value_of_register (regnum,
-					     pending_frame->frame_info);
+      value *val = value_of_register
+        (regnum, get_next_frame_sentinel_okay (pending_frame->frame_info));
       if (val == NULL)
 	PyErr_Format (PyExc_ValueError,
 		      "Cannot read register %d from frame.",
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 3a2891c2c920..48f5a9e35e67 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -168,7 +168,7 @@ static struct value *
 value_of_riscv_user_reg (frame_info_ptr frame, const void *baton)
 {
   const int *reg_p = (const int *) baton;
-  return value_of_register (*reg_p, frame);
+  return value_of_register (*reg_p, get_next_frame_sentinel_okay (frame));
 }
 
 /* Information about a register alias that needs to be set up for this
@@ -1149,7 +1149,7 @@ riscv_print_one_register_info (struct gdbarch *gdbarch,
 
   try
     {
-      val = value_of_register (regnum, frame);
+      val = value_of_register (regnum, get_next_frame_sentinel_okay (frame));
       regtype = val->type ();
     }
   catch (const gdb_exception_error &ex)
diff --git a/gdb/s12z-tdep.c b/gdb/s12z-tdep.c
index 4781eab083eb..42fd1fb1d6ca 100644
--- a/gdb/s12z-tdep.c
+++ b/gdb/s12z-tdep.c
@@ -494,7 +494,7 @@ s12z_print_ccw_info (struct gdbarch *gdbarch,
 		     frame_info_ptr frame,
 		     int reg)
 {
-  struct value *v = value_of_register (reg, frame);
+  value *v = value_of_register (reg, get_next_frame_sentinel_okay (frame));
   const char *name = gdbarch_register_name (gdbarch, reg);
   uint32_t ccw = value_as_long (v);
   gdb_puts (name, file);
diff --git a/gdb/std-regs.c b/gdb/std-regs.c
index 54cf9018e426..221b15f1de9f 100644
--- a/gdb/std-regs.c
+++ b/gdb/std-regs.c
@@ -39,7 +39,7 @@ value_of_builtin_frame_fp_reg (frame_info_ptr frame, const void *baton)
        register can do so by adding "fp" to register name table (mind
        you, doing this is probably a dangerous thing).  */
     return value_of_register (gdbarch_deprecated_fp_regnum (gdbarch),
-			      frame);
+			      get_next_frame_sentinel_okay (frame));
   else
     {
       struct type *data_ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
@@ -58,7 +58,8 @@ value_of_builtin_frame_pc_reg (frame_info_ptr frame, const void *baton)
   struct gdbarch *gdbarch = get_frame_arch (frame);
 
   if (gdbarch_pc_regnum (gdbarch) >= 0)
-    return value_of_register (gdbarch_pc_regnum (gdbarch), frame);
+    return value_of_register (gdbarch_pc_regnum (gdbarch),
+			      get_next_frame_sentinel_okay (frame));
   else
     {
       struct type *func_ptr_type = builtin_type (gdbarch)->builtin_func_ptr;
@@ -77,7 +78,8 @@ value_of_builtin_frame_sp_reg (frame_info_ptr frame, const void *baton)
   struct gdbarch *gdbarch = get_frame_arch (frame);
 
   if (gdbarch_sp_regnum (gdbarch) >= 0)
-    return value_of_register (gdbarch_sp_regnum (gdbarch), frame);
+    return value_of_register (gdbarch_sp_regnum (gdbarch),
+			      get_next_frame_sentinel_okay (frame));
   error (_("Standard register ``$sp'' is not available for this target"));
 }
 
@@ -87,7 +89,8 @@ value_of_builtin_frame_ps_reg (frame_info_ptr frame, const void *baton)
   struct gdbarch *gdbarch = get_frame_arch (frame);
 
   if (gdbarch_ps_regnum (gdbarch) >= 0)
-    return value_of_register (gdbarch_ps_regnum (gdbarch), frame);
+    return value_of_register (gdbarch_ps_regnum (gdbarch),
+			      get_next_frame_sentinel_okay (frame));
   error (_("Standard register ``$ps'' is not available for this target"));
 }
 
diff --git a/gdb/value.h b/gdb/value.h
index e4912717684b..37017c9dd9f7 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1123,9 +1123,14 @@ extern struct value *value_of_variable (struct symbol *var,
 extern struct value *address_of_variable (struct symbol *var,
 					  const struct block *b);
 
-extern struct value *value_of_register (int regnum, frame_info_ptr frame);
+/* Return a value with the contents of register REGNUM as found in the frame
+   previous to NEXT_FRAME.  */
 
-struct value *value_of_register_lazy (frame_info_ptr frame, int regnum);
+extern value *value_of_register (int regnum, frame_info_ptr next_frame);
+
+/* Same as the above, but the value is not fetched.  */
+
+extern value *value_of_register_lazy (frame_info_ptr next_frame, int regnum);
 
 /* Return the symbol's reading requirement.  */
 
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 09/24] gdb: remove frame_register
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (7 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 08/24] gdb: change value_of_register and value_of_register_lazy to take the next frame Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 10/24] gdb: make put_frame_register take the next frame Simon Marchi
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

I was going to change frame_register to take the "next frame", but I
realized that doing so would make it a useless wrapper around
frame_register_unwind.  So, just remove frame_register and replace uses
with frame_register_unwind.

Change-Id: I185868bc69f8e098124775d0550d069220a4678a
---
 gdb/frame.c | 39 ++++++++-------------------------------
 1 file changed, 8 insertions(+), 31 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index 84ef8461b835..5d8ecb52fe2f 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1213,31 +1213,6 @@ frame_register_unwind (frame_info_ptr next_frame, int regnum,
   release_value (value);
 }
 
-/* Get the value of the register that belongs to this FRAME.  This
-   function is a wrapper to the call sequence ``frame_register_unwind
-   (get_next_frame (FRAME))''.  As per frame_register_unwind(), if
-   VALUEP is NULL, the registers value is not fetched/computed.  */
-
-static void
-frame_register (frame_info_ptr frame, int regnum,
-		int *optimizedp, int *unavailablep, enum lval_type *lvalp,
-		CORE_ADDR *addrp, int *realnump, gdb_byte *bufferp)
-{
-  /* Require all but BUFFERP to be valid.  A NULL BUFFERP indicates
-     that the value proper does not need to be fetched.  */
-  gdb_assert (optimizedp != NULL);
-  gdb_assert (lvalp != NULL);
-  gdb_assert (addrp != NULL);
-  gdb_assert (realnump != NULL);
-  /* gdb_assert (bufferp != NULL); */
-
-  /* Obtain the register value by unwinding the register from the next
-     (more inner frame).  */
-  gdb_assert (frame != NULL && frame->next != NULL);
-  frame_register_unwind (frame_info_ptr (frame->next), regnum, optimizedp,
-			 unavailablep, lvalp, addrp, realnump, bufferp);
-}
-
 void
 frame_unwind_register (frame_info_ptr next_frame, int regnum, gdb_byte *buf)
 {
@@ -1435,8 +1410,8 @@ put_frame_register (frame_info_ptr frame, int regnum,
 
   gdb_assert (buf.size () == size);
 
-  frame_register (frame, regnum, &optim, &unavail,
-		  &lval, &addr, &realnum, NULL);
+  frame_register_unwind (get_next_frame_sentinel_okay (frame), regnum, &optim,
+			 &unavail, &lval, &addr, &realnum, nullptr);
   if (optim)
     error (_("Attempt to assign to a register that was not saved."));
   switch (lval)
@@ -1472,8 +1447,9 @@ deprecated_frame_register_read (frame_info_ptr frame, int regnum,
   CORE_ADDR addr;
   int realnum;
 
-  frame_register (frame, regnum, &optimized, &unavailable,
-		  &lval, &addr, &realnum, myaddr);
+  frame_register_unwind (get_next_frame_sentinel_okay (frame), regnum,
+			 &optimized, &unavailable, &lval, &addr, &realnum,
+			 myaddr);
 
   return !optimized && !unavailable;
 }
@@ -1522,8 +1498,9 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
 	  CORE_ADDR addr;
 	  int realnum;
 
-	  frame_register (frame, regnum, optimizedp, unavailablep, &lval,
-			  &addr, &realnum, buffer.data ());
+	  frame_register_unwind (get_next_frame_sentinel_okay (frame), regnum,
+				 optimizedp, unavailablep, &lval, &addr,
+				 &realnum, buffer.data ());
 	  if (*optimizedp || *unavailablep)
 	    return false;
 	}
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 10/24] gdb: make put_frame_register take the next frame
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (8 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 09/24] gdb: remove frame_register Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 11/24] gdb: make put_frame_register_bytes " Simon Marchi
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Similar to the previous patches, change put_frame_register to take the
"next frame" instead of "this frame".

Change-Id: I062fd4663b8f54f0fc7bbf39c860b7341363821b
---
 gdb/alpha-tdep.c  |  3 ++-
 gdb/frame.c       | 16 +++++++++-------
 gdb/frame.h       |  8 ++++----
 gdb/i386-tdep.c   |  3 ++-
 gdb/i387-tdep.c   |  2 +-
 gdb/ia64-tdep.c   |  2 +-
 gdb/m68k-tdep.c   |  2 +-
 gdb/mips-tdep.c   |  5 +++--
 gdb/rs6000-tdep.c |  2 +-
 9 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c
index 1901b9449e6c..289d6f08782c 100644
--- a/gdb/alpha-tdep.c
+++ b/gdb/alpha-tdep.c
@@ -269,8 +269,9 @@ alpha_value_to_register (frame_info_ptr frame, int regnum,
 
   gdb_byte out[ALPHA_REGISTER_SIZE];
   alpha_lds (get_frame_arch (frame), out, in);
+
   auto out_view = gdb::make_array_view (out, reg_size);
-  put_frame_register (frame, regnum, out_view);
+  put_frame_register (get_next_frame_sentinel_okay (frame), regnum, out_view);
 }
 
 \f
diff --git a/gdb/frame.c b/gdb/frame.c
index 5d8ecb52fe2f..753dde189814 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1397,10 +1397,10 @@ read_frame_register_unsigned (frame_info_ptr frame, int regnum,
 }
 
 void
-put_frame_register (frame_info_ptr frame, int regnum,
-		    gdb::array_view<const gdb_byte> buf)
+put_frame_register (frame_info_ptr next_frame, int regnum,
+		     gdb::array_view<const gdb_byte> buf)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
+  gdbarch *gdbarch = frame_unwind_arch (next_frame);
   int realnum;
   int optim;
   int unavail;
@@ -1410,8 +1410,8 @@ put_frame_register (frame_info_ptr frame, int regnum,
 
   gdb_assert (buf.size () == size);
 
-  frame_register_unwind (get_next_frame_sentinel_okay (frame), regnum, &optim,
-			 &unavail, &lval, &addr, &realnum, nullptr);
+  frame_register_unwind (next_frame, regnum, &optim, &unavail, &lval, &addr,
+			 &realnum, nullptr);
   if (optim)
     error (_("Attempt to assign to a register that was not saved."));
   switch (lval)
@@ -1556,7 +1556,8 @@ put_frame_register_bytes (frame_info_ptr frame, int regnum,
 				    buffer.size ());
 
       if (curr_len == register_size (gdbarch, regnum))
-	put_frame_register (frame, regnum, buffer.slice (0, curr_len));
+	put_frame_register (get_next_frame_sentinel_okay (frame), regnum,
+			    buffer.slice (0, curr_len));
       else
 	{
 	  value *value
@@ -1566,7 +1567,8 @@ put_frame_register_bytes (frame_info_ptr frame, int regnum,
 
 	  copy (buffer.slice (0, curr_len),
 		value->contents_writeable ().slice (offset, curr_len));
-	  put_frame_register (frame, regnum, value->contents_raw ());
+	  put_frame_register (get_next_frame_sentinel_okay (frame), regnum,
+			      value->contents_raw ());
 	  release_value (value);
 	}
 
diff --git a/gdb/frame.h b/gdb/frame.h
index e3393cdd972c..7d3b8da0243d 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -720,10 +720,10 @@ extern ULONGEST get_frame_register_unsigned (frame_info_ptr frame,
 extern bool read_frame_register_unsigned (frame_info_ptr frame,
 					  int regnum, ULONGEST *val);
 
-/* The reverse.  Store a register value relative to the specified
-   frame.  Note: this call makes the frame's state undefined.  The
-   register and frame caches must be flushed.  */
-extern void put_frame_register (frame_info_ptr frame, int regnum,
+/* The reverse.  Store a register value relative to NEXT_FRAME's previous frame.
+   Note: this call makes the frame's state undefined.  The register and frame
+   caches must be flushed.  */
+extern void put_frame_register (frame_info_ptr next_frame, int regnum,
 				gdb::array_view<const gdb_byte> buf);
 
 /* Read LEN bytes from one or multiple registers starting with REGNUM
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 7f6ea5ba06ec..9666e1dba890 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -3918,7 +3918,8 @@ i386_value_to_register (frame_info_ptr frame, int regnum,
       gdb_assert (register_size (get_frame_arch (frame), regnum) == 4);
 
       auto from_view = gdb::make_array_view (from, 4);
-      put_frame_register (frame, regnum, from_view);
+      put_frame_register (get_next_frame_sentinel_okay (frame), regnum,
+			  from_view);
       regnum = i386_next_regnum (regnum);
       len -= 4;
       from += 4;
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index 74481f5aeb3c..cfa2a1e297ff 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -400,7 +400,7 @@ i387_value_to_register (frame_info_ptr frame, int regnum,
   struct type *to_type = i387_ext_type (gdbarch);
   target_float_convert (from, type, to, to_type);
   auto to_view = gdb::make_array_view (to, to_type->length ());
-  put_frame_register (frame, regnum, to_view);
+  put_frame_register (get_next_frame_sentinel_okay (frame), regnum, to_view);
 }
 \f
 
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index 113d384e8649..facb1bbac19c 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -1248,7 +1248,7 @@ ia64_value_to_register (frame_info_ptr frame, int regnum,
   type *to_type = ia64_ext_type (gdbarch);
   target_float_convert (in, valtype, out, to_type);
   auto out_view = gdb::make_array_view (out, to_type->length ());
-  put_frame_register (frame, regnum, out_view);
+  put_frame_register (get_next_frame_sentinel_okay (frame), regnum, out_view);
 }
 
 
diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c
index 5a80ecb94390..bee411f6e61f 100644
--- a/gdb/m68k-tdep.c
+++ b/gdb/m68k-tdep.c
@@ -250,7 +250,7 @@ m68k_value_to_register (frame_info_ptr frame, int regnum,
   /* Convert from TYPE.  */
   target_float_convert (from, type, to, fpreg_type);
   auto to_view = gdb::make_array_view (to, fpreg_type->length ());
-  put_frame_register (frame, regnum, to_view);
+  put_frame_register (get_next_frame_sentinel_okay (frame), regnum, to_view);
 }
 
 \f
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index ac90aa1f2c5f..941397b7ccdc 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -991,8 +991,9 @@ mips_value_to_register (frame_info_ptr frame, int regnum,
   if (mips_convert_register_float_case_p (gdbarch, regnum, type))
     {
       auto from_view = gdb::make_array_view (from, 8);
-      put_frame_register (frame, regnum, from_view.slice (4));
-      put_frame_register (frame, regnum + 1, from_view.slice (0, 4));
+      frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
+      put_frame_register (next_frame, regnum, from_view.slice (4));
+      put_frame_register (next_frame, regnum + 1, from_view.slice (0, 4));
     }
   else if (mips_convert_register_gpreg_case_p (gdbarch, regnum, type))
     {
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 9fb3f326bd07..a8626c32c2c1 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -2718,7 +2718,7 @@ rs6000_value_to_register (frame_info_ptr frame,
   struct type *to_type = builtin_type (gdbarch)->builtin_double;
   target_float_convert (from, type, to, to_type);
   auto to_view = gdb::make_array_view (to, to_type->length ());
-  put_frame_register (frame, regnum, to_view);
+  put_frame_register (get_next_frame_sentinel_okay (frame), regnum, to_view);
 }
 
  /* The type of a function that moves the value of REG between CACHE
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 11/24] gdb: make put_frame_register_bytes take the next frame
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (9 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 10/24] gdb: make put_frame_register take the next frame Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 12/24] gdb: make get_frame_register_bytes " Simon Marchi
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Similar to the previous patches, change put_frame_register_bytes to take
the "next frame" instead of "this frame".

Change-Id: I27bcb26573686d99b231230823cff8db6405a788
---
 gdb/dwarf2/expr.c |  6 +++---
 gdb/frame.c       | 14 +++++---------
 gdb/frame.h       |  4 ++--
 gdb/mips-tdep.c   | 11 ++++++-----
 gdb/valops.c      | 32 +++++++++++++++-----------------
 5 files changed, 31 insertions(+), 36 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index b21016eb38bb..7fc7b3abf5ca 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -272,9 +272,9 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
 		copy_bitwise (buffer.data (), bits_to_skip % 8,
 			      from_contents, offset,
 			      this_size_bits, bits_big_endian);
-		put_frame_register_bytes (frame, gdb_regnum,
-					  bits_to_skip / 8,
-					  buffer);
+		put_frame_register_bytes
+		  (get_next_frame_sentinel_okay (frame), gdb_regnum,
+		   bits_to_skip / 8, buffer);
 	      }
 	  }
 	  break;
diff --git a/gdb/frame.c b/gdb/frame.c
index 753dde189814..d4e2155fdb29 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1536,11 +1536,11 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
 }
 
 void
-put_frame_register_bytes (frame_info_ptr frame, int regnum,
+put_frame_register_bytes (frame_info_ptr next_frame, int regnum,
 			  CORE_ADDR offset,
 			  gdb::array_view<const gdb_byte> buffer)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
+  gdbarch *gdbarch = frame_unwind_arch (next_frame);
 
   /* Skip registers wholly inside of OFFSET.  */
   while (offset >= register_size (gdbarch, regnum))
@@ -1556,19 +1556,15 @@ put_frame_register_bytes (frame_info_ptr frame, int regnum,
 				    buffer.size ());
 
       if (curr_len == register_size (gdbarch, regnum))
-	put_frame_register (get_next_frame_sentinel_okay (frame), regnum,
-			    buffer.slice (0, curr_len));
+	put_frame_register (next_frame, regnum, buffer.slice (0, curr_len));
       else
 	{
-	  value *value
-	    = frame_unwind_register_value (frame_info_ptr (frame->next),
-					   regnum);
+	  value *value = frame_unwind_register_value (next_frame, regnum);
 	  gdb_assert (value != nullptr);
 
 	  copy (buffer.slice (0, curr_len),
 		value->contents_writeable ().slice (offset, curr_len));
-	  put_frame_register (get_next_frame_sentinel_okay (frame), regnum,
-			      value->contents_raw ());
+	  put_frame_register (next_frame, regnum, value->contents_raw ());
 	  release_value (value);
 	}
 
diff --git a/gdb/frame.h b/gdb/frame.h
index 7d3b8da0243d..a25f6de35f0d 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -736,8 +736,8 @@ extern bool get_frame_register_bytes (frame_info_ptr frame, int regnum,
 				      int *optimizedp, int *unavailablep);
 
 /* Write bytes from BUFFER to one or multiple registers starting with REGNUM
-   in frame FRAME, starting at OFFSET.  */
-extern void put_frame_register_bytes (frame_info_ptr frame, int regnum,
+   in NEXT_FRAME's previous frame, starting at OFFSET.  */
+extern void put_frame_register_bytes (frame_info_ptr next_frame, int regnum,
 				      CORE_ADDR offset,
 				      gdb::array_view<const gdb_byte> buffer);
 
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 941397b7ccdc..ce3eed3eb70d 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -999,7 +999,8 @@ mips_value_to_register (frame_info_ptr frame, int regnum,
     {
       gdb_byte fill[8];
       size_t len = type->length ();
-      
+      frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
+
       /* Sign extend values, irrespective of type, that are stored to 
 	 a 64-bit general purpose register.  (32-bit unsigned values
 	 are stored as signed quantities within a 64-bit register.
@@ -1012,8 +1013,8 @@ mips_value_to_register (frame_info_ptr frame, int regnum,
 	    store_signed_integer (fill, 8, BFD_ENDIAN_BIG, -1);
 	  else
 	    store_signed_integer (fill, 8, BFD_ENDIAN_BIG, 0);
-	  put_frame_register_bytes (frame, regnum, 0, {fill, 8 - len});
-	  put_frame_register_bytes (frame, regnum, 8 - len, {from, len});
+	  put_frame_register_bytes (next_frame, regnum, 0, {fill, 8 - len});
+	  put_frame_register_bytes (next_frame, regnum, 8 - len, {from, len});
 	}
       else
 	{
@@ -1021,8 +1022,8 @@ mips_value_to_register (frame_info_ptr frame, int regnum,
 	    store_signed_integer (fill, 8, BFD_ENDIAN_LITTLE, -1);
 	  else
 	    store_signed_integer (fill, 8, BFD_ENDIAN_LITTLE, 0);
-	  put_frame_register_bytes (frame, regnum, 0, {from, len});
-	  put_frame_register_bytes (frame, regnum, len, {fill, 8 - len});
+	  put_frame_register_bytes (next_frame, regnum, 0, {from, len});
+	  put_frame_register_bytes (next_frame, regnum, len, {fill, 8 - len});
 	}
     }
   else
diff --git a/gdb/valops.c b/gdb/valops.c
index 70851cd40b4f..2530c1d6412c 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1191,10 +1191,6 @@ value_assign (struct value *toval, struct value *fromval)
 
     case lval_register:
       {
-	frame_info_ptr frame;
-	struct gdbarch *gdbarch;
-	int value_reg;
-
 	/* Figure out which frame this register value is in.  The value
 	   holds the frame_id for the next frame, that is the frame this
 	   register value was unwound from.
@@ -1202,15 +1198,14 @@ value_assign (struct value *toval, struct value *fromval)
 	   Below we will call put_frame_register_bytes which requires that
 	   we pass it the actual frame in which the register value is
 	   valid, i.e. not the next frame.  */
-	frame = frame_find_by_id (VALUE_NEXT_FRAME_ID (toval));
-	frame = get_prev_frame_always (frame);
+	frame_info_ptr next_frame = frame_find_by_id (VALUE_NEXT_FRAME_ID (toval));
 
-	value_reg = VALUE_REGNUM (toval);
+	int value_reg = VALUE_REGNUM (toval);
 
-	if (!frame)
+	if (next_frame == nullptr)
 	  error (_("Value being assigned to is no longer active."));
 
-	gdbarch = get_frame_arch (frame);
+	gdbarch *gdbarch = frame_unwind_arch (next_frame);
 
 	if (toval->bitsize ())
 	  {
@@ -1230,9 +1225,10 @@ value_assign (struct value *toval, struct value *fromval)
 		       "don't fit in a %d bit word."),
 		     (int) sizeof (LONGEST) * HOST_CHAR_BIT);
 
-	    if (!get_frame_register_bytes (frame, value_reg, offset,
-					   {buffer, changed_len},
-					   &optim, &unavail))
+	    if (!get_frame_register_bytes (get_prev_frame_always (next_frame),
+					   value_reg, offset,
+					   { buffer, changed_len }, &optim,
+					   &unavail))
 	      {
 		if (optim)
 		  throw_error (OPTIMIZED_OUT_ERROR,
@@ -1245,8 +1241,8 @@ value_assign (struct value *toval, struct value *fromval)
 	    modify_field (type, buffer, value_as_long (fromval),
 			  toval->bitpos (), toval->bitsize ());
 
-	    put_frame_register_bytes (frame, value_reg, offset,
-				      {buffer, changed_len});
+	    put_frame_register_bytes (next_frame, value_reg, offset,
+				      { buffer, changed_len });
 	  }
 	else
 	  {
@@ -1256,17 +1252,19 @@ value_assign (struct value *toval, struct value *fromval)
 		/* If TOVAL is a special machine register requiring
 		   conversion of program values to a special raw
 		   format.  */
-		gdbarch_value_to_register (gdbarch, frame,
+		gdbarch_value_to_register (gdbarch,
+					   get_prev_frame_always (next_frame),
 					   VALUE_REGNUM (toval), type,
 					   fromval->contents ().data ());
 	      }
 	    else
-	      put_frame_register_bytes (frame, value_reg,
+	      put_frame_register_bytes (next_frame, value_reg,
 					toval->offset (),
 					fromval->contents ());
 	  }
 
-	gdb::observers::register_changed.notify (frame, value_reg);
+	gdb::observers::register_changed.notify
+	  (get_prev_frame_always (next_frame), value_reg);
 	break;
       }
 
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 12/24] gdb: make get_frame_register_bytes take the next frame
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (10 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 11/24] gdb: make put_frame_register_bytes " Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 13/24] gdb: add value::allocate_register Simon Marchi
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Similar to the previous patches, change get_frame_register_bytes to take
the "next frame" instead of "this frame".

Change-Id: Ie8f35042bfa6e93565fcefaee71b6b3903f0fe9f
---
 gdb/dwarf2/expr.c | 22 +++++++++++-----------
 gdb/frame.c       | 16 ++++++----------
 gdb/frame.h       | 10 +++++-----
 gdb/i386-tdep.c   |  8 ++++----
 gdb/i387-tdep.c   | 10 +++++-----
 gdb/ia64-tdep.c   |  9 ++++-----
 gdb/m68k-tdep.c   | 10 +++++-----
 gdb/mips-tdep.c   |  7 ++++---
 gdb/rs6000-tdep.c | 10 +++++-----
 gdb/valops.c      |  3 +--
 10 files changed, 50 insertions(+), 55 deletions(-)

diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 7fc7b3abf5ca..c71bbb594131 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -204,8 +204,9 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
 	{
 	case DWARF_VALUE_REGISTER:
 	  {
-	    frame_info_ptr frame = frame_find_by_id (c->frame_id);
-	    gdbarch *arch = get_frame_arch (frame);
+	    frame_info_ptr next_frame
+	      = get_next_frame_sentinel_okay (frame_find_by_id (c->frame_id));
+	    gdbarch *arch = frame_unwind_arch (next_frame);
 	    int gdb_regnum = dwarf_reg_to_regnum_or_error (arch, p->v.regno);
 	    ULONGEST reg_bits = 8 * register_size (arch, gdb_regnum);
 	    int optim, unavail;
@@ -225,9 +226,9 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
 	    if (from == nullptr)
 	      {
 		/* Read mode.  */
-		if (!get_frame_register_bytes (frame, gdb_regnum,
-					       bits_to_skip / 8,
-					       buffer, &optim, &unavail))
+		if (!get_frame_register_bytes (next_frame, gdb_regnum,
+					       bits_to_skip / 8, buffer,
+					       &optim, &unavail))
 		  {
 		    if (optim)
 		      {
@@ -254,9 +255,9 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
 		  {
 		    /* Data is copied non-byte-aligned into the register.
 		       Need some bits from original register value.  */
-		    get_frame_register_bytes (frame, gdb_regnum,
-					      bits_to_skip / 8,
-					      buffer, &optim, &unavail);
+		    get_frame_register_bytes (next_frame, gdb_regnum,
+					      bits_to_skip / 8, buffer, &optim,
+					      &unavail);
 		    if (optim)
 		      throw_error (OPTIMIZED_OUT_ERROR,
 				   _("Can't do read-modify-write to "
@@ -272,9 +273,8 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
 		copy_bitwise (buffer.data (), bits_to_skip % 8,
 			      from_contents, offset,
 			      this_size_bits, bits_big_endian);
-		put_frame_register_bytes
-		  (get_next_frame_sentinel_okay (frame), gdb_regnum,
-		   bits_to_skip / 8, buffer);
+		put_frame_register_bytes (next_frame, gdb_regnum,
+					  bits_to_skip / 8, buffer);
 	      }
 	  }
 	  break;
diff --git a/gdb/frame.c b/gdb/frame.c
index d4e2155fdb29..341bd26ee0df 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1455,12 +1455,11 @@ deprecated_frame_register_read (frame_info_ptr frame, int regnum,
 }
 
 bool
-get_frame_register_bytes (frame_info_ptr frame, int regnum,
-			  CORE_ADDR offset,
-			  gdb::array_view<gdb_byte> buffer,
+get_frame_register_bytes (frame_info_ptr next_frame, int regnum,
+			  CORE_ADDR offset, gdb::array_view<gdb_byte> buffer,
 			  int *optimizedp, int *unavailablep)
 {
-  struct gdbarch *gdbarch = get_frame_arch (frame);
+  gdbarch *gdbarch = frame_unwind_arch (next_frame);
 
   /* Skip registers wholly inside of OFFSET.  */
   while (offset >= register_size (gdbarch, regnum))
@@ -1498,17 +1497,14 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
 	  CORE_ADDR addr;
 	  int realnum;
 
-	  frame_register_unwind (get_next_frame_sentinel_okay (frame), regnum,
-				 optimizedp, unavailablep, &lval, &addr,
-				 &realnum, buffer.data ());
+	  frame_register_unwind (next_frame, regnum, optimizedp, unavailablep,
+				 &lval, &addr, &realnum, buffer.data ());
 	  if (*optimizedp || *unavailablep)
 	    return false;
 	}
       else
 	{
-	  struct value *value
-	    = frame_unwind_register_value (frame_info_ptr (frame->next),
-					   regnum);
+	  value *value = frame_unwind_register_value (next_frame, regnum);
 	  gdb_assert (value != NULL);
 	  *optimizedp = value->optimized_out ();
 	  *unavailablep = !value->entirely_available ();
diff --git a/gdb/frame.h b/gdb/frame.h
index a25f6de35f0d..3b26da817cd8 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -726,11 +726,11 @@ extern bool read_frame_register_unsigned (frame_info_ptr frame,
 extern void put_frame_register (frame_info_ptr next_frame, int regnum,
 				gdb::array_view<const gdb_byte> buf);
 
-/* Read LEN bytes from one or multiple registers starting with REGNUM
-   in frame FRAME, starting at OFFSET, into BUF.  If the register
-   contents are optimized out or unavailable, set *OPTIMIZEDP,
-   *UNAVAILABLEP accordingly.  */
-extern bool get_frame_register_bytes (frame_info_ptr frame, int regnum,
+/* Read LEN bytes from one or multiple registers starting with REGNUM in
+   NEXT_FRAME's previous frame, starting at OFFSET, into BUF.  If the register
+   contents are optimized out or unavailable, set *OPTIMIZEDP, *UNAVAILABLEP
+   accordingly.  */
+extern bool get_frame_register_bytes (frame_info_ptr next_frame, int regnum,
 				      CORE_ADDR offset,
 				      gdb::array_view<gdb_byte> buffer,
 				      int *optimizedp, int *unavailablep);
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 9666e1dba890..152529834eea 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -3877,10 +3877,10 @@ i386_register_to_value (frame_info_ptr frame, int regnum,
       gdb_assert (regnum != -1);
       gdb_assert (register_size (gdbarch, regnum) == 4);
 
-      if (!get_frame_register_bytes (frame, regnum, 0,
-				     gdb::make_array_view (to,
-							register_size (gdbarch,
-								       regnum)),
+      auto to_view
+	= gdb::make_array_view (to, register_size (gdbarch, regnum));
+      frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
+      if (!get_frame_register_bytes (next_frame, regnum, 0, to_view,
 				     optimizedp, unavailablep))
 	return 0;
 
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index cfa2a1e297ff..9085386172d5 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -364,11 +364,11 @@ i387_register_to_value (frame_info_ptr frame, int regnum,
     }
 
   /* Convert to TYPE.  */
-  if (!get_frame_register_bytes (frame, regnum, 0,
-				 gdb::make_array_view (from,
-						       register_size (gdbarch,
-								      regnum)),
-				 optimizedp, unavailablep))
+  auto from_view
+    = gdb::make_array_view (from, register_size (gdbarch, regnum));
+  frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
+  if (!get_frame_register_bytes (next_frame, regnum, 0, from_view, optimizedp,
+				 unavailablep))
     return 0;
 
   target_float_convert (from, i387_ext_type (gdbarch), to, type);
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index facb1bbac19c..d4c1736f0e7f 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -1227,11 +1227,10 @@ ia64_register_to_value (frame_info_ptr frame, int regnum,
   gdb_byte in[IA64_FP_REGISTER_SIZE];
 
   /* Convert to TYPE.  */
-  if (!get_frame_register_bytes (frame, regnum, 0,
-				 gdb::make_array_view (in,
-						       register_size (gdbarch,
-								      regnum)),
-				 optimizedp, unavailablep))
+  auto in_view = gdb::make_array_view (in, register_size (gdbarch, regnum));
+  frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
+  if (!get_frame_register_bytes (next_frame, regnum, 0, in_view, optimizedp,
+				 unavailablep))
     return 0;
 
   target_float_convert (in, ia64_ext_type (gdbarch), out, valtype);
diff --git a/gdb/m68k-tdep.c b/gdb/m68k-tdep.c
index bee411f6e61f..3cdf23682646 100644
--- a/gdb/m68k-tdep.c
+++ b/gdb/m68k-tdep.c
@@ -216,11 +216,11 @@ m68k_register_to_value (frame_info_ptr frame, int regnum,
   gdb_assert (type->code () == TYPE_CODE_FLT);
 
   /* Convert to TYPE.  */
-  if (!get_frame_register_bytes (frame, regnum, 0,
-				 gdb::make_array_view (from,
-						       register_size (gdbarch,
-								      regnum)),
-				 optimizedp, unavailablep))
+  auto from_view
+    = gdb::make_array_view (from, register_size (gdbarch, regnum));
+  frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
+  if (!get_frame_register_bytes (next_frame, regnum, 0, from_view, optimizedp,
+				 unavailablep))
     return 0;
 
   target_float_convert (from, fpreg_type, to, type);
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index ce3eed3eb70d..8e25c45dd297 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -947,17 +947,18 @@ mips_register_to_value (frame_info_ptr frame, int regnum,
 			int *optimizedp, int *unavailablep)
 {
   struct gdbarch *gdbarch = get_frame_arch (frame);
+  frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
 
   if (mips_convert_register_float_case_p (gdbarch, regnum, type))
     {
       get_frame_register (frame, regnum + 0, to + 4);
       get_frame_register (frame, regnum + 1, to + 0);
 
-      if (!get_frame_register_bytes (frame, regnum + 0, 0, {to + 4, 4},
+      if (!get_frame_register_bytes (next_frame, regnum + 0, 0, { to + 4, 4 },
 				     optimizedp, unavailablep))
 	return 0;
 
-      if (!get_frame_register_bytes (frame, regnum + 1, 0, {to + 0, 4},
+      if (!get_frame_register_bytes (next_frame, regnum + 1, 0, { to + 0, 4 },
 				     optimizedp, unavailablep))
 	return 0;
       *optimizedp = *unavailablep = 0;
@@ -969,7 +970,7 @@ mips_register_to_value (frame_info_ptr frame, int regnum,
       CORE_ADDR offset;
 
       offset = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG ? 8 - len : 0;
-      if (!get_frame_register_bytes (frame, regnum, offset, {to, len},
+      if (!get_frame_register_bytes (next_frame, regnum, offset, { to, len },
 				     optimizedp, unavailablep))
 	return 0;
 
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index a8626c32c2c1..819f07b47c0a 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -2691,11 +2691,11 @@ rs6000_register_to_value (frame_info_ptr frame,
   
   gdb_assert (type->code () == TYPE_CODE_FLT);
 
-  if (!get_frame_register_bytes (frame, regnum, 0,
-				 gdb::make_array_view (from,
-						       register_size (gdbarch,
-								      regnum)),
-				 optimizedp, unavailablep))
+  auto from_view
+    = gdb::make_array_view (from, register_size (gdbarch, regnum));
+  frame_info_ptr next_frame = get_next_frame_sentinel_okay (frame);
+  if (!get_frame_register_bytes (next_frame, regnum, 0, from_view, optimizedp,
+				 unavailablep))
     return 0;
 
   target_float_convert (from, builtin_type (gdbarch)->builtin_double,
diff --git a/gdb/valops.c b/gdb/valops.c
index 2530c1d6412c..35bf8be788f0 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1225,8 +1225,7 @@ value_assign (struct value *toval, struct value *fromval)
 		       "don't fit in a %d bit word."),
 		     (int) sizeof (LONGEST) * HOST_CHAR_BIT);
 
-	    if (!get_frame_register_bytes (get_prev_frame_always (next_frame),
-					   value_reg, offset,
+	    if (!get_frame_register_bytes (next_frame, value_reg, offset,
 					   { buffer, changed_len }, &optim,
 					   &unavail))
 	      {
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 13/24] gdb: add value::allocate_register
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (11 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 12/24] gdb: make get_frame_register_bytes " Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:00 ` [PATCH 14/24] gdb: read pseudo register through frame Simon Marchi
                   ` (13 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Add value::allocate_register, to facilitate allocating a value
representing a register in a given frame (or rather, in the given
frame's previous frame).  It will be used in a subsequent patch.  I
changed one relatively obvious spot that could use it, to at least
exercise the code path.

Change-Id: Icd4960f5e471a74b657bb3596c88d89679ef3772
---
 gdb/regcache.c |  7 ++-----
 gdb/value.c    | 15 +++++++++++++++
 gdb/value.h    |  7 +++++++
 3 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/gdb/regcache.c b/gdb/regcache.c
index 4f3881386f34..4256b82da19a 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -792,11 +792,8 @@ readable_regcache::cooked_read_value (int regnum)
       || (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
       || !gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
     {
-      struct value *result;
-
-      result = value::allocate (register_type (m_descr->gdbarch, regnum));
-      result->set_lval (lval_register);
-      VALUE_REGNUM (result) = regnum;
+      value *result = value::allocate_register
+	(get_next_frame_sentinel_okay (get_current_frame ()), regnum);
 
       /* It is more efficient in general to do this delegation in this
 	 direction than in the other one, even though the value-based
diff --git a/gdb/value.c b/gdb/value.c
index 17b7c53d0522..a1b18087416f 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -959,6 +959,21 @@ value::allocate (struct type *type)
   return allocate (type, true);
 }
 
+/* See value.h  */
+
+struct value *
+value::allocate_register (frame_info_ptr next_frame, int regnum)
+{
+  value *result
+    = value::allocate (register_type (frame_unwind_arch (next_frame), regnum));
+
+  result->set_lval (lval_register);
+  VALUE_REGNUM (result) = regnum;
+  VALUE_NEXT_FRAME_ID (result) = get_frame_id (next_frame);
+
+  return result;
+}
+
 /* Allocate a  value  that has the correct length
    for COUNT repetitions of type TYPE.  */
 
diff --git a/gdb/value.h b/gdb/value.h
index 37017c9dd9f7..511bde8bbd46 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -159,6 +159,13 @@ struct value
   /* Allocate a value and its contents for type TYPE.  */
   static struct value *allocate (struct type *type);
 
+  /* Allocate a non-lazy value representing register RENUM in the frame previous
+     to NEXT_FRAME.  The type of the value is found using `register_type`.
+
+     The caller is responsible for filling the value's contents.  */
+  static struct value *allocate_register (frame_info_ptr next_frame,
+					  int regnum);
+
   /* Create a computed lvalue, with type TYPE, function pointers
      FUNCS, and closure CLOSURE.  */
   static struct value *allocate_computed (struct type *type,
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 14/24] gdb: read pseudo register through frame
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (12 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 13/24] gdb: add value::allocate_register Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-11 20:11   ` John Baldwin
  2023-11-08  5:00 ` [PATCH 15/24] gdb: change parameter name in frame_unwind_register_unsigned declaration Simon Marchi
                   ` (12 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Change gdbarch_pseudo_register_read_value to take a frame instead of a
regcache.  The frame (and formerly the regcache) is used to read raw
registers needed to make up the pseudo register value.  The problem with
using the regcache is that it always provides raw register values for
the current frame (frame 0).

Let's say the user wants to read the ebx register on amd64.  ebx is a pseudo
register, obtained by reading the bottom half (bottom 4 bytes) of the
rbx register, which is a raw register.  If the currently selected frame
is frame 0, it works fine:

    (gdb) frame 0
    #0  break_here_asm () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:36
    36      in /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
    (gdb) p/x $ebx
    $1 = 0x24252627
    (gdb) p/x $rbx
    $2 = 0x2021222324252627

But if the user is looking at another frame, and the raw register behind
the pseudo register has been saved at some point in the call stack, then
we get a wrong answer:

    (gdb) frame 1
    #1  0x000055555555517d in caller () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:56
    56      in /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
    (gdb) p/x $ebx
    $3 = 0x24252627
    (gdb) p/x $rbx
    $4 = 0x1011121314151617

Here, the value of ebx was computed using the value of rbx in frame 0
(through the regcache), it should have been computed using the value of
rbx in frame 1.

In other to make this work properly, make the following changes:

 - Make dwarf2_frame_prev_register return nullptr if it doesn't know how
   to unwind a register and that register is a pseudo register.
   Previously, it returned `frame_unwind_got_register`, meaning, in our
   example, "the value of ebx in frame 1 is the same as the value of ebx
   in frame 0", which is obviously false.  Return nullptr as a way to
   say "I don't know".

 - In frame_unwind_register_value, when prev_register (for instance
   dwarf2_frame_prev_register) returns nullptr, and we are trying to
   read a pseudo register, try to get the register value through
   gdbarch_pseudo_register_read_value or gdbarch_pseudo_register_read.
   If using gdbarch_pseudo_register_read, the behavior is known to be
   broken.  Implementations should be migrated to use
   gdbarch_pseudo_register_read_value to fix that.

 - Change gdbarch_pseudo_register_read_value to take a frame_info
   instead of a regcache, update implementations (aarch64, amd64, i386).
   In i386-tdep.c, I made a copy of i386_mmx_regnum_to_fp_regnum that
   uses a frame instead of a regcache.  The version using the regcache
   is still used by i386_pseudo_register_write.  It will get removed in
   a subsequent patch.

 - Add some helpers in value.{c,h} to implement the common cases of
   pseudo registers: taking part of a raw register and concatenating
   multiple raw registers.

 - Update readable_regcache::{cooked_read,cooked_read_value} to pass the
   current frame to gdbarch_pseudo_register_read_value.  Passing the
   current frame will give the same behavior as before: for frame 0, raw
   registers will be read from the current thread's regcache.

Notes:

 - I do not plan on changing gdbarch_pseudo_register_read to receive a
   frame instead of a regcache. That method is considered deprecated.
   Instead, we should be working on migrating implementations to use
   gdbarch_pseudo_register_read_value instead.

 - In frame_unwind_register_value, we still ask the unwinder to try to
   unwind pseudo register values.  It's apparently possible for the
   debug info to provide information about [1] pseudo registers, so we
   want to try that first, before falling back to computing them
   ourselves.

[1] https://inbox.sourceware.org/gdb-patches/20180528174715.A954AD804AD@oc3748833570.ibm.com/

Change-Id: Id6ef1c64e19090a183dec050e4034d8c2394e7ca
---
 gdb/aarch64-tdep.c        | 148 +++++++++++--------------
 gdb/amd64-tdep.c          |  43 ++------
 gdb/dwarf2/frame.c        |   5 +-
 gdb/frame.c               |  36 +++++-
 gdb/gdbarch-gen.h         |   4 +-
 gdb/gdbarch.c             |   4 +-
 gdb/gdbarch_components.py |   2 +-
 gdb/i386-tdep.c           | 224 ++++++++++++++------------------------
 gdb/i386-tdep.h           |   7 +-
 gdb/regcache.c            |  38 +++++--
 gdb/value.c               |  70 ++++++++++++
 gdb/value.h               |  26 +++++
 12 files changed, 322 insertions(+), 285 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 50bfaa41eee2..341a6a19de5f 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3104,25 +3104,14 @@ aarch64_pseudo_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
 
 /* Helper for aarch64_pseudo_read_value.  */
 
-static struct value *
-aarch64_pseudo_read_value_1 (struct gdbarch *gdbarch,
-			     readable_regcache *regcache, int regnum_offset,
-			     int regsize, struct value *result_value)
+static value *
+aarch64_pseudo_read_value_1 (frame_info_ptr next_frame,
+			     const int pseudo_reg_num, int raw_regnum_offset)
 {
-  unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
-
-  /* Enough space for a full vector register.  */
-  gdb_byte reg_buf[register_size (gdbarch, AARCH64_V0_REGNUM)];
-  gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
+  unsigned v_regnum = AARCH64_V0_REGNUM + raw_regnum_offset;
 
-  if (regcache->raw_read (v_regnum, reg_buf) != REG_VALID)
-    result_value->mark_bytes_unavailable (0,
-					  result_value->type ()->length ());
-  else
-    memcpy (result_value->contents_raw ().data (), reg_buf, regsize);
-
-  return result_value;
- }
+  return pseudo_from_raw_part (next_frame, pseudo_reg_num, v_regnum, 0);
+}
 
 /* Helper function for reading/writing ZA pseudo-registers.  Given REGNUM,
    a ZA pseudo-register number, return, in OFFSETS, the information on positioning
@@ -3205,54 +3194,47 @@ aarch64_za_offsets_from_regnum (struct gdbarch *gdbarch, int regnum,
 
 /* Given REGNUM, a SME pseudo-register number, return its value in RESULT.  */
 
-static struct value *
-aarch64_sme_pseudo_register_read (struct gdbarch *gdbarch,
-				  readable_regcache *regcache, int regnum,
-				  struct value *result)
+static value *
+aarch64_sme_pseudo_register_read (gdbarch *gdbarch, frame_info_ptr next_frame,
+				  const int pseudo_reg_num)
 {
   aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
 
   gdb_assert (tdep->has_sme ());
   gdb_assert (tdep->sme_svq > 0);
-  gdb_assert (tdep->sme_pseudo_base <= regnum);
-  gdb_assert (regnum < tdep->sme_pseudo_base + tdep->sme_pseudo_count);
+  gdb_assert (tdep->sme_pseudo_base <= pseudo_reg_num);
+  gdb_assert (pseudo_reg_num < tdep->sme_pseudo_base + tdep->sme_pseudo_count);
 
   /* Fetch the offsets that we need in order to read from the correct blocks
      of ZA.  */
   struct za_offsets offsets;
-  aarch64_za_offsets_from_regnum (gdbarch, regnum, offsets);
+  aarch64_za_offsets_from_regnum (gdbarch, pseudo_reg_num, offsets);
 
   /* Fetch the contents of ZA.  */
-  size_t svl = sve_vl_from_vq (tdep->sme_svq);
-  gdb::byte_vector za (std::pow (svl, 2));
-  regcache->raw_read (tdep->sme_za_regnum, za.data ());
+  value *za_value = value_of_register (tdep->sme_za_regnum, next_frame);
+  value *result = value::allocate_register (next_frame, pseudo_reg_num);
 
   /* Copy the requested data.  */
   for (int chunks = 0; chunks < offsets.chunks; chunks++)
     {
-      const gdb_byte *source
-	= za.data () + offsets.starting_offset + chunks * offsets.stride_size;
-      gdb_byte *destination
-	= result->contents_raw ().data () + chunks * offsets.chunk_size;
-
-      memcpy (destination, source, offsets.chunk_size);
+      int src_offset = offsets.starting_offset + chunks * offsets.stride_size;
+      int dst_offset = chunks * offsets.chunk_size;
+      za_value->contents_copy (result, dst_offset, src_offset,
+			       offsets.chunk_size);
     }
+
   return result;
 }
 
 /* Implement the "pseudo_register_read_value" gdbarch method.  */
 
-static struct value *
-aarch64_pseudo_read_value (struct gdbarch *gdbarch, readable_regcache *regcache,
-			   int regnum)
+static value *
+aarch64_pseudo_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
+			   const int pseudo_reg_num)
 {
   aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
-  struct value *result_value = value::allocate (register_type (gdbarch, regnum));
 
-  result_value->set_lval (lval_register);
-  VALUE_REGNUM (result_value) = regnum;
-
-  if (is_w_pseudo_register (gdbarch, regnum))
+  if (is_w_pseudo_register (gdbarch, pseudo_reg_num))
     {
       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
       /* Default offset for little endian.  */
@@ -3262,53 +3244,49 @@ aarch64_pseudo_read_value (struct gdbarch *gdbarch, readable_regcache *regcache,
 	offset = 4;
 
       /* Find the correct X register to extract the data from.  */
-      int x_regnum = AARCH64_X0_REGNUM + (regnum - tdep->w_pseudo_base);
-      gdb_byte data[4];
+      int x_regnum
+	= AARCH64_X0_REGNUM + (pseudo_reg_num - tdep->w_pseudo_base);
 
       /* Read the bottom 4 bytes of X.  */
-      if (regcache->raw_read_part (x_regnum, offset, 4, data) != REG_VALID)
-	result_value->mark_bytes_unavailable (0, 4);
-      else
-	memcpy (result_value->contents_raw ().data (), data, 4);
-
-      return result_value;
+      return pseudo_from_raw_part (next_frame, pseudo_reg_num, x_regnum,
+				   offset);
     }
-  else if (is_sme_pseudo_register (gdbarch, regnum))
-    return aarch64_sme_pseudo_register_read (gdbarch, regcache, regnum,
-					     result_value);
-
-  regnum -= gdbarch_num_regs (gdbarch);
-
-  if (regnum >= AARCH64_Q0_REGNUM && regnum < AARCH64_Q0_REGNUM + 32)
-    return aarch64_pseudo_read_value_1 (gdbarch, regcache,
-					regnum - AARCH64_Q0_REGNUM,
-					Q_REGISTER_SIZE, result_value);
-
-  if (regnum >= AARCH64_D0_REGNUM && regnum < AARCH64_D0_REGNUM + 32)
-    return aarch64_pseudo_read_value_1 (gdbarch, regcache,
-					regnum - AARCH64_D0_REGNUM,
-					D_REGISTER_SIZE, result_value);
-
-  if (regnum >= AARCH64_S0_REGNUM && regnum < AARCH64_S0_REGNUM + 32)
-    return aarch64_pseudo_read_value_1 (gdbarch, regcache,
-					regnum - AARCH64_S0_REGNUM,
-					S_REGISTER_SIZE, result_value);
-
-  if (regnum >= AARCH64_H0_REGNUM && regnum < AARCH64_H0_REGNUM + 32)
-    return aarch64_pseudo_read_value_1 (gdbarch, regcache,
-					regnum - AARCH64_H0_REGNUM,
-					H_REGISTER_SIZE, result_value);
-
-  if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32)
-    return aarch64_pseudo_read_value_1 (gdbarch, regcache,
-					regnum - AARCH64_B0_REGNUM,
-					B_REGISTER_SIZE, result_value);
-
-  if (tdep->has_sve () && regnum >= AARCH64_SVE_V0_REGNUM
-      && regnum < AARCH64_SVE_V0_REGNUM + 32)
-    return aarch64_pseudo_read_value_1 (gdbarch, regcache,
-					regnum - AARCH64_SVE_V0_REGNUM,
-					V_REGISTER_SIZE, result_value);
+  else if (is_sme_pseudo_register (gdbarch, pseudo_reg_num))
+    return aarch64_sme_pseudo_register_read (gdbarch, next_frame,
+					     pseudo_reg_num);
+
+  /* Offset in the "pseudo-register space".  */
+  int pseudo_offset = pseudo_reg_num - gdbarch_num_regs (gdbarch);
+
+  if (pseudo_offset >= AARCH64_Q0_REGNUM
+      && pseudo_offset < AARCH64_Q0_REGNUM + 32)
+    return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
+					pseudo_offset - AARCH64_Q0_REGNUM);
+
+  if (pseudo_offset >= AARCH64_D0_REGNUM
+      && pseudo_offset < AARCH64_D0_REGNUM + 32)
+    return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
+					pseudo_offset - AARCH64_D0_REGNUM);
+
+  if (pseudo_offset >= AARCH64_S0_REGNUM
+      && pseudo_offset < AARCH64_S0_REGNUM + 32)
+    return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
+					pseudo_offset - AARCH64_S0_REGNUM);
+
+  if (pseudo_offset >= AARCH64_H0_REGNUM
+      && pseudo_offset < AARCH64_H0_REGNUM + 32)
+    return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
+					pseudo_offset - AARCH64_H0_REGNUM);
+
+  if (pseudo_offset >= AARCH64_B0_REGNUM
+      && pseudo_offset < AARCH64_B0_REGNUM + 32)
+    return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
+					pseudo_offset - AARCH64_B0_REGNUM);
+
+  if (tdep->has_sve () && pseudo_offset >= AARCH64_SVE_V0_REGNUM
+      && pseudo_offset < AARCH64_SVE_V0_REGNUM + 32)
+    return aarch64_pseudo_read_value_1 (next_frame, pseudo_reg_num,
+					pseudo_offset - AARCH64_SVE_V0_REGNUM);
 
   gdb_assert_not_reached ("regnum out of bound");
 }
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index e6feee677b3d..955a6860631d 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -348,18 +348,12 @@ amd64_pseudo_register_name (struct gdbarch *gdbarch, int regnum)
     return i386_pseudo_register_name (gdbarch, regnum);
 }
 
-static struct value *
-amd64_pseudo_register_read_value (struct gdbarch *gdbarch,
-				  readable_regcache *regcache,
+static value *
+amd64_pseudo_register_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
 				  int regnum)
 {
   i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
 
-  value *result_value = value::allocate (register_type (gdbarch, regnum));
-  result_value->set_lval (lval_register);
-  VALUE_REGNUM (result_value) = regnum;
-  gdb_byte *buf = result_value->contents_raw ().data ();
-
   if (i386_byte_regnum_p (gdbarch, regnum))
     {
       int gpnum = regnum - tdep->al_regnum;
@@ -368,44 +362,21 @@ amd64_pseudo_register_read_value (struct gdbarch *gdbarch,
       if (gpnum >= AMD64_NUM_LOWER_BYTE_REGS)
 	{
 	  gpnum -= AMD64_NUM_LOWER_BYTE_REGS;
-	  gdb_byte raw_buf[register_size (gdbarch, gpnum)];
 
 	  /* Special handling for AH, BH, CH, DH.  */
-	  register_status status = regcache->raw_read (gpnum, raw_buf);
-	  if (status == REG_VALID)
-	    memcpy (buf, raw_buf + 1, 1);
-	  else
-	    result_value->mark_bytes_unavailable (0,
-						  result_value->type ()->length ());
+	  return pseudo_from_raw_part (next_frame, regnum, gpnum, 1);
 	}
       else
-	{
-	  gdb_byte raw_buf[register_size (gdbarch, gpnum)];
-	  register_status status = regcache->raw_read (gpnum, raw_buf);
-	  if (status == REG_VALID)
-	    memcpy (buf, raw_buf, 1);
-	  else
-	    result_value->mark_bytes_unavailable (0,
-						  result_value->type ()->length ());
-	}
+	return pseudo_from_raw_part (next_frame, regnum, gpnum, 0);
     }
   else if (i386_dword_regnum_p (gdbarch, regnum))
     {
       int gpnum = regnum - tdep->eax_regnum;
-      gdb_byte raw_buf[register_size (gdbarch, gpnum)];
-      /* Extract (always little endian).  */
-      register_status status = regcache->raw_read (gpnum, raw_buf);
-      if (status == REG_VALID)
-	memcpy (buf, raw_buf, 4);
-      else
-	result_value->mark_bytes_unavailable (0,
-					      result_value->type ()->length ());
+
+      return pseudo_from_raw_part (next_frame, regnum, gpnum, 0);
     }
   else
-    i386_pseudo_register_read_into_value (gdbarch, regcache, regnum,
-					  result_value);
-
-  return result_value;
+    return i386_pseudo_register_read_value (gdbarch, next_frame, regnum);
 }
 
 static void
diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c
index abc8d6134824..7f8ed2bc9eec 100644
--- a/gdb/dwarf2/frame.c
+++ b/gdb/dwarf2/frame.c
@@ -1220,7 +1220,10 @@ dwarf2_frame_prev_register (frame_info_ptr this_frame, void **this_cache,
 	 "undefined").  Code above issues a complaint about this.
 	 Here just fudge the books, assume GCC, and that the value is
 	 more inner on the stack.  */
-      return frame_unwind_got_register (this_frame, regnum, regnum);
+      if (regnum < gdbarch_num_regs (gdbarch))
+	return frame_unwind_got_register (this_frame, regnum, regnum);
+      else
+	return nullptr;
 
     case DWARF2_FRAME_REG_SAME_VALUE:
       return frame_unwind_got_register (this_frame, regnum, regnum);
diff --git a/gdb/frame.c b/gdb/frame.c
index 341bd26ee0df..3035f87369ca 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1256,9 +1256,39 @@ frame_unwind_register_value (frame_info_ptr next_frame, int regnum)
     frame_unwind_find_by_frame (next_frame, &next_frame->prologue_cache);
 
   /* Ask this frame to unwind its register.  */
-  value *value = next_frame->unwind->prev_register (next_frame,
-						    &next_frame->prologue_cache,
-						    regnum);
+  value *value
+    = next_frame->unwind->prev_register (next_frame,
+					 &next_frame->prologue_cache, regnum);
+  if (value == nullptr)
+    {
+      if (gdbarch_pseudo_register_read_value_p (gdbarch))
+	{
+	  /* This is a pseudo register, we don't know how how what raw registers
+            this pseudo register is made of.  Ask the gdbarch to read the
+            value, it will itself ask the next frame to unwind the values of
+            the raw registers it needs to compose the value of the pseudo
+            register.  */
+	  value = gdbarch_pseudo_register_read_value
+	    (gdbarch, next_frame, regnum);
+	}
+      else if (gdbarch_pseudo_register_read_p (gdbarch))
+	{
+	  value = value::allocate_register (next_frame, regnum);
+
+	  /* Passing the current regcache is known to be broken, the pseudo
+	     register value will be constructed using the current raw registers,
+	     rather than reading them using NEXT_FRAME.  Architectures should be
+	     migrated to gdbarch_pseudo_register_read_value.  */
+	  register_status status = gdbarch_pseudo_register_read
+	    (gdbarch, get_current_regcache (), regnum,
+	     value->contents_writeable ().data ());
+	  if (status == REG_UNAVAILABLE)
+	    value->mark_bytes_unavailable (0, value->type ()->length ());
+	}
+      else
+	error (_("Can't unwind value of register %d (%s)"), regnum,
+	       user_reg_map_regnum_to_name (gdbarch, regnum));
+    }
 
   if (frame_debug)
     {
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 1d169c6e4f46..c2de274ed2a9 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -196,8 +196,8 @@ extern void set_gdbarch_pseudo_register_read (struct gdbarch *gdbarch, gdbarch_p
 
 extern bool gdbarch_pseudo_register_read_value_p (struct gdbarch *gdbarch);
 
-typedef struct value * (gdbarch_pseudo_register_read_value_ftype) (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
-extern struct value * gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+typedef struct value * (gdbarch_pseudo_register_read_value_ftype) (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum);
+extern struct value * gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum);
 extern void set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value);
 
 extern bool gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index ea6e4c647b10..06ff52576bdf 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -1886,13 +1886,13 @@ gdbarch_pseudo_register_read_value_p (struct gdbarch *gdbarch)
 }
 
 struct value *
-gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum)
+gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum)
 {
   gdb_assert (gdbarch != NULL);
   gdb_assert (gdbarch->pseudo_register_read_value != NULL);
   if (gdbarch_debug >= 2)
     gdb_printf (gdb_stdlog, "gdbarch_pseudo_register_read_value called\n");
-  return gdbarch->pseudo_register_read_value (gdbarch, regcache, cookednum);
+  return gdbarch->pseudo_register_read_value (gdbarch, next_frame, cookednum);
 }
 
 void
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 592d301ed353..86754af17cc3 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -414,7 +414,7 @@ never be called.
 """,
     type="struct value *",
     name="pseudo_register_read_value",
-    params=[("readable_regcache *", "regcache"), ("int", "cookednum")],
+    params=[("frame_info_ptr", "next_frame"), ("int", "cookednum")],
     predicate=True,
 )
 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 152529834eea..7f849bac6cd3 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -3411,195 +3411,131 @@ i386_mmx_regnum_to_fp_regnum (readable_regcache *regcache, int regnum)
   return (I387_ST0_REGNUM (tdep) + fpreg);
 }
 
+/* Map a cooked register onto a raw register or memory.  For the i386,
+   the MMX registers need to be mapped onto floating point registers.  */
+
+static int
+i386_mmx_regnum_to_fp_regnum (frame_info_ptr next_frame, int regnum)
+{
+  gdbarch *arch = frame_unwind_arch (next_frame);
+  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (arch);
+  ULONGEST fstat
+    = frame_unwind_register_unsigned (next_frame, I387_FSTAT_REGNUM (tdep));
+  int tos = (fstat >> 11) & 0x7;
+  int mmxreg = regnum - tdep->mm0_regnum;
+  int fpreg = (mmxreg + tos) % 8;
+
+  return (I387_ST0_REGNUM (tdep) + fpreg);
+}
+
 /* A helper function for us by i386_pseudo_register_read_value and
    amd64_pseudo_register_read_value.  It does all the work but reads
    the data into an already-allocated value.  */
 
-void
-i386_pseudo_register_read_into_value (struct gdbarch *gdbarch,
-				      readable_regcache *regcache,
-				      int regnum,
-				      struct value *result_value)
+value *
+i386_pseudo_register_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
+				 const int pseudo_reg_num)
 {
-  gdb_byte raw_buf[I386_MAX_REGISTER_SIZE];
-  enum register_status status;
-  gdb_byte *buf = result_value->contents_raw ().data ();
-
-  if (i386_mmx_regnum_p (gdbarch, regnum))
+  if (i386_mmx_regnum_p (gdbarch, pseudo_reg_num))
     {
-      int fpnum = i386_mmx_regnum_to_fp_regnum (regcache, regnum);
+      int fpnum = i386_mmx_regnum_to_fp_regnum (next_frame, pseudo_reg_num);
 
       /* Extract (always little endian).  */
-      status = regcache->raw_read (fpnum, raw_buf);
-      if (status != REG_VALID)
-	result_value->mark_bytes_unavailable (0,
-					      result_value->type ()->length ());
-      else
-	memcpy (buf, raw_buf, register_size (gdbarch, regnum));
+      return pseudo_from_raw_part (next_frame, pseudo_reg_num, fpnum, 0);
     }
   else
     {
       i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
-      if (i386_bnd_regnum_p (gdbarch, regnum))
+      if (i386_bnd_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  regnum -= tdep->bnd0_regnum;
+	  int i = pseudo_reg_num - tdep->bnd0_regnum;
 
 	  /* Extract (always little endian).  Read lower 128bits.  */
-	  status = regcache->raw_read (I387_BND0R_REGNUM (tdep) + regnum,
-				       raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (0, 16);
-	  else
+	  value *bndr_value
+	    = value_of_register (I387_BND0R_REGNUM (tdep) + i, next_frame);
+	  int size = builtin_type (gdbarch)->builtin_data_ptr->length ();
+	  value *result
+	    = value::allocate_register (next_frame, pseudo_reg_num);
+
+	  /* Copy the lower. */
+	  bndr_value->contents_copy (result, 0, 0, size);
+
+	  /* Copy the upper.  */
+	  bndr_value->contents_copy (result, size, 8, size);
+
+	  /* If upper bytes are available, compute ones' complement.  */
+	  if (result->bytes_available (size, size))
 	    {
 	      bfd_endian byte_order
-		= gdbarch_byte_order (current_inferior ()->arch ());
-	      LONGEST upper, lower;
-	      int size = builtin_type (gdbarch)->builtin_data_ptr->length ();
-
-	      lower = extract_unsigned_integer (raw_buf, 8, byte_order);
-	      upper = extract_unsigned_integer (raw_buf + 8, 8, byte_order);
+		= gdbarch_byte_order (frame_unwind_arch (next_frame));
+	      gdb::array_view<gdb_byte> upper_bytes
+		= result->contents_raw ().slice (size, size);
+	      ULONGEST upper
+		= extract_unsigned_integer (upper_bytes, byte_order);
 	      upper = ~upper;
-
-	      memcpy (buf, &lower, size);
-	      memcpy (buf + size, &upper, size);
+	      store_unsigned_integer (upper_bytes, byte_order, upper);
 	    }
+
+	  return result;
 	}
-      else if (i386_zmm_regnum_p (gdbarch, regnum))
+      else if (i386_zmm_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  regnum -= tdep->zmm0_regnum;
-
-	  if (regnum < num_lower_zmm_regs)
-	    {
-	      /* Extract (always little endian).  Read lower 128bits.  */
-	      status = regcache->raw_read (I387_XMM0_REGNUM (tdep) + regnum,
-					   raw_buf);
-	      if (status != REG_VALID)
-		result_value->mark_bytes_unavailable (0, 16);
-	      else
-		memcpy (buf, raw_buf, 16);
+	  /* Which register is it, relative to zmm0.  */
+	  int i_0 = pseudo_reg_num - tdep->zmm0_regnum;
 
-	      /* Extract (always little endian).  Read upper 128bits.  */
-	      status = regcache->raw_read (tdep->ymm0h_regnum + regnum,
-					   raw_buf);
-	      if (status != REG_VALID)
-		result_value->mark_bytes_unavailable (16, 16);
-	      else
-		memcpy (buf + 16, raw_buf, 16);
-	    }
+	  if (i_0 < num_lower_zmm_regs)
+	    return pseudo_from_concat_raw (next_frame, pseudo_reg_num,
+					   I387_XMM0_REGNUM (tdep) + i_0,
+					   tdep->ymm0h_regnum + i_0,
+					   tdep->zmm0h_regnum + i_0);
 	  else
 	    {
-	      /* Extract (always little endian).  Read lower 128bits.  */
-	      status = regcache->raw_read (I387_XMM16_REGNUM (tdep) + regnum
-					   - num_lower_zmm_regs,
-					   raw_buf);
-	      if (status != REG_VALID)
-		result_value->mark_bytes_unavailable (0, 16);
-	      else
-		memcpy (buf, raw_buf, 16);
-
-	      /* Extract (always little endian).  Read upper 128bits.  */
-	      status = regcache->raw_read (I387_YMM16H_REGNUM (tdep) + regnum
-					   - num_lower_zmm_regs,
-					   raw_buf);
-	      if (status != REG_VALID)
-		result_value->mark_bytes_unavailable (16, 16);
-	      else
-		memcpy (buf + 16, raw_buf, 16);
-	    }
+	      /* Which register is it, relative to zmm16.  */
+	      int i_16 = i_0 - num_lower_zmm_regs;
 
-	  /* Read upper 256bits.  */
-	  status = regcache->raw_read (tdep->zmm0h_regnum + regnum,
-				       raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (32, 32);
-	  else
-	    memcpy (buf + 32, raw_buf, 32);
+	      return pseudo_from_concat_raw (next_frame, pseudo_reg_num,
+					     I387_XMM16_REGNUM (tdep) + i_16,
+					     I387_YMM16H_REGNUM (tdep) + i_16,
+					     tdep->zmm0h_regnum + i_0);
+	    }
 	}
-      else if (i386_ymm_regnum_p (gdbarch, regnum))
+      else if (i386_ymm_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  regnum -= tdep->ymm0_regnum;
+	  int i = pseudo_reg_num - tdep->ymm0_regnum;
 
-	  /* Extract (always little endian).  Read lower 128bits.  */
-	  status = regcache->raw_read (I387_XMM0_REGNUM (tdep) + regnum,
-				       raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (0, 16);
-	  else
-	    memcpy (buf, raw_buf, 16);
-	  /* Read upper 128bits.  */
-	  status = regcache->raw_read (tdep->ymm0h_regnum + regnum,
-				       raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (16, 32);
-	  else
-	    memcpy (buf + 16, raw_buf, 16);
+	  return pseudo_from_concat_raw (next_frame, pseudo_reg_num,
+					 I387_XMM0_REGNUM (tdep) + i,
+					 tdep->ymm0h_regnum + i);
 	}
-      else if (i386_ymm_avx512_regnum_p (gdbarch, regnum))
+      else if (i386_ymm_avx512_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  regnum -= tdep->ymm16_regnum;
-	  /* Extract (always little endian).  Read lower 128bits.  */
-	  status = regcache->raw_read (I387_XMM16_REGNUM (tdep) + regnum,
-				       raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (0, 16);
-	  else
-	    memcpy (buf, raw_buf, 16);
-	  /* Read upper 128bits.  */
-	  status = regcache->raw_read (tdep->ymm16h_regnum + regnum,
-				       raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (16, 16);
-	  else
-	    memcpy (buf + 16, raw_buf, 16);
+	  int i = pseudo_reg_num - tdep->ymm16_regnum;
+
+	  return pseudo_from_concat_raw (next_frame, pseudo_reg_num,
+					 I387_XMM16_REGNUM (tdep) + i,
+					 tdep->ymm16h_regnum + i);
 	}
-      else if (i386_word_regnum_p (gdbarch, regnum))
+      else if (i386_word_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  int gpnum = regnum - tdep->ax_regnum;
+	  int gpnum = pseudo_reg_num - tdep->ax_regnum;
 
 	  /* Extract (always little endian).  */
-	  status = regcache->raw_read (gpnum, raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (0,
-						  result_value->type ()->length ());
-	  else
-	    memcpy (buf, raw_buf, 2);
+	  return pseudo_from_raw_part (next_frame, pseudo_reg_num, gpnum, 0);
 	}
-      else if (i386_byte_regnum_p (gdbarch, regnum))
+      else if (i386_byte_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  int gpnum = regnum - tdep->al_regnum;
+	  int gpnum = pseudo_reg_num - tdep->al_regnum;
 
 	  /* Extract (always little endian).  We read both lower and
 	     upper registers.  */
-	  status = regcache->raw_read (gpnum % 4, raw_buf);
-	  if (status != REG_VALID)
-	    result_value->mark_bytes_unavailable (0,
-						  result_value->type ()->length ());
-	  else if (gpnum >= 4)
-	    memcpy (buf, raw_buf + 1, 1);
-	  else
-	    memcpy (buf, raw_buf, 1);
+	  return pseudo_from_raw_part (next_frame, pseudo_reg_num, gpnum % 4,
+				       gpnum >= 4 ? 1 : 0);
 	}
       else
 	internal_error (_("invalid regnum"));
     }
 }
 
-static struct value *
-i386_pseudo_register_read_value (struct gdbarch *gdbarch,
-				 readable_regcache *regcache,
-				 int regnum)
-{
-  struct value *result;
-
-  result = value::allocate (register_type (gdbarch, regnum));
-  result->set_lval (lval_register);
-  VALUE_REGNUM (result) = regnum;
-
-  i386_pseudo_register_read_into_value (gdbarch, regcache, regnum, result);
-
-  return result;
-}
-
 void
 i386_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
 			    int regnum, const gdb_byte *buf)
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 4283a52c9292..970dc8904f2a 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -376,10 +376,9 @@ extern const char *i386_pseudo_register_name (struct gdbarch *gdbarch,
 extern struct type *i386_pseudo_register_type (struct gdbarch *gdbarch,
 					       int regnum);
 
-extern void i386_pseudo_register_read_into_value (struct gdbarch *gdbarch,
-						  readable_regcache *regcache,
-						  int regnum,
-						  struct value *result);
+extern value *i386_pseudo_register_read_value (gdbarch *gdbarch,
+					       frame_info_ptr next_frame,
+					       int regnum);
 
 extern void i386_pseudo_register_write (struct gdbarch *gdbarch,
 					struct regcache *regcache,
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 4256b82da19a..ff5f5b70433e 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -752,8 +752,9 @@ readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst,
     {
       register_status result = REG_VALID;
       scoped_value_mark mark;
-      value *computed
-	= gdbarch_pseudo_register_read_value (m_descr->gdbarch, this, regnum);
+      value *computed = gdbarch_pseudo_register_read_value
+	(m_descr->gdbarch, get_next_frame_sentinel_okay (get_current_frame ()),
+	 regnum);
 
       if (computed->entirely_available ())
 	copy (computed->contents_raw (), dst);
@@ -806,8 +807,9 @@ readable_regcache::cooked_read_value (int regnum)
       return result;
     }
   else
-    return gdbarch_pseudo_register_read_value (m_descr->gdbarch,
-					       this, regnum);
+    return gdbarch_pseudo_register_read_value
+      (m_descr->gdbarch, get_next_frame_sentinel_okay (get_current_frame ()),
+       regnum);
 }
 
 enum register_status
@@ -1958,10 +1960,18 @@ cooked_read_test (struct gdbarch *gdbarch)
 	break;
     }
 
-  readwrite_regcache readwrite (&mockctx.mock_inferior, gdbarch);
+  /* Install this regcache in the regcaches global structure, so that.  */
+  pid_ptid_regcache_map &x = regcaches[&mockctx.mock_target];
+  ptid_regcache_map &y = x[mockctx.mock_ptid.pid ()];
+  regcache &readwrite
+    = *y.emplace (std::make_pair (mockctx.mock_ptid,
+				  gdb::make_unique<readwrite_regcache> (
+				    &mockctx.mock_inferior, gdbarch)))
+	 ->second;
+
   readwrite.set_ptid (mockctx.mock_ptid);
-  gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum));
 
+  gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum));
   readwrite.raw_read (nonzero_regnum, buf, 1.0f);
 
   /* raw_read calls target_fetch_registers.  */
@@ -2060,6 +2070,8 @@ cooked_read_test (struct gdbarch *gdbarch)
 
       mockctx.mock_target.reset ();
     }
+
+  regcaches.erase (&mockctx.mock_target);
 }
 
 /* Test regcache::cooked_write by writing some expected contents to
@@ -2074,7 +2086,17 @@ cooked_write_test (struct gdbarch *gdbarch)
 
   /* Create a mock environment.  A process_stratum target pushed.  */
   scoped_mock_context<target_ops_no_register> ctx (gdbarch);
-  readwrite_regcache readwrite (&ctx.mock_inferior, gdbarch);
+
+
+  /* Install this regcache in the regcaches global structure, so that.  */
+  pid_ptid_regcache_map &x = regcaches[&ctx.mock_target];
+  ptid_regcache_map &y = x[ctx.mock_ptid.pid ()];
+  regcache &readwrite
+    = *y.emplace (std::make_pair (ctx.mock_ptid,
+				  gdb::make_unique<readwrite_regcache> (
+				    &ctx.mock_inferior, gdbarch)))
+	 ->second;
+
   readwrite.set_ptid (ctx.mock_ptid);
   const int num_regs = gdbarch_num_cooked_regs (gdbarch);
 
@@ -2155,6 +2177,8 @@ cooked_write_test (struct gdbarch *gdbarch)
       SELF_CHECK (readwrite.cooked_read (regnum, buf, 1.0f) == REG_VALID);
       SELF_CHECK (expected == buf);
     }
+
+  regcaches.erase (&ctx.mock_target);
 }
 
 /* Verify that when two threads with the same ptid exist (from two different
diff --git a/gdb/value.c b/gdb/value.c
index a1b18087416f..27a79dc62fc1 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -4041,6 +4041,76 @@ value::fetch_lazy ()
   set_lazy (false);
 }
 
+/* See value.h.  */
+
+value *
+pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
+		      int raw_reg_num, int raw_offset)
+{
+  value *pseudo_reg_val
+    = value::allocate_register (next_frame, pseudo_reg_num);
+  value *raw_reg_val = value_of_register (raw_reg_num, next_frame);
+  raw_reg_val->contents_copy (pseudo_reg_val, 0, raw_offset,
+			      pseudo_reg_val->type ()->length ());
+  return pseudo_reg_val;
+}
+
+/* See value.h.  */
+
+value *
+pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
+			int raw_reg_1_num, int raw_reg_2_num)
+{
+  value *pseudo_reg_val
+    = value::allocate_register (next_frame, pseudo_reg_num);
+  int dst_offset = 0;
+
+  value *raw_reg_1_val = value_of_register (raw_reg_1_num, next_frame);
+  raw_reg_1_val->contents_copy (pseudo_reg_val, dst_offset, 0,
+				raw_reg_1_val->type ()->length ());
+  dst_offset += raw_reg_1_val->type ()->length ();
+
+  value *raw_reg_2_val = value_of_register (raw_reg_2_num, next_frame);
+  raw_reg_2_val->contents_copy (pseudo_reg_val, dst_offset, 0,
+				raw_reg_2_val->type ()->length ());
+  dst_offset += raw_reg_2_val->type ()->length ();
+
+  gdb_assert (dst_offset == pseudo_reg_val->type ()->length ());
+
+  return pseudo_reg_val;
+}
+
+/* See value.h.  */
+
+value *
+pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
+			int raw_reg_1_num, int raw_reg_2_num,
+			int raw_reg_3_num)
+{
+  value *pseudo_reg_val
+    = value::allocate_register (next_frame, pseudo_reg_num);
+  int dst_offset = 0;
+
+  value *raw_reg_1_val = value_of_register (raw_reg_1_num, next_frame);
+  raw_reg_1_val->contents_copy (pseudo_reg_val, dst_offset, 0,
+				raw_reg_1_val->type ()->length ());
+  dst_offset += raw_reg_1_val->type ()->length ();
+
+  value *raw_reg_2_val = value_of_register (raw_reg_2_num, next_frame);
+  raw_reg_2_val->contents_copy (pseudo_reg_val, dst_offset, 0,
+				raw_reg_2_val->type ()->length ());
+  dst_offset += raw_reg_2_val->type ()->length ();
+
+  value *raw_reg_3_val = value_of_register (raw_reg_3_num, next_frame);
+  raw_reg_3_val->contents_copy (pseudo_reg_val, dst_offset, 0,
+				raw_reg_3_val->type ()->length ());
+  dst_offset += raw_reg_3_val->type ()->length ();
+
+  gdb_assert (dst_offset == pseudo_reg_val->type ()->length ());
+
+  return pseudo_reg_val;
+}
+
 /* Implementation of the convenience function $_isvoid.  */
 
 static struct value *
diff --git a/gdb/value.h b/gdb/value.h
index 511bde8bbd46..b5c097ad58bf 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1650,4 +1650,30 @@ struct scoped_array_length_limiting
   gdb::optional<int> m_old_value;
 };
 
+/* Helpers for building pseudo register values from raw registers.  */
+
+/* Create a value for pseudo register PSEUDO_REG_NUM by using bytes from
+   raw register RAW_REG_NUM starting at RAW_OFFSET.
+
+   The size of the pseudo register specifies how many bytes to use.  The
+   offset plus the size must not overflow the raw register's size.  */
+
+value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
+			     int raw_reg_num, int raw_offset);
+
+/* Create a value for pseudo register PSEUDO_REG_NUM by concatenating raw
+   registers RAW_REG_1_NUM and RAW_REG_2_NUM.
+
+   The sum of the sizes of raw registers must be equal to the size of the
+   pseudo register.  */
+
+value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
+			       int raw_reg_1_num, int raw_reg_2_num);
+
+/* Same as the above, but with three raw registers.  */
+
+value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
+			       int raw_reg_1_num, int raw_reg_2_num,
+			       int raw_reg_3_num);
+
 #endif /* !defined (VALUE_H) */
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 15/24] gdb: change parameter name in frame_unwind_register_unsigned declaration
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (13 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 14/24] gdb: read pseudo register through frame Simon Marchi
@ 2023-11-08  5:00 ` Simon Marchi
  2023-11-08  5:01 ` [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write Simon Marchi
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:00 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

For consistency with the declarations around.

Change-Id: I398266a61eae6e93fb7e306923009da9dd7f8fc4
---
 gdb/frame.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/frame.h b/gdb/frame.h
index 3b26da817cd8..a2921674782c 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -707,7 +707,7 @@ extern LONGEST frame_unwind_register_signed (frame_info_ptr next_frame,
 					     int regnum);
 extern LONGEST get_frame_register_signed (frame_info_ptr frame,
 					  int regnum);
-extern ULONGEST frame_unwind_register_unsigned (frame_info_ptr frame,
+extern ULONGEST frame_unwind_register_unsigned (frame_info_ptr next_frame,
 						int regnum);
 extern ULONGEST get_frame_register_unsigned (frame_info_ptr frame,
 					     int regnum);
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (14 preceding siblings ...)
  2023-11-08  5:00 ` [PATCH 15/24] gdb: change parameter name in frame_unwind_register_unsigned declaration Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-14 12:12   ` Andrew Burgess
  2023-11-08  5:01 ` [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame Simon Marchi
                   ` (10 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

The next patch introduces a new variant of gdbarch_pseudo_register_write
that takes a frame instead of a regcache for implementations to write
raw registers.  Rename to old one to make it clear it's deprecated.

Change-Id: If8872c89c6f8a1edfcab983eb064248fd5ff3115
---
 gdb/aarch64-tdep.c        |  2 +-
 gdb/amd64-tdep.c          |  4 ++--
 gdb/arm-tdep.c            |  2 +-
 gdb/avr-tdep.c            |  3 ++-
 gdb/bfin-tdep.c           |  3 ++-
 gdb/csky-tdep.c           |  4 ++--
 gdb/frv-tdep.c            |  3 ++-
 gdb/gdbarch-gen.h         | 15 +++++++++++----
 gdb/gdbarch.c             | 30 +++++++++++++++---------------
 gdb/gdbarch_components.py | 10 +++++++++-
 gdb/h8300-tdep.c          |  3 ++-
 gdb/i386-tdep.c           |  3 ++-
 gdb/ia64-tdep.c           |  3 ++-
 gdb/m32c-tdep.c           |  3 ++-
 gdb/m68hc11-tdep.c        |  3 ++-
 gdb/mep-tdep.c            |  3 ++-
 gdb/mips-tdep.c           |  3 ++-
 gdb/msp430-tdep.c         |  3 ++-
 gdb/nds32-tdep.c          |  3 ++-
 gdb/regcache.c            |  4 ++--
 gdb/riscv-tdep.c          |  9 +++++----
 gdb/rl78-tdep.c           |  3 ++-
 gdb/rs6000-tdep.c         |  4 ++--
 gdb/s390-tdep.c           |  3 ++-
 gdb/sh-tdep.c             |  9 ++++++---
 gdb/sparc-tdep.c          |  3 ++-
 gdb/sparc64-tdep.c        |  3 ++-
 gdb/xtensa-tdep.c         |  3 ++-
 28 files changed, 90 insertions(+), 54 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 341a6a19de5f..d1a6d46fb1f2 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -4481,7 +4481,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   set_gdbarch_num_pseudo_regs (gdbarch, num_pseudo_regs);
   set_gdbarch_pseudo_register_read_value (gdbarch, aarch64_pseudo_read_value);
-  set_gdbarch_pseudo_register_write (gdbarch, aarch64_pseudo_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch, aarch64_pseudo_write);
   set_tdesc_pseudo_register_name (gdbarch, aarch64_pseudo_register_name);
   set_tdesc_pseudo_register_type (gdbarch, aarch64_pseudo_register_type);
   set_tdesc_pseudo_register_reggroup_p (gdbarch,
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 955a6860631d..a0b4986d5b6c 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -3205,8 +3205,8 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
 
   set_gdbarch_pseudo_register_read_value (gdbarch,
 					  amd64_pseudo_register_read_value);
-  set_gdbarch_pseudo_register_write (gdbarch,
-				     amd64_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						amd64_pseudo_register_write);
   set_gdbarch_ax_pseudo_register_collect (gdbarch,
 					  amd64_ax_pseudo_register_collect);
 
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 5c8c4cd4e94b..cd4c760c2ff4 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -10906,7 +10906,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     {
       set_gdbarch_num_pseudo_regs (gdbarch, num_pseudos);
       set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_read);
-      set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_write);
+      set_gdbarch_deprecated_pseudo_register_write (gdbarch, arm_pseudo_write);
     }
 
   /* Add standard register aliases.  We add aliases even for those
diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c
index 92aec3b510f5..5690f6f65c74 100644
--- a/gdb/avr-tdep.c
+++ b/gdb/avr-tdep.c
@@ -1510,7 +1510,8 @@ avr_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   set_gdbarch_num_pseudo_regs (gdbarch, AVR_NUM_PSEUDO_REGS);
   set_gdbarch_pseudo_register_read (gdbarch, avr_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, avr_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						avr_pseudo_register_write);
 
   set_gdbarch_return_value (gdbarch, avr_return_value);
 
diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c
index 4b9067a2d0f7..0ff4355f573e 100644
--- a/gdb/bfin-tdep.c
+++ b/gdb/bfin-tdep.c
@@ -805,7 +805,8 @@ bfin_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   set_gdbarch_num_regs (gdbarch, BFIN_NUM_REGS);
   set_gdbarch_pseudo_register_read (gdbarch, bfin_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, bfin_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						bfin_pseudo_register_write);
   set_gdbarch_num_pseudo_regs (gdbarch, BFIN_NUM_PSEUDO_REGS);
   set_gdbarch_sp_regnum (gdbarch, BFIN_SP_REGNUM);
   set_gdbarch_pc_regnum (gdbarch, BFIN_PC_REGNUM);
diff --git a/gdb/csky-tdep.c b/gdb/csky-tdep.c
index e82b8dff546e..6cca72041a73 100644
--- a/gdb/csky-tdep.c
+++ b/gdb/csky-tdep.c
@@ -2872,8 +2872,8 @@ csky_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 				   tdep->fv_pseudo_registers_count);
       set_gdbarch_pseudo_register_read (gdbarch,
 					csky_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch,
-					 csky_pseudo_register_write);
+      set_gdbarch_deprecated_pseudo_register_write
+	(gdbarch, csky_pseudo_register_write);
       set_tdesc_pseudo_register_name (gdbarch, csky_pseudo_register_name);
     }
 
diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c
index 3656f6fcb663..e4c10dfb4347 100644
--- a/gdb/frv-tdep.c
+++ b/gdb/frv-tdep.c
@@ -1499,7 +1499,8 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_register_sim_regno (gdbarch, frv_register_sim_regno);
 
   set_gdbarch_pseudo_register_read (gdbarch, frv_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, frv_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						frv_pseudo_register_write);
 
   set_gdbarch_skip_prologue (gdbarch, frv_skip_prologue);
   set_gdbarch_skip_main_prologue (gdbarch, frv_skip_main_prologue);
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index c2de274ed2a9..3160aa8a9613 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -200,11 +200,18 @@ typedef struct value * (gdbarch_pseudo_register_read_value_ftype) (struct gdbarc
 extern struct value * gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum);
 extern void set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value);
 
-extern bool gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch);
+/* Write bytes to a pseudo register.
 
-typedef void (gdbarch_pseudo_register_write_ftype) (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
-extern void gdbarch_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
-extern void set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch, gdbarch_pseudo_register_write_ftype *pseudo_register_write);
+   This is marked as deprecated because it gets passed a regcache for
+   implementations to write raw registers in.  This doesn't work for unwound
+   frames, where the raw registers backing the pseudo registers may have been
+   saved elsewhere. */
+
+extern bool gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_deprecated_pseudo_register_write_ftype) (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
+extern void gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
+extern void set_gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch, gdbarch_deprecated_pseudo_register_write_ftype *deprecated_pseudo_register_write);
 
 extern int gdbarch_num_regs (struct gdbarch *gdbarch);
 extern void set_gdbarch_num_regs (struct gdbarch *gdbarch, int num_regs);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 06ff52576bdf..e198d339f6ba 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -74,7 +74,7 @@ struct gdbarch
   gdbarch_virtual_frame_pointer_ftype *virtual_frame_pointer = legacy_virtual_frame_pointer;
   gdbarch_pseudo_register_read_ftype *pseudo_register_read = nullptr;
   gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value = nullptr;
-  gdbarch_pseudo_register_write_ftype *pseudo_register_write = nullptr;
+  gdbarch_deprecated_pseudo_register_write_ftype *deprecated_pseudo_register_write = nullptr;
   int num_regs = -1;
   int num_pseudo_regs = 0;
   gdbarch_ax_pseudo_register_collect_ftype *ax_pseudo_register_collect = nullptr;
@@ -330,7 +330,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of virtual_frame_pointer, invalid_p == 0 */
   /* Skip verify of pseudo_register_read, has predicate.  */
   /* Skip verify of pseudo_register_read_value, has predicate.  */
-  /* Skip verify of pseudo_register_write, has predicate.  */
+  /* Skip verify of deprecated_pseudo_register_write, has predicate.  */
   if (gdbarch->num_regs == -1)
     log.puts ("\n\tnum_regs");
   /* Skip verify of num_pseudo_regs, invalid_p == 0 */
@@ -650,11 +650,11 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
 	      "gdbarch_dump: pseudo_register_read_value = <%s>\n",
 	      host_address_to_string (gdbarch->pseudo_register_read_value));
   gdb_printf (file,
-	      "gdbarch_dump: gdbarch_pseudo_register_write_p() = %d\n",
-	      gdbarch_pseudo_register_write_p (gdbarch));
+	      "gdbarch_dump: gdbarch_deprecated_pseudo_register_write_p() = %d\n",
+	      gdbarch_deprecated_pseudo_register_write_p (gdbarch));
   gdb_printf (file,
-	      "gdbarch_dump: pseudo_register_write = <%s>\n",
-	      host_address_to_string (gdbarch->pseudo_register_write));
+	      "gdbarch_dump: deprecated_pseudo_register_write = <%s>\n",
+	      host_address_to_string (gdbarch->deprecated_pseudo_register_write));
   gdb_printf (file,
 	      "gdbarch_dump: num_regs = %s\n",
 	      plongest (gdbarch->num_regs));
@@ -1903,27 +1903,27 @@ set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch,
 }
 
 bool
-gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch)
+gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch)
 {
   gdb_assert (gdbarch != NULL);
-  return gdbarch->pseudo_register_write != NULL;
+  return gdbarch->deprecated_pseudo_register_write != NULL;
 }
 
 void
-gdbarch_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf)
+gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf)
 {
   gdb_assert (gdbarch != NULL);
-  gdb_assert (gdbarch->pseudo_register_write != NULL);
+  gdb_assert (gdbarch->deprecated_pseudo_register_write != NULL);
   if (gdbarch_debug >= 2)
-    gdb_printf (gdb_stdlog, "gdbarch_pseudo_register_write called\n");
-  gdbarch->pseudo_register_write (gdbarch, regcache, cookednum, buf);
+    gdb_printf (gdb_stdlog, "gdbarch_deprecated_pseudo_register_write called\n");
+  gdbarch->deprecated_pseudo_register_write (gdbarch, regcache, cookednum, buf);
 }
 
 void
-set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch,
-				   gdbarch_pseudo_register_write_ftype pseudo_register_write)
+set_gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch,
+					      gdbarch_deprecated_pseudo_register_write_ftype deprecated_pseudo_register_write)
 {
-  gdbarch->pseudo_register_write = pseudo_register_write;
+  gdbarch->deprecated_pseudo_register_write = deprecated_pseudo_register_write;
 }
 
 int
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 86754af17cc3..1100da160550 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -419,8 +419,16 @@ never be called.
 )
 
 Method(
+    comment="""
+Write bytes to a pseudo register.
+
+This is marked as deprecated because it gets passed a regcache for
+implementations to write raw registers in.  This doesn't work for unwound
+frames, where the raw registers backing the pseudo registers may have been
+saved elsewhere.
+""",
     type="void",
-    name="pseudo_register_write",
+    name="deprecated_pseudo_register_write",
     params=[
         ("struct regcache *", "regcache"),
         ("int", "cookednum"),
diff --git a/gdb/h8300-tdep.c b/gdb/h8300-tdep.c
index d1494916f594..20f503c209bf 100644
--- a/gdb/h8300-tdep.c
+++ b/gdb/h8300-tdep.c
@@ -1318,7 +1318,8 @@ h8300_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     }
 
   set_gdbarch_pseudo_register_read (gdbarch, h8300_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, h8300_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						h8300_pseudo_register_write);
 
   /*
    * Basic register fields and methods.
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 7f849bac6cd3..5f12f17bc5eb 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8603,7 +8603,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   /* Pseudo registers may be changed by amd64_init_abi.  */
   set_gdbarch_pseudo_register_read_value (gdbarch,
 					  i386_pseudo_register_read_value);
-  set_gdbarch_pseudo_register_write (gdbarch, i386_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						i386_pseudo_register_write);
   set_gdbarch_ax_pseudo_register_collect (gdbarch,
 					  i386_ax_pseudo_register_collect);
 
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index d4c1736f0e7f..34b7fa09d5d3 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -3959,7 +3959,8 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_register_type (gdbarch, ia64_register_type);
 
   set_gdbarch_pseudo_register_read (gdbarch, ia64_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, ia64_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						ia64_pseudo_register_write);
   set_gdbarch_dwarf2_reg_to_regnum (gdbarch, ia64_dwarf_reg_to_regnum);
   set_gdbarch_register_reggroup_p (gdbarch, ia64_register_reggroup_p);
   set_gdbarch_convert_register_p (gdbarch, ia64_convert_register_p);
diff --git a/gdb/m32c-tdep.c b/gdb/m32c-tdep.c
index e06dbbe12bff..d053135e592d 100644
--- a/gdb/m32c-tdep.c
+++ b/gdb/m32c-tdep.c
@@ -977,7 +977,8 @@ make_regs (struct gdbarch *arch)
   set_gdbarch_register_name (arch, m32c_register_name);
   set_gdbarch_register_type (arch, m32c_register_type);
   set_gdbarch_pseudo_register_read (arch, m32c_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (arch, m32c_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (arch,
+						m32c_pseudo_register_write);
   set_gdbarch_register_sim_regno (arch, m32c_register_sim_regno);
   set_gdbarch_stab_reg_to_regnum (arch, m32c_debug_info_reg_to_regnum);
   set_gdbarch_dwarf2_reg_to_regnum (arch, m32c_debug_info_reg_to_regnum);
diff --git a/gdb/m68hc11-tdep.c b/gdb/m68hc11-tdep.c
index 1257c55ce667..668158f15a09 100644
--- a/gdb/m68hc11-tdep.c
+++ b/gdb/m68hc11-tdep.c
@@ -1491,7 +1491,8 @@ m68hc11_gdbarch_init (struct gdbarch_info info,
   set_gdbarch_register_name (gdbarch, m68hc11_register_name);
   set_gdbarch_register_type (gdbarch, m68hc11_register_type);
   set_gdbarch_pseudo_register_read (gdbarch, m68hc11_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, m68hc11_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						m68hc11_pseudo_register_write);
 
   set_gdbarch_push_dummy_call (gdbarch, m68hc11_push_dummy_call);
 
diff --git a/gdb/mep-tdep.c b/gdb/mep-tdep.c
index a84df4e1f53c..94d1ddf2a147 100644
--- a/gdb/mep-tdep.c
+++ b/gdb/mep-tdep.c
@@ -2424,7 +2424,8 @@ mep_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_register_type (gdbarch, mep_register_type);
   set_gdbarch_num_pseudo_regs (gdbarch, MEP_NUM_PSEUDO_REGS);
   set_gdbarch_pseudo_register_read (gdbarch, mep_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, mep_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						mep_pseudo_register_write);
   set_gdbarch_dwarf2_reg_to_regnum (gdbarch, mep_debug_reg_to_regnum);
   set_gdbarch_stab_reg_to_regnum (gdbarch, mep_debug_reg_to_regnum);
 
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index 8e25c45dd297..a4ba68748b9c 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -8518,7 +8518,8 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_long_double_bit (gdbarch, 64);
   set_gdbarch_register_reggroup_p (gdbarch, mips_register_reggroup_p);
   set_gdbarch_pseudo_register_read (gdbarch, mips_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, mips_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						mips_pseudo_register_write);
 
   set_gdbarch_ax_pseudo_register_collect (gdbarch,
 					  mips_ax_pseudo_register_collect);
diff --git a/gdb/msp430-tdep.c b/gdb/msp430-tdep.c
index 97d9073efbbe..92404045497c 100644
--- a/gdb/msp430-tdep.c
+++ b/gdb/msp430-tdep.c
@@ -937,7 +937,8 @@ msp430_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_sp_regnum (gdbarch, MSP430_SP_REGNUM);
   set_gdbarch_register_reggroup_p (gdbarch, msp430_register_reggroup_p);
   set_gdbarch_pseudo_register_read (gdbarch, msp430_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, msp430_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						msp430_pseudo_register_write);
   set_gdbarch_dwarf2_reg_to_regnum (gdbarch, msp430_dwarf2_reg_to_regnum);
   set_gdbarch_register_sim_regno (gdbarch, msp430_register_sim_regno);
 
diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c
index 7618a81fc256..83685d06f3ee 100644
--- a/gdb/nds32-tdep.c
+++ b/gdb/nds32-tdep.c
@@ -2001,7 +2001,8 @@ nds32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   else if (use_pseudo_fsrs == 1)
     {
       set_gdbarch_pseudo_register_read (gdbarch, nds32_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch, nds32_pseudo_register_write);
+      set_gdbarch_deprecated_pseudo_register_write
+	(gdbarch, nds32_pseudo_register_write);
       set_tdesc_pseudo_register_name (gdbarch, nds32_pseudo_register_name);
       set_tdesc_pseudo_register_type (gdbarch, nds32_pseudo_register_type);
       set_gdbarch_num_pseudo_regs (gdbarch, num_fsr_map[fpu_freg]);
diff --git a/gdb/regcache.c b/gdb/regcache.c
index ff5f5b70433e..dadc949434ee 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -930,8 +930,8 @@ regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float)
   if (regnum < num_raw_registers ())
     raw_write (regnum, src, 1.0f);
   else
-    gdbarch_pseudo_register_write (m_descr->gdbarch, this, regnum,
-				   src.data ());
+    gdbarch_deprecated_pseudo_register_write (m_descr->gdbarch, this, regnum,
+					      src.data ());
 }
 
 /* See regcache.h.  */
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 48f5a9e35e67..c1b9c8d435e5 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -1002,9 +1002,9 @@ riscv_pseudo_register_read (struct gdbarch *gdbarch,
   return REG_UNKNOWN;
 }
 
-/* Implement gdbarch_pseudo_register_write.  Write the contents of BUF into
-   pseudo-register REGNUM in REGCACHE.  BUF is sized based on the type of
-   register REGNUM.  */
+/* Implement gdbarch_deprecated_pseudo_register_write.  Write the contents of
+   BUF into pseudo-register REGNUM in REGCACHE.  BUF is sized based on the type
+   of register REGNUM.  */
 
 static void
 riscv_pseudo_register_write (struct gdbarch *gdbarch,
@@ -4229,7 +4229,8 @@ riscv_gdbarch_init (struct gdbarch_info info,
   set_tdesc_pseudo_register_reggroup_p (gdbarch,
 					riscv_pseudo_register_reggroup_p);
   set_gdbarch_pseudo_register_read (gdbarch, riscv_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, riscv_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						riscv_pseudo_register_write);
 
   /* Finalise the target description registers.  */
   tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data),
diff --git a/gdb/rl78-tdep.c b/gdb/rl78-tdep.c
index 2f40fbddaa1e..19298063186f 100644
--- a/gdb/rl78-tdep.c
+++ b/gdb/rl78-tdep.c
@@ -1436,7 +1436,8 @@ rl78_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_pc_regnum (gdbarch, RL78_PC_REGNUM);
   set_gdbarch_sp_regnum (gdbarch, RL78_SP_REGNUM);
   set_gdbarch_pseudo_register_read (gdbarch, rl78_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, rl78_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						rl78_pseudo_register_write);
   set_gdbarch_dwarf2_reg_to_regnum (gdbarch, rl78_dwarf_reg_to_regnum);
   set_gdbarch_register_reggroup_p (gdbarch, rl78_register_reggroup_p);
   set_gdbarch_register_sim_regno (gdbarch, rl78_register_sim_regno);
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 819f07b47c0a..78016eb8fccd 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -8291,8 +8291,8 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       || have_vsx || have_htm_fpu || have_htm_vsx)
     {
       set_gdbarch_pseudo_register_read (gdbarch, rs6000_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch,
-					 rs6000_pseudo_register_write);
+      set_gdbarch_deprecated_pseudo_register_write
+	(gdbarch, rs6000_pseudo_register_write);
       set_gdbarch_ax_pseudo_register_collect (gdbarch,
 	      rs6000_ax_pseudo_register_collect);
     }
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 54b5c89e5e3c..52faae971ee6 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -7081,7 +7081,8 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   /* Pseudo registers.  */
   set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						s390_pseudo_register_write);
   set_tdesc_pseudo_register_name (gdbarch, s390_pseudo_register_name);
   set_tdesc_pseudo_register_type (gdbarch, s390_pseudo_register_type);
   set_tdesc_pseudo_register_reggroup_p (gdbarch,
diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c
index babf85eeb906..ae1ce1e44713 100644
--- a/gdb/sh-tdep.c
+++ b/gdb/sh-tdep.c
@@ -2286,7 +2286,8 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       set_gdbarch_fp0_regnum (gdbarch, 25);
       set_gdbarch_num_pseudo_regs (gdbarch, 9);
       set_gdbarch_pseudo_register_read (gdbarch, sh_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch, sh_pseudo_register_write);
+      set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						    sh_pseudo_register_write);
       set_gdbarch_return_value (gdbarch, sh_return_value_fpu);
       set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
       break;
@@ -2297,7 +2298,8 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
       set_gdbarch_num_pseudo_regs (gdbarch, 1);
       set_gdbarch_pseudo_register_read (gdbarch, sh_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch, sh_pseudo_register_write);
+      set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						    sh_pseudo_register_write);
       break;
 
     case bfd_mach_sh_dsp:
@@ -2337,7 +2339,8 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       set_gdbarch_fp0_regnum (gdbarch, 25);
       set_gdbarch_num_pseudo_regs (gdbarch, 13);
       set_gdbarch_pseudo_register_read (gdbarch, sh_pseudo_register_read);
-      set_gdbarch_pseudo_register_write (gdbarch, sh_pseudo_register_write);
+      set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						    sh_pseudo_register_write);
       set_gdbarch_return_value (gdbarch, sh_return_value_fpu);
       set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
       break;
diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c
index 78f240db04e7..3a053a99e351 100644
--- a/gdb/sparc-tdep.c
+++ b/gdb/sparc-tdep.c
@@ -1844,7 +1844,8 @@ sparc32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_tdesc_pseudo_register_name (gdbarch, sparc32_pseudo_register_name);
   set_tdesc_pseudo_register_type (gdbarch, sparc32_pseudo_register_type);
   set_gdbarch_pseudo_register_read (gdbarch, sparc32_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, sparc32_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						sparc32_pseudo_register_write);
 
   /* Register numbers of various important registers.  */
   set_gdbarch_sp_regnum (gdbarch, SPARC_SP_REGNUM); /* %sp */
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index abbbb73d1db9..61f486689f15 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -1825,7 +1825,8 @@ sparc64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_tdesc_pseudo_register_name (gdbarch, sparc64_pseudo_register_name);
   set_tdesc_pseudo_register_type (gdbarch, sparc64_pseudo_register_type);
   set_gdbarch_pseudo_register_read (gdbarch, sparc64_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, sparc64_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						sparc64_pseudo_register_write);
 
   /* Register numbers of various important registers.  */
   set_gdbarch_pc_regnum (gdbarch, SPARC64_PC_REGNUM); /* %pc */
diff --git a/gdb/xtensa-tdep.c b/gdb/xtensa-tdep.c
index c9935d152641..8f313f0feabd 100644
--- a/gdb/xtensa-tdep.c
+++ b/gdb/xtensa-tdep.c
@@ -3174,7 +3174,8 @@ xtensa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   /* Pseudo-Register read/write.  */
   set_gdbarch_pseudo_register_read (gdbarch, xtensa_pseudo_register_read);
-  set_gdbarch_pseudo_register_write (gdbarch, xtensa_pseudo_register_write);
+  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
+						xtensa_pseudo_register_write);
 
   /* Set target information.  */
   set_gdbarch_num_regs (gdbarch, tdep->num_regs);
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (15 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-14 12:20   ` Andrew Burgess
  2023-11-08  5:01 ` [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write Simon Marchi
                   ` (9 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Add a new variant of gdbarch_pseudo_register_write that takes a
frame_info in order to write raw registers.  Use this new method when
available:

 - in put_frame_register, when trying to write a pseudo register to a given frame
 - in regcache::cooked_write

No implementation is migrated to use this new method (that will come in
subsequent patches), so no behavior change is expected here.

The objective is to fix writing pseudo registers to non-current
frames.  See previous commit "gdb: read pseudo register through
frame" for a more detailed explanation.

Change-Id: Ie7fe364a15a4d86c2ecb09de2b4baa08c45555ac
---
 gdb/frame.c               |  9 +++++-
 gdb/gdbarch-gen.h         | 15 +++++++++-
 gdb/gdbarch.c             | 32 ++++++++++++++++++++
 gdb/gdbarch_components.py | 19 ++++++++++++
 gdb/regcache.c            |  4 +++
 gdb/value.c               | 63 +++++++++++++++++++++++++++++++++++++++
 gdb/value.h               | 22 ++++++++++++++
 7 files changed, 162 insertions(+), 2 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index 3035f87369ca..81836d6d5357 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1452,7 +1452,14 @@ put_frame_register (frame_info_ptr next_frame, int regnum,
 	break;
       }
     case lval_register:
-      get_current_regcache ()->cooked_write (realnum, buf, 1.0f);
+      /* Not sure if that's always true... but we have a problem if not.  */
+      gdb_assert (size == register_size (gdbarch, realnum));
+
+      if (realnum < gdbarch_num_regs (gdbarch)
+	  || !gdbarch_pseudo_register_write_p (gdbarch))
+	get_current_regcache ()->cooked_write (realnum, buf, 1.0f);
+      else
+	gdbarch_pseudo_register_write (gdbarch, next_frame, realnum, buf);
       break;
     default:
       error (_("Attempt to assign to an unmodifiable value."));
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 3160aa8a9613..9cf58490d0b6 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -200,12 +200,25 @@ typedef struct value * (gdbarch_pseudo_register_read_value_ftype) (struct gdbarc
 extern struct value * gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum);
 extern void set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value);
 
+/* Write bytes in BUF to pseudo register with number PSEUDO_REG_NUM.
+
+   Raw registers backing the pseudo register should be written to using
+   NEXT_FRAME. */
+
+extern bool gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_pseudo_register_write_ftype) (struct gdbarch *gdbarch, frame_info_ptr next_frame, int pseudo_reg_num, gdb::array_view<const gdb_byte> buf);
+extern void gdbarch_pseudo_register_write (struct gdbarch *gdbarch, frame_info_ptr next_frame, int pseudo_reg_num, gdb::array_view<const gdb_byte> buf);
+extern void set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch, gdbarch_pseudo_register_write_ftype *pseudo_register_write);
+
 /* Write bytes to a pseudo register.
 
    This is marked as deprecated because it gets passed a regcache for
    implementations to write raw registers in.  This doesn't work for unwound
    frames, where the raw registers backing the pseudo registers may have been
-   saved elsewhere. */
+   saved elsewhere.
+
+   Implementations should be migrated to implement pseudo_register_write instead. */
 
 extern bool gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch);
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index e198d339f6ba..d584305fefb2 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -74,6 +74,7 @@ struct gdbarch
   gdbarch_virtual_frame_pointer_ftype *virtual_frame_pointer = legacy_virtual_frame_pointer;
   gdbarch_pseudo_register_read_ftype *pseudo_register_read = nullptr;
   gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value = nullptr;
+  gdbarch_pseudo_register_write_ftype *pseudo_register_write = nullptr;
   gdbarch_deprecated_pseudo_register_write_ftype *deprecated_pseudo_register_write = nullptr;
   int num_regs = -1;
   int num_pseudo_regs = 0;
@@ -330,6 +331,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of virtual_frame_pointer, invalid_p == 0 */
   /* Skip verify of pseudo_register_read, has predicate.  */
   /* Skip verify of pseudo_register_read_value, has predicate.  */
+  /* Skip verify of pseudo_register_write, has predicate.  */
   /* Skip verify of deprecated_pseudo_register_write, has predicate.  */
   if (gdbarch->num_regs == -1)
     log.puts ("\n\tnum_regs");
@@ -649,6 +651,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: pseudo_register_read_value = <%s>\n",
 	      host_address_to_string (gdbarch->pseudo_register_read_value));
+  gdb_printf (file,
+	      "gdbarch_dump: gdbarch_pseudo_register_write_p() = %d\n",
+	      gdbarch_pseudo_register_write_p (gdbarch));
+  gdb_printf (file,
+	      "gdbarch_dump: pseudo_register_write = <%s>\n",
+	      host_address_to_string (gdbarch->pseudo_register_write));
   gdb_printf (file,
 	      "gdbarch_dump: gdbarch_deprecated_pseudo_register_write_p() = %d\n",
 	      gdbarch_deprecated_pseudo_register_write_p (gdbarch));
@@ -1902,6 +1910,30 @@ set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch,
   gdbarch->pseudo_register_read_value = pseudo_register_read_value;
 }
 
+bool
+gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->pseudo_register_write != NULL;
+}
+
+void
+gdbarch_pseudo_register_write (struct gdbarch *gdbarch, frame_info_ptr next_frame, int pseudo_reg_num, gdb::array_view<const gdb_byte> buf)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->pseudo_register_write != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_pseudo_register_write called\n");
+  gdbarch->pseudo_register_write (gdbarch, next_frame, pseudo_reg_num, buf);
+}
+
+void
+set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch,
+				   gdbarch_pseudo_register_write_ftype pseudo_register_write)
+{
+  gdbarch->pseudo_register_write = pseudo_register_write;
+}
+
 bool
 gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch)
 {
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 1100da160550..ce8169b19a57 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -418,6 +418,23 @@ never be called.
     predicate=True,
 )
 
+Method(
+    comment="""
+Write bytes in BUF to pseudo register with number PSEUDO_REG_NUM.
+
+Raw registers backing the pseudo register should be written to using
+NEXT_FRAME.
+""",
+    type="void",
+    name="pseudo_register_write",
+    params=[
+        ("frame_info_ptr", "next_frame"),
+        ("int", "pseudo_reg_num"),
+        ("gdb::array_view<const gdb_byte>", "buf"),
+    ],
+    predicate=True,
+)
+
 Method(
     comment="""
 Write bytes to a pseudo register.
@@ -426,6 +443,8 @@ This is marked as deprecated because it gets passed a regcache for
 implementations to write raw registers in.  This doesn't work for unwound
 frames, where the raw registers backing the pseudo registers may have been
 saved elsewhere.
+
+Implementations should be migrated to implement pseudo_register_write instead.
 """,
     type="void",
     name="deprecated_pseudo_register_write",
diff --git a/gdb/regcache.c b/gdb/regcache.c
index dadc949434ee..16b117e767be 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -929,6 +929,10 @@ regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float)
 
   if (regnum < num_raw_registers ())
     raw_write (regnum, src, 1.0f);
+  else if (gdbarch_pseudo_register_write_p (m_descr->gdbarch))
+    gdbarch_pseudo_register_write
+      (m_descr->gdbarch, get_next_frame_sentinel_okay (get_current_frame ()),
+       regnum, src);
   else
     gdbarch_deprecated_pseudo_register_write (m_descr->gdbarch, this, regnum,
 					      src.data ());
diff --git a/gdb/value.c b/gdb/value.c
index 27a79dc62fc1..51dca972a587 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -4057,6 +4057,22 @@ pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
 
 /* See value.h.  */
 
+void
+pseudo_to_raw_part (frame_info_ptr next_frame,
+		    gdb::array_view<const gdb_byte> pseudo_buf,
+		    int raw_reg_num, int raw_offset)
+{
+  int raw_reg_size = register_size (get_frame_arch (next_frame), raw_reg_num);
+
+  /* When overflowing a register, put_frame_register_bytes writes to the
+     subsequent registers.  We don't want that behavior here, so make sure
+     the write is wholly within register RAW_REG_NUM.  */
+  gdb_assert (raw_offset + pseudo_buf.size () <= raw_reg_size);
+  put_frame_register_bytes (next_frame, raw_reg_num, raw_offset, pseudo_buf);
+}
+
+/* See value.h.  */
+
 value *
 pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
 			int raw_reg_1_num, int raw_reg_2_num)
@@ -4080,6 +4096,27 @@ pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
   return pseudo_reg_val;
 }
 
+void
+pseudo_to_concat_raw (frame_info_ptr next_frame,
+		      gdb::array_view<const gdb_byte> pseudo_buf,
+		      int raw_reg_1_num, int raw_reg_2_num)
+{
+  int src_offset = 0;
+  gdbarch *arch = frame_unwind_arch (next_frame);
+
+  int raw_reg_1_size = register_size (arch, raw_reg_1_num);
+  put_frame_register_bytes (next_frame, raw_reg_1_num, 0,
+			    pseudo_buf.slice (src_offset, raw_reg_1_size));
+  src_offset += raw_reg_1_size;
+
+  int raw_reg_2_size = register_size (arch, raw_reg_2_num);
+  put_frame_register_bytes (next_frame, raw_reg_2_num, 0,
+			    pseudo_buf.slice (src_offset, raw_reg_2_size));
+  src_offset += raw_reg_2_size;
+
+  gdb_assert (src_offset == pseudo_buf.size ());
+}
+
 /* See value.h.  */
 
 value *
@@ -4111,6 +4148,32 @@ pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
   return pseudo_reg_val;
 }
 
+void
+pseudo_to_concat_raw (frame_info_ptr next_frame,
+		      gdb::array_view<const gdb_byte> pseudo_buf,
+		      int raw_reg_1_num, int raw_reg_2_num, int raw_reg_3_num)
+{
+  int src_offset = 0;
+  gdbarch *arch = frame_unwind_arch (next_frame);
+
+  int raw_reg_1_size = register_size (arch, raw_reg_1_num);
+  put_frame_register_bytes (next_frame, raw_reg_1_num, 0,
+			    pseudo_buf.slice (src_offset, raw_reg_1_size));
+  src_offset += raw_reg_1_size;
+
+  int raw_reg_2_size = register_size (arch, raw_reg_2_num);
+  put_frame_register_bytes (next_frame, raw_reg_2_num, 0,
+			    pseudo_buf.slice (src_offset, raw_reg_2_size));
+  src_offset += raw_reg_2_size;
+
+  int raw_reg_3_size = register_size (arch, raw_reg_3_num);
+  put_frame_register_bytes (next_frame, raw_reg_3_num, 0,
+			    pseudo_buf.slice (src_offset, raw_reg_3_size));
+  src_offset += raw_reg_3_size;
+
+  gdb_assert (src_offset == pseudo_buf.size ());
+}
+
 /* Implementation of the convenience function $_isvoid.  */
 
 static struct value *
diff --git a/gdb/value.h b/gdb/value.h
index b5c097ad58bf..6a74d4e2c2ee 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1661,6 +1661,13 @@ struct scoped_array_length_limiting
 value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
 			     int raw_reg_num, int raw_offset);
 
+/* Write PSEUDO_BUF, the contents of a pseudo register, to part of raw register
+   RAW_REG_NUM starting at RAW_OFFSET.  */
+
+void pseudo_to_raw_part (frame_info_ptr this_frame,
+			 gdb::array_view<const gdb_byte> pseudo_buf,
+			 int raw_reg_num, int raw_offset);
+
 /* Create a value for pseudo register PSEUDO_REG_NUM by concatenating raw
    registers RAW_REG_1_NUM and RAW_REG_2_NUM.
 
@@ -1670,10 +1677,25 @@ value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
 value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
 			       int raw_reg_1_num, int raw_reg_2_num);
 
+/* Write PSEUDO_BUF, the contents of a pseudo register, to the two raw registers
+   RAW_REG_1_NUM and RAW_REG_2_NUM.  */
+
+void pseudo_to_concat_raw (frame_info_ptr this_frame,
+			   gdb::array_view<const gdb_byte> pseudo_buf,
+			   int raw_reg_1_num, int raw_reg_2_num);
+
 /* Same as the above, but with three raw registers.  */
 
 value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
 			       int raw_reg_1_num, int raw_reg_2_num,
 			       int raw_reg_3_num);
 
+/* Write PSEUDO_BUF, the contents of a pseudo register, to the tthreewo raw
+   registers RAW_REG_1_NUM, RAW_REG_2_NUM and RAW_REG_3_NUM.  */
+
+void pseudo_to_concat_raw (frame_info_ptr this_frame,
+			   gdb::array_view<const gdb_byte> pseudo_buf,
+			   int raw_reg_1_num, int raw_reg_2_num,
+			   int raw_reg_3_num);
+
 #endif /* !defined (VALUE_H) */
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (16 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-11 20:16   ` John Baldwin
  2023-11-08  5:01 ` [PATCH 19/24] gdb: make aarch64_za_offsets_from_regnum return za_offsets Simon Marchi
                   ` (8 subsequent siblings)
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Make i386 and amd64 use the new gdbarch_pseudo_register_write.  This
fixes writing to pseudo registers in non-current frames for those
architectures.

Change-Id: I4977e8fe12d2cef116f8834c34cdf6fec618554f
---
 gdb/amd64-tdep.c |  39 +++----------
 gdb/i386-tdep.c  | 146 +++++++++++++++++------------------------------
 gdb/i386-tdep.h  |   6 +-
 gdb/value.c      |   3 +-
 gdb/value.h      |   6 +-
 5 files changed, 66 insertions(+), 134 deletions(-)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index a0b4986d5b6c..4b9dbbab66e8 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -380,9 +380,8 @@ amd64_pseudo_register_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
 }
 
 static void
-amd64_pseudo_register_write (struct gdbarch *gdbarch,
-			     struct regcache *regcache,
-			     int regnum, const gdb_byte *buf)
+amd64_pseudo_register_write (gdbarch *gdbarch, frame_info_ptr next_frame,
+			     int regnum, gdb::array_view<const gdb_byte> buf)
 {
   i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
 
@@ -393,41 +392,18 @@ amd64_pseudo_register_write (struct gdbarch *gdbarch,
       if (gpnum >= AMD64_NUM_LOWER_BYTE_REGS)
 	{
 	  gpnum -= AMD64_NUM_LOWER_BYTE_REGS;
-	  gdb_byte raw_buf[register_size (gdbarch, gpnum)];
-
-	  /* Read ... AH, BH, CH, DH.  */
-	  regcache->raw_read (gpnum, raw_buf);
-	  /* ... Modify ... (always little endian).  */
-	  memcpy (raw_buf + 1, buf, 1);
-	  /* ... Write.  */
-	  regcache->raw_write (gpnum, raw_buf);
+	  pseudo_to_raw_part (next_frame, buf, gpnum, 1);
 	}
       else
-	{
-	  gdb_byte raw_buf[register_size (gdbarch, gpnum)];
-
-	  /* Read ...  */
-	  regcache->raw_read (gpnum, raw_buf);
-	  /* ... Modify ... (always little endian).  */
-	  memcpy (raw_buf, buf, 1);
-	  /* ... Write.  */
-	  regcache->raw_write (gpnum, raw_buf);
-	}
+	pseudo_to_raw_part (next_frame, buf, gpnum, 0);
     }
   else if (i386_dword_regnum_p (gdbarch, regnum))
     {
       int gpnum = regnum - tdep->eax_regnum;
-      gdb_byte raw_buf[register_size (gdbarch, gpnum)];
-
-      /* Read ...  */
-      regcache->raw_read (gpnum, raw_buf);
-      /* ... Modify ... (always little endian).  */
-      memcpy (raw_buf, buf, 4);
-      /* ... Write.  */
-      regcache->raw_write (gpnum, raw_buf);
+      pseudo_to_raw_part (next_frame, buf, gpnum, 0);
     }
   else
-    i386_pseudo_register_write (gdbarch, regcache, regnum, buf);
+    i386_pseudo_register_write (gdbarch, next_frame, regnum, buf);
 }
 
 /* Implement the 'ax_pseudo_register_collect' gdbarch method.  */
@@ -3205,8 +3181,7 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
 
   set_gdbarch_pseudo_register_read_value (gdbarch,
 					  amd64_pseudo_register_read_value);
-  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
-						amd64_pseudo_register_write);
+  set_gdbarch_pseudo_register_write (gdbarch, amd64_pseudo_register_write);
   set_gdbarch_ax_pseudo_register_collect (gdbarch,
 					  amd64_ax_pseudo_register_collect);
 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 5f12f17bc5eb..6b4e16155fee 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -3391,26 +3391,6 @@ i386_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
   internal_error (_("invalid regnum"));
 }
 
-/* Map a cooked register onto a raw register or memory.  For the i386,
-   the MMX registers need to be mapped onto floating point registers.  */
-
-static int
-i386_mmx_regnum_to_fp_regnum (readable_regcache *regcache, int regnum)
-{
-  gdbarch *arch = regcache->arch ();
-  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (arch);
-  int mmxreg, fpreg;
-  ULONGEST fstat;
-  int tos;
-
-  mmxreg = regnum - tdep->mm0_regnum;
-  regcache->raw_read (I387_FSTAT_REGNUM (tdep), &fstat);
-  tos = (fstat >> 11) & 0x7;
-  fpreg = (mmxreg + tos) % 8;
-
-  return (I387_ST0_REGNUM (tdep) + fpreg);
-}
-
 /* Map a cooked register onto a raw register or memory.  For the i386,
    the MMX registers need to be mapped onto floating point registers.  */
 
@@ -3537,115 +3517,92 @@ i386_pseudo_register_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
 }
 
 void
-i386_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
-			    int regnum, const gdb_byte *buf)
+i386_pseudo_register_write (gdbarch *gdbarch, frame_info_ptr next_frame,
+			    const int pseudo_reg_num,
+			    gdb::array_view<const gdb_byte> buf)
 {
-  gdb_byte raw_buf[I386_MAX_REGISTER_SIZE];
-
-  if (i386_mmx_regnum_p (gdbarch, regnum))
+  if (i386_mmx_regnum_p (gdbarch, pseudo_reg_num))
     {
-      int fpnum = i386_mmx_regnum_to_fp_regnum (regcache, regnum);
+      int fpnum = i386_mmx_regnum_to_fp_regnum (next_frame, pseudo_reg_num);
 
-      /* Read ...  */
-      regcache->raw_read (fpnum, raw_buf);
-      /* ... Modify ... (always little endian).  */
-      memcpy (raw_buf, buf, register_size (gdbarch, regnum));
-      /* ... Write.  */
-      regcache->raw_write (fpnum, raw_buf);
+      pseudo_to_raw_part (next_frame, buf, fpnum, 0);
     }
   else
     {
       i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
 
-      if (i386_bnd_regnum_p (gdbarch, regnum))
+      if (i386_bnd_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  ULONGEST upper, lower;
 	  int size = builtin_type (gdbarch)->builtin_data_ptr->length ();
 	  bfd_endian byte_order
 	    = gdbarch_byte_order (current_inferior ()->arch ());
 
 	  /* New values from input value.  */
-	  regnum -= tdep->bnd0_regnum;
-	  lower = extract_unsigned_integer (buf, size, byte_order);
-	  upper = extract_unsigned_integer (buf + size, size, byte_order);
+	  int reg_index = pseudo_reg_num - tdep->bnd0_regnum;
+	  int raw_regnum = I387_BND0R_REGNUM (tdep) + reg_index;
 
-	  /* Fetching register buffer.  */
-	  regcache->raw_read (I387_BND0R_REGNUM (tdep) + regnum,
-			      raw_buf);
+	  value *bndr_value = value_of_register (raw_regnum, next_frame);
+	  gdb::array_view<gdb_byte> bndr_view
+	    = bndr_value->contents_writeable ();
 
-	  upper = ~upper;
+	  /* Copy lower bytes directly.  */
+	  copy (buf.slice (0, size), bndr_view.slice (0, size));
 
-	  /* Set register bits.  */
-	  memcpy (raw_buf, &lower, 8);
-	  memcpy (raw_buf + 8, &upper, 8);
+	  /* Convert and then copy upper bytes.  */
+	  ULONGEST upper
+	    = extract_unsigned_integer (buf.slice (size, size), byte_order);
+	  upper = ~upper;
+	  store_unsigned_integer (bndr_view.slice (8, size), byte_order,
+				  upper);
 
-	  regcache->raw_write (I387_BND0R_REGNUM (tdep) + regnum, raw_buf);
+	  put_frame_register (next_frame, raw_regnum, bndr_view);
 	}
-      else if (i386_zmm_regnum_p (gdbarch, regnum))
+      else if (i386_zmm_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  regnum -= tdep->zmm0_regnum;
+	  /* Which register is it, relative to zmm0.  */
+	  int reg_index_0 = pseudo_reg_num - tdep->zmm0_regnum;
 
-	  if (regnum < num_lower_zmm_regs)
-	    {
-	      /* Write lower 128bits.  */
-	      regcache->raw_write (I387_XMM0_REGNUM (tdep) + regnum, buf);
-	      /* Write upper 128bits.  */
-	      regcache->raw_write (I387_YMM0_REGNUM (tdep) + regnum, buf + 16);
-	    }
+	  if (reg_index_0 < num_lower_zmm_regs)
+	    pseudo_to_concat_raw (next_frame, buf,
+				  I387_XMM0_REGNUM (tdep) + reg_index_0,
+				  I387_YMM0_REGNUM (tdep) + reg_index_0,
+				  tdep->zmm0h_regnum + reg_index_0);
 	  else
 	    {
-	      /* Write lower 128bits.  */
-	      regcache->raw_write (I387_XMM16_REGNUM (tdep) + regnum
-				   - num_lower_zmm_regs, buf);
-	      /* Write upper 128bits.  */
-	      regcache->raw_write (I387_YMM16H_REGNUM (tdep) + regnum
-				   - num_lower_zmm_regs, buf + 16);
+	      /* Which register is it, relative to zmm16.  */
+	      int reg_index_16 = reg_index_0 - num_lower_zmm_regs;
+
+	      pseudo_to_concat_raw (next_frame, buf,
+				    I387_XMM16_REGNUM (tdep) + reg_index_16,
+				    I387_YMM16H_REGNUM (tdep) + reg_index_16,
+				    tdep->zmm0h_regnum + +reg_index_0);
 	    }
-	  /* Write upper 256bits.  */
-	  regcache->raw_write (tdep->zmm0h_regnum + regnum, buf + 32);
 	}
-      else if (i386_ymm_regnum_p (gdbarch, regnum))
+      else if (i386_ymm_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  regnum -= tdep->ymm0_regnum;
+	  int i = pseudo_reg_num - tdep->ymm0_regnum;
 
-	  /* ... Write lower 128bits.  */
-	  regcache->raw_write (I387_XMM0_REGNUM (tdep) + regnum, buf);
-	  /* ... Write upper 128bits.  */
-	  regcache->raw_write (tdep->ymm0h_regnum + regnum, buf + 16);
+	  pseudo_to_concat_raw (next_frame, buf, I387_XMM0_REGNUM (tdep) + i,
+				tdep->ymm0h_regnum + i);
 	}
-      else if (i386_ymm_avx512_regnum_p (gdbarch, regnum))
+      else if (i386_ymm_avx512_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  regnum -= tdep->ymm16_regnum;
+	  int i = pseudo_reg_num - tdep->ymm16_regnum;
 
-	  /* ... Write lower 128bits.  */
-	  regcache->raw_write (I387_XMM16_REGNUM (tdep) + regnum, buf);
-	  /* ... Write upper 128bits.  */
-	  regcache->raw_write (tdep->ymm16h_regnum + regnum, buf + 16);
+	  pseudo_to_concat_raw (next_frame, buf, I387_XMM16_REGNUM (tdep) + i,
+				tdep->ymm16h_regnum + i);
 	}
-      else if (i386_word_regnum_p (gdbarch, regnum))
+      else if (i386_word_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  int gpnum = regnum - tdep->ax_regnum;
+	  int gpnum = pseudo_reg_num - tdep->ax_regnum;
 
-	  /* Read ...  */
-	  regcache->raw_read (gpnum, raw_buf);
-	  /* ... Modify ... (always little endian).  */
-	  memcpy (raw_buf, buf, 2);
-	  /* ... Write.  */
-	  regcache->raw_write (gpnum, raw_buf);
+	  pseudo_to_raw_part (next_frame, buf, gpnum, 0);
 	}
-      else if (i386_byte_regnum_p (gdbarch, regnum))
+      else if (i386_byte_regnum_p (gdbarch, pseudo_reg_num))
 	{
-	  int gpnum = regnum - tdep->al_regnum;
+	  int gpnum = pseudo_reg_num - tdep->al_regnum;
 
-	  /* Read ...  We read both lower and upper registers.  */
-	  regcache->raw_read (gpnum % 4, raw_buf);
-	  /* ... Modify ... (always little endian).  */
-	  if (gpnum >= 4)
-	    memcpy (raw_buf + 1, buf, 1);
-	  else
-	    memcpy (raw_buf, buf, 1);
-	  /* ... Write.  */
-	  regcache->raw_write (gpnum % 4, raw_buf);
+	  pseudo_to_raw_part (next_frame, buf, gpnum % 4, gpnum >= 4 ? 1 : 0);
 	}
       else
 	internal_error (_("invalid regnum"));
@@ -8603,8 +8560,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   /* Pseudo registers may be changed by amd64_init_abi.  */
   set_gdbarch_pseudo_register_read_value (gdbarch,
 					  i386_pseudo_register_read_value);
-  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
-						i386_pseudo_register_write);
+  set_gdbarch_pseudo_register_write (gdbarch, i386_pseudo_register_write);
   set_gdbarch_ax_pseudo_register_collect (gdbarch,
 					  i386_ax_pseudo_register_collect);
 
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 970dc8904f2a..b132da23a104 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -380,9 +380,9 @@ extern value *i386_pseudo_register_read_value (gdbarch *gdbarch,
 					       frame_info_ptr next_frame,
 					       int regnum);
 
-extern void i386_pseudo_register_write (struct gdbarch *gdbarch,
-					struct regcache *regcache,
-					int regnum, const gdb_byte *buf);
+extern void i386_pseudo_register_write (gdbarch *gdbarch,
+					frame_info_ptr next_frame, int regnum,
+					gdb::array_view<const gdb_byte> buf);
 
 extern int i386_ax_pseudo_register_collect (struct gdbarch *gdbarch,
 					    struct agent_expr *ax,
diff --git a/gdb/value.c b/gdb/value.c
index 51dca972a587..5c18bb17a781 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -4062,7 +4062,8 @@ pseudo_to_raw_part (frame_info_ptr next_frame,
 		    gdb::array_view<const gdb_byte> pseudo_buf,
 		    int raw_reg_num, int raw_offset)
 {
-  int raw_reg_size = register_size (get_frame_arch (next_frame), raw_reg_num);
+  int raw_reg_size
+    = register_size (frame_unwind_arch (next_frame), raw_reg_num);
 
   /* When overflowing a register, put_frame_register_bytes writes to the
      subsequent registers.  We don't want that behavior here, so make sure
diff --git a/gdb/value.h b/gdb/value.h
index 6a74d4e2c2ee..3b3cb9c00982 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1664,7 +1664,7 @@ value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
 /* Write PSEUDO_BUF, the contents of a pseudo register, to part of raw register
    RAW_REG_NUM starting at RAW_OFFSET.  */
 
-void pseudo_to_raw_part (frame_info_ptr this_frame,
+void pseudo_to_raw_part (frame_info_ptr next_frame,
 			 gdb::array_view<const gdb_byte> pseudo_buf,
 			 int raw_reg_num, int raw_offset);
 
@@ -1680,7 +1680,7 @@ value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
 /* Write PSEUDO_BUF, the contents of a pseudo register, to the two raw registers
    RAW_REG_1_NUM and RAW_REG_2_NUM.  */
 
-void pseudo_to_concat_raw (frame_info_ptr this_frame,
+void pseudo_to_concat_raw (frame_info_ptr next_frame,
 			   gdb::array_view<const gdb_byte> pseudo_buf,
 			   int raw_reg_1_num, int raw_reg_2_num);
 
@@ -1693,7 +1693,7 @@ value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
 /* Write PSEUDO_BUF, the contents of a pseudo register, to the tthreewo raw
    registers RAW_REG_1_NUM, RAW_REG_2_NUM and RAW_REG_3_NUM.  */
 
-void pseudo_to_concat_raw (frame_info_ptr this_frame,
+void pseudo_to_concat_raw (frame_info_ptr next_frame,
 			   gdb::array_view<const gdb_byte> pseudo_buf,
 			   int raw_reg_1_num, int raw_reg_2_num,
 			   int raw_reg_3_num);
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 19/24] gdb: make aarch64_za_offsets_from_regnum return za_offsets
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (17 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-08  5:01 ` [PATCH 20/24] gdb: add missing raw register read in aarch64_sme_pseudo_register_write Simon Marchi
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

This is not necessary, but it seems more natural to me to make
aarch64_za_offsets_from_regnum return a za_offsets object, rather than
fill an instance passed by parameter.

Change-Id: I40a185f055727da887ce7774be193eef1f4b9147
---
 gdb/aarch64-tdep.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index d1a6d46fb1f2..e9f5bdd141c8 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3114,12 +3114,11 @@ aarch64_pseudo_read_value_1 (frame_info_ptr next_frame,
 }
 
 /* Helper function for reading/writing ZA pseudo-registers.  Given REGNUM,
-   a ZA pseudo-register number, return, in OFFSETS, the information on positioning
-   of the bytes that must be read from/written to.  */
+   a ZA pseudo-register number, return the information on positioning of the
+   bytes that must be read from/written to.  */
 
-static void
-aarch64_za_offsets_from_regnum (struct gdbarch *gdbarch, int regnum,
-				struct za_offsets &offsets)
+static za_offsets
+aarch64_za_offsets_from_regnum (struct gdbarch *gdbarch, int regnum)
 {
   aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
 
@@ -3135,6 +3134,7 @@ aarch64_za_offsets_from_regnum (struct gdbarch *gdbarch, int regnum,
 
   /* Fetch the streaming vector length.  */
   size_t svl = sve_vl_from_vq (tdep->sme_svq);
+  za_offsets offsets;
 
   if (is_sme_tile_slice_pseudo_register (gdbarch, regnum))
     {
@@ -3190,6 +3190,8 @@ aarch64_za_offsets_from_regnum (struct gdbarch *gdbarch, int regnum,
       /* The chunk size is always svl bytes.  */
       offsets.chunk_size = svl;
     }
+
+  return offsets;
 }
 
 /* Given REGNUM, a SME pseudo-register number, return its value in RESULT.  */
@@ -3207,8 +3209,8 @@ aarch64_sme_pseudo_register_read (gdbarch *gdbarch, frame_info_ptr next_frame,
 
   /* Fetch the offsets that we need in order to read from the correct blocks
      of ZA.  */
-  struct za_offsets offsets;
-  aarch64_za_offsets_from_regnum (gdbarch, pseudo_reg_num, offsets);
+  za_offsets offsets
+    = aarch64_za_offsets_from_regnum (gdbarch, pseudo_reg_num);
 
   /* Fetch the contents of ZA.  */
   value *za_value = value_of_register (tdep->sme_za_regnum, next_frame);
@@ -3330,8 +3332,7 @@ aarch64_sme_pseudo_register_write (struct gdbarch *gdbarch,
 
   /* Fetch the offsets that we need in order to write to the correct blocks
      of ZA.  */
-  struct za_offsets offsets;
-  aarch64_za_offsets_from_regnum (gdbarch, regnum, offsets);
+  za_offsets offsets = aarch64_za_offsets_from_regnum (gdbarch, regnum);
 
   /* Fetch the contents of ZA.  */
   size_t svl = sve_vl_from_vq (tdep->sme_svq);
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 20/24] gdb: add missing raw register read in aarch64_sme_pseudo_register_write
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (18 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 19/24] gdb: make aarch64_za_offsets_from_regnum return za_offsets Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-08  5:01 ` [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write Simon Marchi
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

It seems like the intention here is to read the contents of the ZA
register and only write part of it.  However, there's no actual read of
the ZA register, so it looks like we'll write uninitialized bytes to the
target, for the portion of the raw register where we don't write the
pseudo register.  Add a call to raw_read to fix this.

I don't know how to test this though.

Change-Id: I7548240bd4324f6a3b729a1ebf7502fae5a46e9e
---
 gdb/aarch64-tdep.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index e9f5bdd141c8..ff159c412ced 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3337,6 +3337,7 @@ aarch64_sme_pseudo_register_write (struct gdbarch *gdbarch,
   /* Fetch the contents of ZA.  */
   size_t svl = sve_vl_from_vq (tdep->sme_svq);
   gdb::byte_vector za (std::pow (svl, 2));
+  regcache->raw_read (tdep->sme_za_regnum, za, 1.0f);
 
   /* Copy the requested data.  */
   for (int chunks = 0; chunks < offsets.chunks; chunks++)
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (19 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 20/24] gdb: add missing raw register read in aarch64_sme_pseudo_register_write Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-08  5:01 ` [PATCH 22/24] gdb: migrate arm to gdbarch_pseudo_register_read_value Simon Marchi
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Make aarch64 use the new gdbarch_pseudo_register_write.  This fixes
writing pseudo registers to non-current frames on this architecture.

Change-Id: Ic012a0b95ae728d45a7121f77a79d604c23a849e
---
 gdb/aarch64-tdep.c | 132 ++++++++++++++++++++++++---------------------
 1 file changed, 71 insertions(+), 61 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index ff159c412ced..1815d78dec40 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3296,32 +3296,33 @@ aarch64_pseudo_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
 /* Helper for aarch64_pseudo_write.  */
 
 static void
-aarch64_pseudo_write_1 (struct gdbarch *gdbarch, struct regcache *regcache,
-			int regnum_offset, int regsize, const gdb_byte *buf)
+aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
+			int regnum_offset,
+			gdb::array_view<const gdb_byte> buf)
 {
   unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
-
-  /* Enough space for a full vector register.  */
-  gdb_byte reg_buf[register_size (gdbarch, AARCH64_V0_REGNUM)];
   gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
 
-  /* Ensure the register buffer is zero, we want gdb writes of the
+  /* Enough space for a full vector register.
+
+     Ensure the register buffer is zero-ed, we want gdb writes of the
      various 'scalar' pseudo registers to behavior like architectural
      writes, register width bytes are written the remainder are set to
      zero.  */
-  memset (reg_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));
-
-  memcpy (reg_buf, buf, regsize);
-  regcache->raw_write (v_regnum, reg_buf);
+  constexpr int raw_reg_size = 16;
+  gdb_byte raw_buf[raw_reg_size] {};
+  gdb::array_view<gdb_byte> raw_view (raw_buf);
+  copy (buf, raw_view.slice (0, buf.size ()));
+  put_frame_register (next_frame, v_regnum, raw_view);
 }
 
 /* Given REGNUM, a SME pseudo-register number, store the bytes from DATA to the
    pseudo-register.  */
 
 static void
-aarch64_sme_pseudo_register_write (struct gdbarch *gdbarch,
-				   struct regcache *regcache,
-				   int regnum, const gdb_byte *data)
+aarch64_sme_pseudo_register_write (gdbarch *gdbarch, frame_info_ptr next_frame,
+				   const int regnum,
+				   gdb::array_view<const gdb_byte> data)
 {
   aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
 
@@ -3335,33 +3336,39 @@ aarch64_sme_pseudo_register_write (struct gdbarch *gdbarch,
   za_offsets offsets = aarch64_za_offsets_from_regnum (gdbarch, regnum);
 
   /* Fetch the contents of ZA.  */
-  size_t svl = sve_vl_from_vq (tdep->sme_svq);
-  gdb::byte_vector za (std::pow (svl, 2));
-  regcache->raw_read (tdep->sme_za_regnum, za, 1.0f);
+  value *za_value = value_of_register (tdep->sme_za_regnum, next_frame);
 
-  /* Copy the requested data.  */
-  for (int chunks = 0; chunks < offsets.chunks; chunks++)
-    {
-      const gdb_byte *source = data + chunks * offsets.chunk_size;
-      gdb_byte *destination
-	= za.data () + offsets.starting_offset + chunks * offsets.stride_size;
+  {
+    /* Create a view only on the portion of za we want to write.  */
+    gdb::array_view<gdb_byte> za_view
+      = za_value->contents_writeable ().slice (offsets.starting_offset);
 
-      memcpy (destination, source, offsets.chunk_size);
-    }
+    /* Copy the requested data.  */
+    for (int chunks = 0; chunks < offsets.chunks; chunks++)
+      {
+	gdb::array_view<const gdb_byte> src
+	  = data.slice (chunks * offsets.chunk_size, offsets.chunk_size);
+	gdb::array_view<gdb_byte> dst
+	  = za_view.slice (chunks * offsets.stride_size, offsets.chunk_size);
+	copy (src, dst);
+      }
+  }
 
   /* Write back to ZA.  */
-  regcache->raw_write (tdep->sme_za_regnum, za.data ());
+  put_frame_register (next_frame, tdep->sme_za_regnum,
+		      za_value->contents_raw ());
 }
 
 /* Implement the "pseudo_register_write" gdbarch method.  */
 
 static void
-aarch64_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache,
-		      int regnum, const gdb_byte *buf)
+aarch64_pseudo_write (gdbarch *gdbarch, frame_info_ptr next_frame,
+		      const int pseudo_reg_num,
+		      gdb::array_view<const gdb_byte> buf)
 {
   aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
 
-  if (is_w_pseudo_register (gdbarch, regnum))
+  if (is_w_pseudo_register (gdbarch, pseudo_reg_num))
     {
       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
       /* Default offset for little endian.  */
@@ -3371,53 +3378,56 @@ aarch64_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache,
 	offset = 4;
 
       /* Find the correct X register to extract the data from.  */
-      int x_regnum = AARCH64_X0_REGNUM + (regnum - tdep->w_pseudo_base);
+      int x_regnum = AARCH64_X0_REGNUM + (pseudo_reg_num - tdep->w_pseudo_base);
 
       /* First zero-out the contents of X.  */
-      ULONGEST zero = 0;
-      regcache->raw_write (x_regnum, zero);
+      gdb_byte bytes[8] {};
+      gdb::array_view<gdb_byte> bytes_view (bytes);
+      copy (buf, bytes_view.slice (offset, 4));
+
       /* Write to the bottom 4 bytes of X.  */
-      regcache->raw_write_part (x_regnum, offset, 4, buf);
+      put_frame_register (next_frame, x_regnum, bytes_view);
       return;
     }
-  else if (is_sme_pseudo_register (gdbarch, regnum))
+  else if (is_sme_pseudo_register (gdbarch, pseudo_reg_num))
     {
-      aarch64_sme_pseudo_register_write (gdbarch, regcache, regnum, buf);
+      aarch64_sme_pseudo_register_write (gdbarch, next_frame, pseudo_reg_num,
+					 buf);
       return;
     }
 
-  regnum -= gdbarch_num_regs (gdbarch);
+  /* Offset in the "pseudo-register space".  */
+  int pseudo_offset = pseudo_reg_num - gdbarch_num_regs (gdbarch);
 
-  if (regnum >= AARCH64_Q0_REGNUM && regnum < AARCH64_Q0_REGNUM + 32)
-    return aarch64_pseudo_write_1 (gdbarch, regcache,
-				   regnum - AARCH64_Q0_REGNUM, Q_REGISTER_SIZE,
-				   buf);
+  if (pseudo_offset >= AARCH64_Q0_REGNUM
+      && pseudo_offset < AARCH64_Q0_REGNUM + 32)
+    return aarch64_pseudo_write_1 (gdbarch, next_frame,
+				   pseudo_offset - AARCH64_Q0_REGNUM, buf);
 
-  if (regnum >= AARCH64_D0_REGNUM && regnum < AARCH64_D0_REGNUM + 32)
-    return aarch64_pseudo_write_1 (gdbarch, regcache,
-				   regnum - AARCH64_D0_REGNUM, D_REGISTER_SIZE,
-				   buf);
+  if (pseudo_offset >= AARCH64_D0_REGNUM
+      && pseudo_offset < AARCH64_D0_REGNUM + 32)
+    return aarch64_pseudo_write_1 (gdbarch, next_frame,
+				   pseudo_offset - AARCH64_D0_REGNUM, buf);
 
-  if (regnum >= AARCH64_S0_REGNUM && regnum < AARCH64_S0_REGNUM + 32)
-    return aarch64_pseudo_write_1 (gdbarch, regcache,
-				   regnum - AARCH64_S0_REGNUM, S_REGISTER_SIZE,
-				   buf);
+  if (pseudo_offset >= AARCH64_S0_REGNUM
+      && pseudo_offset < AARCH64_S0_REGNUM + 32)
+    return aarch64_pseudo_write_1 (gdbarch, next_frame,
+				   pseudo_offset - AARCH64_S0_REGNUM, buf);
 
-  if (regnum >= AARCH64_H0_REGNUM && regnum < AARCH64_H0_REGNUM + 32)
-    return aarch64_pseudo_write_1 (gdbarch, regcache,
-				   regnum - AARCH64_H0_REGNUM, H_REGISTER_SIZE,
-				   buf);
+  if (pseudo_offset >= AARCH64_H0_REGNUM
+      && pseudo_offset < AARCH64_H0_REGNUM + 32)
+    return aarch64_pseudo_write_1 (gdbarch, next_frame,
+				   pseudo_offset - AARCH64_H0_REGNUM, buf);
 
-  if (regnum >= AARCH64_B0_REGNUM && regnum < AARCH64_B0_REGNUM + 32)
-    return aarch64_pseudo_write_1 (gdbarch, regcache,
-				   regnum - AARCH64_B0_REGNUM, B_REGISTER_SIZE,
-				   buf);
+  if (pseudo_offset >= AARCH64_B0_REGNUM
+      && pseudo_offset < AARCH64_B0_REGNUM + 32)
+    return aarch64_pseudo_write_1 (gdbarch, next_frame,
+				   pseudo_offset - AARCH64_B0_REGNUM, buf);
 
-  if (tdep->has_sve () && regnum >= AARCH64_SVE_V0_REGNUM
-      && regnum < AARCH64_SVE_V0_REGNUM + 32)
-    return aarch64_pseudo_write_1 (gdbarch, regcache,
-				   regnum - AARCH64_SVE_V0_REGNUM,
-				   V_REGISTER_SIZE, buf);
+  if (tdep->has_sve () && pseudo_offset >= AARCH64_SVE_V0_REGNUM
+      && pseudo_offset < AARCH64_SVE_V0_REGNUM + 32)
+    return aarch64_pseudo_write_1 (gdbarch, next_frame,
+				   pseudo_offset - AARCH64_SVE_V0_REGNUM, buf);
 
   gdb_assert_not_reached ("regnum out of bound");
 }
@@ -4483,7 +4493,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
   set_gdbarch_num_pseudo_regs (gdbarch, num_pseudo_regs);
   set_gdbarch_pseudo_register_read_value (gdbarch, aarch64_pseudo_read_value);
-  set_gdbarch_deprecated_pseudo_register_write (gdbarch, aarch64_pseudo_write);
+  set_gdbarch_pseudo_register_write (gdbarch, aarch64_pseudo_write);
   set_tdesc_pseudo_register_name (gdbarch, aarch64_pseudo_register_name);
   set_tdesc_pseudo_register_type (gdbarch, aarch64_pseudo_register_type);
   set_tdesc_pseudo_register_reggroup_p (gdbarch,
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 22/24] gdb: migrate arm to gdbarch_pseudo_register_read_value
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (20 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-08  5:01 ` [PATCH 23/24] gdb: migrate arm to new gdbarch_pseudo_register_write Simon Marchi
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Make arm use the "newer" gdbarch_pseudo_register_read_value.  This fixes
reading pseudo registers in non-current frames on that architecture.

Change-Id: Ic4d3d5d96957a4addfa3443c7b567dc4a31794a9
---
 gdb/arm-tdep.c | 76 +++++++++++++++++++++++++++++---------------------
 1 file changed, 44 insertions(+), 32 deletions(-)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index cd4c760c2ff4..9bf51485b800 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -9816,61 +9816,73 @@ arm_neon_quad_read (struct gdbarch *gdbarch, readable_regcache *regcache,
   return REG_VALID;
 }
 
-/* Read the contents of the MVE pseudo register REGNUM and store it
-   in BUF.  */
+/* Read the contents of a NEON quad register, by reading from two double
+   registers, and return it as a value.  QUAD_REG_INDEX is the index of the quad
+   register, in [0, 15].  */
 
-static enum register_status
-arm_mve_pseudo_read (struct gdbarch *gdbarch, readable_regcache *regcache,
-		     int regnum, gdb_byte *buf)
+static value *
+arm_neon_quad_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
+			  int pseudo_reg_num, int quad_reg_index)
+{
+  std::string raw_reg_name = string_printf ("d%d", quad_reg_index << 1);
+  int double_regnum
+    = user_reg_map_name_to_regnum (gdbarch, raw_reg_name.c_str (),
+				   raw_reg_name.length ());
+
+  return pseudo_from_concat_raw (next_frame, pseudo_reg_num, double_regnum,
+				 double_regnum + 1);
+}
+
+/* Read the contents of the MVE pseudo register REGNUM and return it as a
+   value.  */
+static value *
+arm_mve_pseudo_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
+			   int pseudo_reg_num)
 {
   arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch);
 
   /* P0 is the first 16 bits of VPR.  */
-  return regcache->raw_read_part (tdep->mve_vpr_regnum, 0, 2, buf);
+  return pseudo_from_raw_part (next_frame, pseudo_reg_num,
+			       tdep->mve_vpr_regnum, 0);
 }
 
-static enum register_status
-arm_pseudo_read (struct gdbarch *gdbarch, readable_regcache *regcache,
-		 int regnum, gdb_byte *buf)
+static value *
+arm_pseudo_read_value (gdbarch *gdbarch, frame_info_ptr next_frame,
+		       const int pseudo_reg_num)
 {
-  const int num_regs = gdbarch_num_regs (gdbarch);
-  char name_buf[4];
-  gdb_byte reg_buf[8];
-  int offset, double_regnum;
   arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch);
 
-  gdb_assert (regnum >= num_regs);
+  gdb_assert (pseudo_reg_num >= gdbarch_num_regs (gdbarch));
 
-  if (is_q_pseudo (gdbarch, regnum))
+  if (is_q_pseudo (gdbarch, pseudo_reg_num))
     {
       /* Quad-precision register.  */
-      return arm_neon_quad_read (gdbarch, regcache,
-				 regnum - tdep->q_pseudo_base, buf);
+      return arm_neon_quad_read_value (gdbarch, next_frame, pseudo_reg_num,
+				       pseudo_reg_num - tdep->q_pseudo_base);
     }
-  else if (is_mve_pseudo (gdbarch, regnum))
-    return arm_mve_pseudo_read (gdbarch, regcache, regnum, buf);
+  else if (is_mve_pseudo (gdbarch, pseudo_reg_num))
+    return arm_mve_pseudo_read_value (gdbarch, next_frame, pseudo_reg_num);
   else
     {
-      enum register_status status;
+      int s_reg_index = pseudo_reg_num - tdep->s_pseudo_base;
 
-      regnum -= tdep->s_pseudo_base;
       /* Single-precision register.  */
-      gdb_assert (regnum < 32);
+      gdb_assert (s_reg_index < 32);
 
       /* s0 is always the least significant half of d0.  */
+      int offset;
       if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
-	offset = (regnum & 1) ? 0 : 4;
+	offset = (s_reg_index & 1) ? 0 : 4;
       else
-	offset = (regnum & 1) ? 4 : 0;
+	offset = (s_reg_index & 1) ? 4 : 0;
 
-      xsnprintf (name_buf, sizeof (name_buf), "d%d", regnum >> 1);
-      double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
-						   strlen (name_buf));
+      std::string raw_reg_name = string_printf ("d%d", s_reg_index >> 1);
+      int double_regnum
+	= user_reg_map_name_to_regnum (gdbarch, raw_reg_name.c_str (),
+				       raw_reg_name.length ());
 
-      status = regcache->raw_read (double_regnum, reg_buf);
-      if (status == REG_VALID)
-	memcpy (buf, reg_buf + offset, 4);
-      return status;
+      return pseudo_from_raw_part (next_frame, pseudo_reg_num, double_regnum,
+				   offset);
     }
 }
 
@@ -10905,7 +10917,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   if (tdep->have_s_pseudos || have_mve || have_pacbti)
     {
       set_gdbarch_num_pseudo_regs (gdbarch, num_pseudos);
-      set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_read);
+      set_gdbarch_pseudo_register_read_value (gdbarch, arm_pseudo_read_value);
       set_gdbarch_deprecated_pseudo_register_write (gdbarch, arm_pseudo_write);
     }
 
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 23/24] gdb: migrate arm to new gdbarch_pseudo_register_write
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (21 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 22/24] gdb: migrate arm to gdbarch_pseudo_register_read_value Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-08  5:01 ` [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers Simon Marchi
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@efficios.com>

Make arm use the new gdbarch_pseudo_register_write.  This fixes writing
pseudo registers to non-current frames for that architecture.

Change-Id: Icb2a649ab6394817844230e9e94c3d0564d2f765
---
 gdb/arm-tdep.c | 62 +++++++++++++++++++++++++++++---------------------
 1 file changed, 36 insertions(+), 26 deletions(-)

diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index 9bf51485b800..a0d019b564bf 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -9908,57 +9908,67 @@ arm_neon_quad_write (struct gdbarch *gdbarch, struct regcache *regcache,
   regcache->raw_write (double_regnum + 1, buf + 8);
 }
 
+static void
+arm_neon_quad_write (gdbarch *gdbarch, frame_info_ptr next_frame,
+		     int quad_reg_index, gdb::array_view<const gdb_byte> buf)
+{
+  std::string raw_reg_name = string_printf ("d%d", quad_reg_index << 1);
+  int double_regnum
+    = user_reg_map_name_to_regnum (gdbarch, raw_reg_name.data (),
+				   raw_reg_name.length ());
+
+  pseudo_to_concat_raw (next_frame, buf, double_regnum, double_regnum + 1);
+}
+
 /* Store the contents of BUF to the MVE pseudo register REGNUM.  */
 
 static void
-arm_mve_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache,
-		      int regnum, const gdb_byte *buf)
+arm_mve_pseudo_write (gdbarch *gdbarch, frame_info_ptr next_frame,
+		      int pseudo_reg_num, gdb::array_view<const gdb_byte> buf)
 {
   arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch);
 
   /* P0 is the first 16 bits of VPR.  */
-  regcache->raw_write_part (tdep->mve_vpr_regnum, 0, 2, buf);
+  pseudo_to_raw_part(next_frame, buf, tdep->mve_vpr_regnum, 0);
 }
 
 static void
-arm_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache,
-		  int regnum, const gdb_byte *buf)
+arm_pseudo_write (gdbarch *gdbarch, frame_info_ptr next_frame,
+		  const int pseudo_reg_num,
+		  gdb::array_view<const gdb_byte> buf)
 {
-  const int num_regs = gdbarch_num_regs (gdbarch);
-  char name_buf[4];
-  gdb_byte reg_buf[8];
-  int offset, double_regnum;
   arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch);
 
-  gdb_assert (regnum >= num_regs);
+  gdb_assert (pseudo_reg_num >= gdbarch_num_regs (gdbarch));
 
-  if (is_q_pseudo (gdbarch, regnum))
+  if (is_q_pseudo (gdbarch, pseudo_reg_num))
     {
       /* Quad-precision register.  */
-      arm_neon_quad_write (gdbarch, regcache,
-			   regnum - tdep->q_pseudo_base, buf);
+      arm_neon_quad_write (gdbarch, next_frame,
+			   pseudo_reg_num - tdep->q_pseudo_base, buf);
     }
-  else if (is_mve_pseudo (gdbarch, regnum))
-    arm_mve_pseudo_write (gdbarch, regcache, regnum, buf);
+  else if (is_mve_pseudo (gdbarch, pseudo_reg_num))
+    arm_mve_pseudo_write (gdbarch, next_frame, pseudo_reg_num, buf);
   else
     {
-      regnum -= tdep->s_pseudo_base;
+      int s_reg_index = pseudo_reg_num - tdep->s_pseudo_base;
+
       /* Single-precision register.  */
-      gdb_assert (regnum < 32);
+      gdb_assert (s_reg_index < 32);
 
       /* s0 is always the least significant half of d0.  */
+      int offset;
       if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
-	offset = (regnum & 1) ? 0 : 4;
+	offset = (s_reg_index & 1) ? 0 : 4;
       else
-	offset = (regnum & 1) ? 4 : 0;
+	offset = (s_reg_index & 1) ? 4 : 0;
 
-      xsnprintf (name_buf, sizeof (name_buf), "d%d", regnum >> 1);
-      double_regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
-						   strlen (name_buf));
+      std::string raw_reg_name = string_printf ("d%d", s_reg_index >> 1);
+      int double_regnum
+	= user_reg_map_name_to_regnum (gdbarch, raw_reg_name.c_str (),
+				       raw_reg_name.length ());
 
-      regcache->raw_read (double_regnum, reg_buf);
-      memcpy (reg_buf + offset, buf, 4);
-      regcache->raw_write (double_regnum, reg_buf);
+      pseudo_to_raw_part (next_frame, buf, double_regnum, offset);
     }
 }
 
@@ -10918,7 +10928,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
     {
       set_gdbarch_num_pseudo_regs (gdbarch, num_pseudos);
       set_gdbarch_pseudo_register_read_value (gdbarch, arm_pseudo_read_value);
-      set_gdbarch_deprecated_pseudo_register_write (gdbarch, arm_pseudo_write);
+      set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_write);
     }
 
   /* Add standard register aliases.  We add aliases even for those
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (22 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 23/24] gdb: migrate arm to new gdbarch_pseudo_register_write Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  2023-11-08  5:16 ` [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

This patch adds tests to exercise the previous patches' changes.

All three tests:

 - aarch64-pseudo-unwind
 - amd64-pseudo-unwind
 - arm-pseudo-unwind

follow the same pattern, just with different registers.

The other test, arm-pseudo-unwind-legacy, tests the special case where
the unwind information contains an entry for a register considered a
pseudo-register by GDB.

Change-Id: Ic29ac040c5eb087b4a0d79f9d02f65b7979df30f
---
 .../gdb.arch/aarch64-pseudo-unwind-asm.S      | 82 +++++++++++++++++
 .../gdb.arch/aarch64-pseudo-unwind.c          | 33 +++++++
 .../gdb.arch/aarch64-pseudo-unwind.exp        | 90 ++++++++++++++++++
 .../gdb.arch/amd64-pseudo-unwind-asm.S        | 66 ++++++++++++++
 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c  | 33 +++++++
 .../gdb.arch/amd64-pseudo-unwind.exp          | 91 +++++++++++++++++++
 .../gdb.arch/arm-pseudo-unwind-asm.S          | 81 +++++++++++++++++
 .../gdb.arch/arm-pseudo-unwind-legacy-asm.S   | 84 +++++++++++++++++
 .../gdb.arch/arm-pseudo-unwind-legacy.c       | 33 +++++++
 .../gdb.arch/arm-pseudo-unwind-legacy.exp     | 86 ++++++++++++++++++
 gdb/testsuite/gdb.arch/arm-pseudo-unwind.c    | 33 +++++++
 gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp  | 88 ++++++++++++++++++
 12 files changed, 800 insertions(+)
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.c
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.exp
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy-asm.S
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.c
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.exp
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.c
 create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp

diff --git a/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind-asm.S b/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind-asm.S
new file mode 100644
index 000000000000..586642656296
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind-asm.S
@@ -0,0 +1,82 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+.section .note.GNU-stack,"",%progbits
+
+.data
+value_callee:
+.quad 0x2021222324252627
+value_caller:
+.quad 0x1011121314151617
+
+.text
+.global callee
+callee:
+	/* Standard prologue:
+	    - push fp (x29) and lr (x30) to the stack.
+	    - mov sp to fp  */
+.cfi_startproc
+	stp x29, x30, [sp, -16]!
+.cfi_def_cfa 29, 16
+.cfi_offset 29, -16
+.cfi_offset 30, -8
+	mov x29, sp
+
+	/* Save caller's q8 value on the stack.  */
+.cfi_offset 72, -32
+	str q8, [sp, -16]!
+
+	/* Put our own q8 value.  */
+	adr x0, value_callee
+	ld1 { v8.1d }, [x0]
+break_here_asm:
+
+	/* Restore caller's q8 value.  */
+	ldr q8, [sp], 16
+
+	/* Standard epilogue:
+	    - pop fo (x29) and lr (x30) from the stack  */
+	ldp x29, x30, [sp], 16
+	ret
+.cfi_endproc
+
+
+.global caller
+caller:
+	/* Standard prologue.  */
+.cfi_startproc
+	stp x29, x30, [sp, -16]!
+.cfi_def_cfa 29, 16
+.cfi_offset x29, -16
+.cfi_offset x30, -8
+	add x29, sp, 0
+
+	/* Put our own q8 value.  */
+	adr x0, value_caller
+	ld1 { v8.1d }, [x0]
+
+	/* Call callee.  */
+	bl callee
+
+	/* Store our q8 value in x0 to return it.  */
+	str q8, [sp, -16]!
+	ldr x0, [sp], 16
+
+	/* Standard epilogue.  */
+	ldp x29, x30, [sp], 16
+	ret
+.cfi_endproc
diff --git a/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.c b/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.c
new file mode 100644
index 000000000000..d18876fe3be3
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.c
@@ -0,0 +1,33 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include <stdint.h>
+
+uint64_t caller (void);
+
+static void
+break_here_c (uint64_t value)
+{
+}
+
+int
+main (void)
+{
+  uint64_t value = caller ();
+  break_here_c (value);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp b/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp
new file mode 100644
index 000000000000..685bebc9df1f
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp
@@ -0,0 +1,90 @@
+# Copyright 2018-2023 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 test is equivalent to amd64-pseudo-unwind, but specific to AArch64.  We
+# use the raw register v8/q8 (it's the same register, but referred to differently
+# depending on the instruction) which is 128 bits long (although we only load
+# a 64-bits value, it's enough for the test).  We use pseudo register s8, which
+# is the low 32-bits of v8/q8.
+
+if { ![istarget aarch64-*-* ] } {
+    verbose "Skipping aarch64 pseudo register unwind."
+    return
+}
+
+standard_testfile aarch64-pseudo-unwind.c aarch64-pseudo-unwind-asm.S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	"${srcfile} ${srcfile2}" {debug}] } {
+    return -1
+}
+
+clean_restart ${binfile}
+
+if ![runto_main] then {
+    fail "could not run to main"
+}
+
+gdb_breakpoint break_here_asm temporary
+gdb_continue_to_breakpoint "continue to callee"
+
+# Verify the value of v8/s8 in the inner frame (callee).
+with_test_prefix "callee, before change" {
+    gdb_test "p/x \$v8.q.u" " = \\{0x2021222324252627\\}"
+    gdb_test "p/x \$s8.u" " = 0x24252627"
+}
+
+# Verify that we can change the value of the pseudo register (s8) in the inner
+# frame (callee).
+gdb_test_no_output "set \$s8.u = 0x34353637"
+
+# Verify the value of v8/s8 in the inner frame (callee) after the change.
+with_test_prefix "callee, after change" {
+    gdb_test "p/x \$v8.q.u" " = \\{0x34353637\\}"
+    gdb_test "p/x \$s8.u" " = 0x34353637"
+}
+
+# Go up one frame (to caller) and do the same.
+gdb_test "up"
+
+# Verify the value of v8/s8 in the outer frame (caller).
+with_test_prefix "caller, before change" {
+    gdb_test "p/x \$v8.q.u" " = \\{0x1011121314151617\\}"
+    gdb_test "p/x \$s8.u" " = 0x14151617"
+}
+
+# Verify that we can change the value of the pseudo register (s8) in the outer
+# frame (caller).
+gdb_test_no_output "set \$s8.u = 0x44454647"
+
+# Verify the value of v8/s8 in the outer frame (caller) after the change.
+with_test_prefix "caller, after change" {
+    gdb_test "p/x \$v8.q.u" " = \\{0x44454647\\}"
+    gdb_test "p/x \$s8.u" " = 0x44454647"
+}
+
+# Go back to frame 0 (callee), check that the change to the outer frame didn't
+# mess up anything there.
+gdb_test "down"
+with_test_prefix "callee, after change in caller" {
+    gdb_test "p/x \$v8.q.u" " = \\{0x34353637\\}"
+    gdb_test "p/x \$s8.u" " = 0x34353637"
+}
+
+# Verify that the value of the saved v8 we changed is correctly seen by the
+# inferior.
+gdb_breakpoint break_here_c temporary
+gdb_continue_to_breakpoint "continue to break_here_c"
+gdb_test "p/x value" " = 0x44454647"
diff --git a/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S b/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
new file mode 100644
index 000000000000..c306b82e4864
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
@@ -0,0 +1,66 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+.section .note.GNU-stack,"",%progbits
+
+.text
+.global callee
+callee:
+	/* Standard prologue.  */
+.cfi_startproc
+	push %rbp
+.cfi_def_cfa rbp, 16
+	mov %rsp, %rbp
+
+	/* Save caller's rbx value on the stack.  */
+.cfi_offset rbx, -24
+	push %rbx
+
+	/* Put our own rbx value.  */
+	mov $0x2021222324252627, %rbx
+break_here_asm:
+
+	/* Restore caller's rbx value.  */
+	pop %rbx
+
+	/* Standard epilogue.  */
+	pop %rbp
+	ret
+.cfi_endproc
+
+
+.global caller
+caller:
+.cfi_startproc
+	/* Standard prologue.  */
+	push %rbp
+.cfi_def_cfa_offset 16
+	mov %rsp, %rbp
+
+	/* Put our own rbx value.  */
+	mov $0x1011121314151617, %rbx
+
+	/* Call callee.  */
+	call callee
+
+	/* Store our rbx value in rax to return it.  */
+	mov %rbx, %rax
+
+	/* Standard epilogue.  */
+	pop %rbp
+	ret
+.cfi_endproc
diff --git a/gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c b/gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c
new file mode 100644
index 000000000000..d18876fe3be3
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c
@@ -0,0 +1,33 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include <stdint.h>
+
+uint64_t caller (void);
+
+static void
+break_here_c (uint64_t value)
+{
+}
+
+int
+main (void)
+{
+  uint64_t value = caller ();
+  break_here_c (value);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-pseudo-unwind.exp b/gdb/testsuite/gdb.arch/amd64-pseudo-unwind.exp
new file mode 100644
index 000000000000..45e9a3c96224
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-pseudo-unwind.exp
@@ -0,0 +1,91 @@
+# Copyright 2018-2023 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 test verifies that we can read and write the value of a pseudo register
+# in unwound frames.  For the test, we choose one raw register, rbx, and one
+# pseudo register that is backed by rbx, ebx.  We have two frames (the inner one,
+# #0 and the outer one, #1) that each set a value for rbx.  We verify that we
+# can read both rbx and ebx correctly for each frame, and that when we write to
+# ebx, rbx for that frame is correctly updated.
+
+if { ![istarget x86_64-*-* ] || ![is_lp64_target] } {
+    verbose "Skipping amd64 pseudo register unwind."
+    return
+}
+
+standard_testfile amd64-pseudo-unwind.c amd64-pseudo-unwind-asm.S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	"${srcfile} ${srcfile2}" {debug}] } {
+    return -1
+}
+
+clean_restart ${binfile}
+
+if ![runto_main] then {
+    fail "could not run to main"
+}
+
+gdb_breakpoint break_here_asm temporary
+gdb_continue_to_breakpoint "continue to callee"
+
+# Verify the value of rbx/ebx in the inner frame (callee).
+with_test_prefix "callee, before change" {
+    gdb_test "p/x \$rbx" " = 0x2021222324252627"
+    gdb_test "p/x \$ebx" " = 0x24252627"
+}
+
+# Verify that we can change the value of the pseudo register (ebx) in the inner
+# frame (callee).
+gdb_test_no_output "set \$ebx = 0x34353637"
+
+# Verify the value of rbx/ebx in the inner frame (callee) after the change.
+with_test_prefix "callee, after change" {
+    gdb_test "p/x \$rbx" " = 0x2021222334353637"
+    gdb_test "p/x \$ebx" " = 0x34353637"
+}
+
+# Go up one frame, and do the same.
+gdb_test "up"
+
+# Verify the value of rbx/ebx in the outer frame (caller).
+with_test_prefix "caller, before change" {
+    gdb_test "p/x \$rbx" " = 0x1011121314151617"
+    gdb_test "p/x \$ebx" " = 0x14151617"
+}
+
+# Verify that we can change the value of the pseudo register (ebx) in the outer
+# frame (caller).
+gdb_test_no_output "set \$ebx = 0x44454647"
+
+# Verify the value of rbx/ebx in the outer frame (caller) after the change.
+with_test_prefix "caller, after change" {
+    gdb_test "p/x \$rbx" " = 0x1011121344454647"
+    gdb_test "p/x \$ebx" " = 0x44454647"
+}
+
+# Go back to frame 0 (callee), check that the change to the outer frame didn't
+# mess up anything there.
+gdb_test "down"
+with_test_prefix "callee, after change in caller" {
+    gdb_test "p/x \$rbx" " = 0x2021222334353637"
+    gdb_test "p/x \$ebx" " = 0x34353637"
+}
+
+# Verify that the value of the saved rbx we changed is correctly seen by the
+# inferior.
+gdb_breakpoint break_here_c temporary
+gdb_continue_to_breakpoint "continue to break_here_c"
+gdb_test "p/x value" " = 0x1011121344454647"
diff --git a/gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
new file mode 100644
index 000000000000..8d78573cefe4
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
@@ -0,0 +1,81 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+.section .note.GNU-stack,"",%progbits
+
+.data
+value_callee:
+.quad 0x2021222324252627
+value_caller:
+.quad 0x1011121314151617
+
+.text
+.arm
+.global callee
+callee:
+	/* Standard prologue.  */
+.cfi_startproc
+	push {fp, lr}
+.cfi_def_cfa fp, 4
+.cfi_offset fp, -8
+.cfi_offset lr, -4
+	add fp, sp, #4
+
+	/* Save caller's d8 value on the stack.  */
+.cfi_offset d8, -16
+	vpush {d8}
+
+	/* Put our own d8 value.  */
+	ldr r0, =value_callee
+	vldr d8, [r0]
+break_here_asm:
+
+	/* Restore caller's d8 value.  */
+	vpop {d8}
+
+	/* Standard epilogue.  */
+	pop {fp, pc}
+.cfi_endproc
+
+
+.global caller
+caller:
+	/* Standard prologue.  */
+.cfi_startproc
+	push {fp, lr}
+.cfi_def_cfa fp, 4
+.cfi_offset fp, -8
+.cfi_offset lr, -4
+	add fp, sp, #4
+
+	/* Put our own d8 value.  */
+	ldr r0, =value_caller
+	vldr d8, [r0]
+
+	/* Call callee.  */
+	bl callee
+
+	/* Store our d8 value in r0-r1 to return it.  */
+	vpush {d8}
+	pop {r0}
+	pop {r1}
+
+	/* Standard epilogue.  */
+	pop {fp, pc}
+.cfi_endproc
+
+
diff --git a/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy-asm.S b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy-asm.S
new file mode 100644
index 000000000000..786e79a0543e
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy-asm.S
@@ -0,0 +1,84 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+/* The difference between this and arm-pseudo-unwind is that here, the CFI
+   directives use the obsolete DWARF number for the s16 register (a
+   pseudo-register in GDB), whereas arm-pseudo-unwind uses the number for the d8
+   register (the underlying raw register for s16).  */
+
+.section .note.GNU-stack,"",%progbits
+
+.data
+value_callee:
+.quad 0x20212223
+value_caller:
+.quad 0x10111213
+
+.text
+.arm
+.global callee
+callee:
+.cfi_startproc
+	/* Standard prologue.  */
+	push {fp, lr}
+.cfi_def_cfa fp, 4
+.cfi_offset fp, -8
+.cfi_offset lr, -4
+	add fp, sp, #4
+
+	/* Save caller's s16 value on the stack.  */
+.cfi_offset 80, -12
+	vpush {s16}
+
+	/* Put our own s16 value.  */
+	ldr r0, =value_callee
+	vldr s16, [r0]
+break_here_asm:
+
+	/* Restore caller's s16 value.  */
+	vpop {s16}
+
+	/* Standard epilogue.  */
+	pop {fp, pc}
+.cfi_endproc
+
+
+.global caller
+caller:
+.cfi_startproc
+	/* Standard prologue.  */
+	push {fp, lr}
+.cfi_def_cfa fp, 4
+.cfi_offset fp, -8
+.cfi_offset lr, -4
+	add fp, sp, #4
+
+	/* Put our own s16 value.  */
+	ldr r0, =value_caller
+	vldr s16, [r0]
+
+	/* Call callee.  */
+	bl callee
+
+	/* Store our s16 value in r0-r1 to return it.  */
+	vpush {s16}
+	pop {r0}
+	mov r1, #0
+
+	/* Standard epilogue.  */
+	pop {fp, pc}
+.cfi_endproc
diff --git a/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.c b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.c
new file mode 100644
index 000000000000..d18876fe3be3
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.c
@@ -0,0 +1,33 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include <stdint.h>
+
+uint64_t caller (void);
+
+static void
+break_here_c (uint64_t value)
+{
+}
+
+int
+main (void)
+{
+  uint64_t value = caller ();
+  break_here_c (value);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.exp b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.exp
new file mode 100644
index 000000000000..c3d0979799dc
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.exp
@@ -0,0 +1,86 @@
+# Copyright 2018-2023 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 test is in the same vein as amd64-pseudo-unwind, making sure we can
+# read write pseudo registers in outer frames.  However, it tests a special
+# case where the debug info includes unwind information for a pseudo register
+# but not the underlying raw register.  This can happen for the pseudo register
+# s16, which is the bottom half of the raw register d8.
+#
+# See "DWARF for the ARM architecture":
+#   https://github.com/ARM-software/abi-aa/releases/download/2023Q3/aadwarf32.pdf
+
+if { ![istarget arm*-*-* ] } {
+    verbose "Skipping arm pseudo register unwind."
+    return
+}
+
+standard_testfile arm-pseudo-unwind-legacy.c arm-pseudo-unwind-legacy-asm.S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	"${srcfile} ${srcfile2}" {debug additional_flags=-marm}] } {
+    return -1
+}
+
+clean_restart ${binfile}
+
+if ![runto_main] then {
+    fail "could not run to main"
+}
+
+gdb_breakpoint break_here_asm temporary
+gdb_continue_to_breakpoint "continue to callee"
+
+# Verify the value of s16 in the inner frame (callee).
+with_test_prefix "callee, before change" {
+    gdb_test "p/x \$s16" " = 0x20212223"
+}
+
+# Verify that we can change the value of s16 in the inner frame (callee).
+gdb_test_no_output "set \$s16 = 1.0"
+
+# Verify the value of s16 in the inner frame (callee) after the change.
+with_test_prefix "callee, after change" {
+    gdb_test "p/x \$s16" " = 0x3f800000"
+}
+
+# Go up one frame, and do the same.
+gdb_test "up"
+
+# Verify the value of s16 in the outer frame (caller).
+with_test_prefix "caller, before change" {
+    gdb_test "p/x \$s16" " = 0x10111213"
+}
+
+# Verify that we can change the value of s16 in the outer frame (caller).
+gdb_test_no_output "set \$s16 = 2.0"
+
+# Verify the value of s16 in the outer frame (caller) after the change.
+with_test_prefix "caller, after change" {
+    gdb_test "p/x \$s16" " = 0x40000000"
+}
+
+# Go back to frame 0 (callee), check that the change to the outer frame didn't
+# mess up anything there.
+gdb_test "down"
+with_test_prefix "callee, after change in caller" {
+    gdb_test "p/x \$s16" " = 0x3f800000"
+}
+
+# Verify that the value of the saved s16 we changed is correctly seen by the
+# inferior.
+gdb_breakpoint break_here_c temporary
+gdb_continue_to_breakpoint "continue to break_here_c"
+gdb_test "p/x value" " = 0x40000000"
diff --git a/gdb/testsuite/gdb.arch/arm-pseudo-unwind.c b/gdb/testsuite/gdb.arch/arm-pseudo-unwind.c
new file mode 100644
index 000000000000..d18876fe3be3
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind.c
@@ -0,0 +1,33 @@
+/* Copyright 2018-2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include <stdint.h>
+
+uint64_t caller (void);
+
+static void
+break_here_c (uint64_t value)
+{
+}
+
+int
+main (void)
+{
+  uint64_t value = caller ();
+  break_here_c (value);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp b/gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp
new file mode 100644
index 000000000000..bfe81b7d5f21
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp
@@ -0,0 +1,88 @@
+# Copyright 2018-2023 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 test is equivalent to amd64-pseudo-unwind, but specific to ARM.  We
+# use the raw register d8 which is 64 bits long.  We use pseudo register s16,
+# which is the low 32 bits of d8.
+
+if { ![istarget arm*-*-* ] } {
+    verbose "Skipping arm pseudo register unwind."
+    return
+}
+
+standard_testfile arm-pseudo-unwind.c arm-pseudo-unwind-asm.S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+	"${srcfile} ${srcfile2}" {debug additional_flags=-marm}] } {
+    return -1
+}
+
+clean_restart ${binfile}
+
+if ![runto_main] then {
+    fail "could not run to main"
+}
+
+gdb_breakpoint break_here_asm temporary
+gdb_continue_to_breakpoint "continue to callee"
+
+# Verify the value of d8/s16 in the inner frame (callee).
+with_test_prefix "callee, before change" {
+    gdb_test "p/x \$d8" " = 0x2021222324252627"
+    gdb_test "p/x \$s16" " = 0x24252627"
+}
+
+# Verify that we can change the value of the pseudo register (s16) in the inner
+# frame (callee).
+gdb_test_no_output "set \$s16 = 1.0"
+
+# Verify the value of d8/s16 in the inner frame (callee) after the change.
+with_test_prefix "callee, after change" {
+    gdb_test "p/x \$d8" " = 0x202122233f800000"
+    gdb_test "p/x \$s16" " = 0x3f800000"
+}
+
+# Go up one frame (to caller), and do the same.
+gdb_test "up"
+
+# Verify the value of d8/s16 in the outer frame (caller).
+with_test_prefix "caller, before change" {
+    gdb_test "p/x \$d8" " = 0x1011121314151617"
+    gdb_test "p/x \$s16" " = 0x14151617"
+}
+
+# Verify that we can change the value of the pseudo register (s16) in the outer
+# frame (caller).
+gdb_test_no_output "set \$s16 = 2.0"
+
+# Verify the value of d8/s16 in the outer frame (caller) after the change.
+with_test_prefix "caller, after change" {
+    gdb_test "p/x \$d8" " = 0x1011121340000000"
+    gdb_test "p/x \$s16" " = 0x40000000"
+}
+
+# Go back to frame 0 (callee), check that the change to the outer frame didn't
+# mess up anything there.
+gdb_test "down"
+with_test_prefix "callee, after change in caller" {
+    gdb_test "p/x \$d8" " = 0x202122233f800000"
+    gdb_test "p/x \$s16" " = 0x3f800000"
+}
+
+# Verify that the value of the saved d8 we changed is correctly seen by the
+# inferior.
+gdb_breakpoint break_here_c temporary
+gdb_continue_to_breakpoint "continue to break_here_c"
+gdb_test "p/x value" " = 0x1011121340000000"
-- 
2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (23 preceding siblings ...)
  2023-11-08  5:01 ` [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers Simon Marchi
@ 2023-11-08  5:16 ` Simon Marchi
  2023-11-09  3:05   ` Simon Marchi
  2023-11-08 11:57 ` Luis Machado
  2023-11-11 20:26 ` John Baldwin
  26 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08  5:16 UTC (permalink / raw)
  To: gdb-patches



On 2023-11-08 00:00, Simon Marchi wrote:
> This series fixes reading/writing pseudo registers from/to non-current
> frames (that is, frames other than frame 0).  Currently, we get this:
> 
>     (gdb) frame 0
>     #0  break_here_asm () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:38
>     38              pop %rbx
>     (gdb) p/x $rbx
>     $1 = 0x2021222324252627
>     (gdb) p/x $ebx
>     $2 = 0x24252627
>     (gdb) frame 1
>     #1  0x000055555555517d in caller () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:58
>     58              call callee
>     (gdb) p/x $rbx
>     $3 = 0x1011121314151617
>     (gdb) p/x $ebx
>     $4 = 0x24252627
> 
> This is a bit surprising, we would expect the last value to be
> 0x14151617, the bottom half of the rbx value from frame 1 (the currently
> selected frame at that point).  Instead, we got the bottom half of the
> rbx value from frame 0.  This is because pseudo registers are always
> read/written from/to the current thread's regcache.
> 
> This series fixes this (as well as writing to pseudo registers) by
> making it so pseudo registers are read/written using a frame.

Ah, I forgot because it's been so long, but this can be considered a v3
of this series  here...

  https://inbox.sourceware.org/gdb-patches/20181024014333.14143-1-simon.marchi@polymtl.ca/

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (24 preceding siblings ...)
  2023-11-08  5:16 ` [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
@ 2023-11-08 11:57 ` Luis Machado
  2023-11-08 15:47   ` Simon Marchi
  2023-11-11 20:26 ` John Baldwin
  26 siblings, 1 reply; 51+ messages in thread
From: Luis Machado @ 2023-11-08 11:57 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

Hi Simon,

On 11/8/23 05:00, Simon Marchi wrote:
> This series fixes reading/writing pseudo registers from/to non-current
> frames (that is, frames other than frame 0).  Currently, we get this:
> 
>     (gdb) frame 0
>     #0  break_here_asm () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:38
>     38              pop %rbx
>     (gdb) p/x $rbx
>     $1 = 0x2021222324252627
>     (gdb) p/x $ebx
>     $2 = 0x24252627
>     (gdb) frame 1
>     #1  0x000055555555517d in caller () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:58
>     58              call callee
>     (gdb) p/x $rbx
>     $3 = 0x1011121314151617
>     (gdb) p/x $ebx
>     $4 = 0x24252627
> 
> This is a bit surprising, we would expect the last value to be
> 0x14151617, the bottom half of the rbx value from frame 1 (the currently
> selected frame at that point).  Instead, we got the bottom half of the
> rbx value from frame 0.  This is because pseudo registers are always
> read/written from/to the current thread's regcache.
> 
> This series fixes this (as well as writing to pseudo registers) by
> making it so pseudo registers are read/written using a frame.
> 
> Simon Marchi (24):
>   gdb: don't handle i386 k registers as pseudo registers
>   gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h
>   gdb: make store_integer take an array_view
>   gdb: simplify conditions in
>     regcache::{read,write,raw_collect,raw_supply}_part
>   gdb: change regcache interface to use array_view
>   gdb: fix bugs in {get,put}_frame_register_bytes
>   gdb: make put_frame_register take an array_view
>   gdb: change value_of_register and value_of_register_lazy to take the
>     next frame
>   gdb: remove frame_register
>   gdb: make put_frame_register take the next frame
>   gdb: make put_frame_register_bytes take the next frame
>   gdb: make get_frame_register_bytes take the next frame
>   gdb: add value::allocate_register
>   gdb: read pseudo register through frame
>   gdb: change parameter name in frame_unwind_register_unsigned
>     declaration
>   gdb: rename gdbarch_pseudo_register_write to
>     gdbarch_deprecated_pseudo_register_write
>   gdb: add gdbarch_pseudo_register_write that takes a frame
>   gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write
>   gdb: make aarch64_za_offsets_from_regnum return za_offsets
>   gdb: add missing raw register read in
>     aarch64_sme_pseudo_register_write
>   gdb: migrate aarch64 to new gdbarch_pseudo_register_write
>   gdb: migrate arm to gdbarch_pseudo_register_read_value
>   gdb: migrate arm to new gdbarch_pseudo_register_write
>   gdb/testsuite: add tests for unwinding of pseudo registers
> 
>  gdb/aarch64-tdep.c                            | 297 +++++-----
>  gdb/alpha-tdep.c                              |  11 +-
>  gdb/amd64-tdep.c                              |  82 +--
>  gdb/arch/arm-get-next-pcs.c                   |   6 +-
>  gdb/arch/arm-get-next-pcs.h                   |   5 +-
>  gdb/arch/arm.c                                |   2 +-
>  gdb/arch/arm.h                                |   4 +-
>  gdb/arm-linux-tdep.c                          |  11 +-
>  gdb/arm-tdep.c                                | 145 +++--
>  gdb/avr-tdep.c                                |   3 +-
>  gdb/bfin-tdep.c                               |   3 +-
>  gdb/csky-tdep.c                               |   4 +-
>  gdb/defs.h                                    |  39 +-
>  gdb/dwarf2/expr.c                             |  22 +-
>  gdb/dwarf2/frame.c                            |   5 +-
>  gdb/eval.c                                    |   3 +-
>  gdb/findvar.c                                 |  50 +-
>  gdb/frame-unwind.c                            |   3 +-
>  gdb/frame.c                                   | 174 +++---
>  gdb/frame.h                                   |  28 +-
>  gdb/frv-tdep.c                                |   3 +-
>  gdb/gdbarch-gen.h                             |  28 +-
>  gdb/gdbarch.c                                 |  40 +-
>  gdb/gdbarch_components.py                     |  29 +-
>  gdb/guile/scm-frame.c                         |   3 +-
>  gdb/h8300-tdep.c                              |   3 +-
>  gdb/i386-tdep.c                               | 380 ++++--------
>  gdb/i386-tdep.h                               |  15 +-
>  gdb/i387-tdep.c                               |  16 +-
>  gdb/ia64-tdep.c                               |  18 +-
>  gdb/infcmd.c                                  |   6 +-
>  gdb/loongarch-tdep.c                          |   3 +-
>  gdb/m32c-tdep.c                               |   3 +-
>  gdb/m68hc11-tdep.c                            |   3 +-
>  gdb/m68k-tdep.c                               |  17 +-
>  gdb/mep-tdep.c                                |   3 +-
>  gdb/mi/mi-main.c                              |   3 +-
>  gdb/mips-tdep.c                               |  29 +-
>  gdb/msp430-tdep.c                             |   3 +-
>  gdb/nat/aarch64-hw-point.c                    |   3 +-
>  gdb/nat/aarch64-scalable-linux-ptrace.c       |  20 +-
>  gdb/nat/linux-btrace.c                        |   3 +-
>  gdb/nds32-tdep.c                              |   8 +-
>  gdb/python/py-frame.c                         |   3 +-
>  gdb/python/py-unwind.c                        |   4 +-
>  gdb/regcache.c                                | 548 +++++++++++-------
>  gdb/regcache.h                                | 113 +++-
>  gdb/riscv-tdep.c                              |  13 +-
>  gdb/rl78-tdep.c                               |   3 +-
>  gdb/rs6000-tdep.c                             |  21 +-
>  gdb/s12z-tdep.c                               |   2 +-
>  gdb/s390-tdep.c                               |   3 +-
>  gdb/sh-tdep.c                                 |   9 +-
>  gdb/sparc-tdep.c                              |   3 +-
>  gdb/sparc64-tdep.c                            |   3 +-
>  gdb/std-regs.c                                |  11 +-
>  .../gdb.arch/aarch64-pseudo-unwind-asm.S      |  82 +++
>  .../gdb.arch/aarch64-pseudo-unwind.c          |  33 ++
>  .../gdb.arch/aarch64-pseudo-unwind.exp        |  90 +++
>  .../gdb.arch/amd64-pseudo-unwind-asm.S        |  66 +++
>  gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c  |  33 ++
>  .../gdb.arch/amd64-pseudo-unwind.exp          |  91 +++
>  .../gdb.arch/arm-pseudo-unwind-asm.S          |  81 +++
>  .../gdb.arch/arm-pseudo-unwind-legacy-asm.S   |  84 +++
>  .../gdb.arch/arm-pseudo-unwind-legacy.c       |  33 ++
>  .../gdb.arch/arm-pseudo-unwind-legacy.exp     |  86 +++
>  gdb/testsuite/gdb.arch/arm-pseudo-unwind.c    |  33 ++
>  gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp  |  88 +++
>  gdb/valops.c                                  |  31 +-
>  gdb/value.c                                   | 149 +++++
>  gdb/value.h                                   |  64 +-
>  gdb/xtensa-tdep.c                             |   3 +-
>  gdbserver/linux-arm-low.cc                    |   4 +-
>  gdbserver/regcache.cc                         |  69 ++-
>  gdbserver/regcache.h                          |   6 +-
>  gdbsupport/common-regcache.cc                 |   2 +-
>  gdbsupport/common-regcache.h                  |  58 +-
>  gdbsupport/rsp-low.cc                         |   8 +
>  gdbsupport/rsp-low.h                          |   2 +
>  79 files changed, 2324 insertions(+), 1144 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind-asm.S
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.c
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.exp
>  create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
>  create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy-asm.S
>  create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.c
>  create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.exp
>  create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.c
>  create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp
> 
> 
> base-commit: 1185b5b79a12ba67eb60bee3f75babf7a222fde0

I haven't tracked the particular patch that causes this, but for aarch64 systems supporting SVE
(and probably SME as well) I'm seeing internal errors related to one assertion:

Running gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp ...
PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to callee
PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $v8.q.u
PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $s8.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $s8.u
PASS: gdb.arch/aarch64-pseudo-unwind.exp: up
PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $v8.q.u
PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $s8.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x44454647 (GDB internal error)
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
PASS: gdb.arch/aarch64-pseudo-unwind.exp: down
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $s8.u
PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
Running gdb/testsuite/gdb.arch/aarch64-fp.exp ...
PASS: gdb.arch/aarch64-fp.exp: set the breakpoint after setting the fp registers
PASS: gdb.arch/aarch64-fp.exp: continue until breakpoint
PASS: gdb.arch/aarch64-fp.exp: check register q0 value
PASS: gdb.arch/aarch64-fp.exp: check register q1 value
PASS: gdb.arch/aarch64-fp.exp: check register v0 value
PASS: gdb.arch/aarch64-fp.exp: check register v1 value
PASS: gdb.arch/aarch64-fp.exp: check register fpsr value
PASS: gdb.arch/aarch64-fp.exp: check register fpcr value
FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set h0.bf to 129 (GDB internal error)
FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid
FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 0 (GDB internal error)
FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 0
FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 129 (GDB internal error)
FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 129

The failure mode is:

set $s8.u = 0x34353637^M
../../../repos/binutils-gdb/gdb/frame.c:1441: internal-error: put_frame_register: Assertion `buf.size () == size' failed.^M
A problem internal to GDB has been detected,^M
further debugging may prove unreliable.^M
----- Backtrace -----^M
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
Resyncing due to internal error.
0xaaaaabd6e08f gdb_internal_backtrace_1^M
        ../../../repos/binutils-gdb/gdb/bt-utils.c:122^M
0xaaaaabd6e08f _Z22gdb_internal_backtracev^M
        ../../../repos/binutils-gdb/gdb/bt-utils.c:168^M
0xaaaaac315307 internal_vproblem^M
        ../../../repos/binutils-gdb/gdb/utils.c:396^M
0xaaaaac31556f _Z15internal_verrorPKciS0_St9__va_list^M
        ../../../repos/binutils-gdb/gdb/utils.c:476^M
0xaaaaac8604e3 _Z18internal_error_locPKciS0_z^M
        ../../../repos/binutils-gdb/gdbsupport/errors.cc:58^M
0xaaaaabec89c7 _Z18put_frame_register14frame_info_ptriN3gdb10array_viewIKhEE^M
        ../../../repos/binutils-gdb/gdb/frame.c:1441^M
0xaaaaabc55243 aarch64_pseudo_write_1^M
        ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3316^M
0xaaaaabc5c943 aarch64_pseudo_write^M
        ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3414^M
0xaaaaabce0913 _Z29gdbarch_pseudo_register_writeP7gdbarch14frame_info_ptriN3gdb10array_viewIKhEE^M
        ../../../repos/binutils-gdb/gdb/gdbarch.c:1927^M
0xaaaaabec892f _Z18put_frame_register14frame_info_ptriN3gdb10array_viewIKhEE^M
        ../../../repos/binutils-gdb/gdb/frame.c:1462^M
0xaaaaabed0fa7 _Z24put_frame_register_bytes14frame_info_ptrimN3gdb10array_viewIKhEE^M
        ../../../repos/binutils-gdb/gdb/frame.c:1592^M
0xaaaaac33092b _Z12value_assignP5valueS0_^M
        ../../../repos/binutils-gdb/gdb/valops.c:1260^M
0xaaaaabe8fcdf _ZN10expression8evaluateEP4type6noside^M
        ../../../repos/binutils-gdb/gdb/eval.c:111^M
0xaaaaac0cba9f set_command^M
        ../../../repos/binutils-gdb/gdb/printcmd.c:1542^M
0xaaaaabd9d91b _Z8cmd_funcP16cmd_list_elementPKci^M
        ../../../repos/binutils-gdb/gdb/cli/cli-decode.c:2735^M
0xaaaaac2af583 _Z15execute_commandPKci^M
        ../../../repos/binutils-gdb/gdb/top.c:575^M
0xaaaaabe97413 _Z15command_handlerPKc^M
        ../../../repos/binutils-gdb/gdb/event-top.c:552^M
0xaaaaabe9869f _Z20command_line_handlerOSt10unique_ptrIcN3gdb13xfree_deleterIcEEE^M
        ../../../repos/binutils-gdb/gdb/event-top.c:788^M
0xaaaaabe97f17 gdb_rl_callback_handler^M
        ../../../repos/binutils-gdb/gdb/event-top.c:259^M
0xaaaaac3ce363 rl_callback_read_char^M
        ../../../../repos/binutils-gdb/readline/readline/callback.c:290^M
0xaaaaabe96e93 gdb_rl_callback_read_char_wrapper_noexcept^M
        ../../../repos/binutils-gdb/gdb/event-top.c:195^M
0xaaaaabe97d7b gdb_rl_callback_read_char_wrapper^M
        ../../../repos/binutils-gdb/gdb/event-top.c:234^M
0xaaaaac2ea1f7 stdin_event_handler^M
        ../../../repos/binutils-gdb/gdb/ui.c:155^M
0xaaaaac860ecf gdb_wait_for_event^M
        ../../../repos/binutils-gdb/gdbsupport/event-loop.cc:694^M
0xaaaaac8618af gdb_wait_for_event^M
        ../../../repos/binutils-gdb/gdbsupport/event-loop.cc:593^M
0xaaaaac8618af _Z16gdb_do_one_eventi^M
        ../../../repos/binutils-gdb/gdbsupport/event-loop.cc:264^M
0xaaaaabffe02f start_event_loop^M
        ../../../repos/binutils-gdb/gdb/main.c:407^M
0xaaaaabffe02f captured_command_loop^M
        ../../../repos/binutils-gdb/gdb/main.c:471^M
0xaaaaabfffc73 captured_main^M
        ../../../repos/binutils-gdb/gdb/main.c:1324^M
0xaaaaabfffc73 _Z8gdb_mainP18captured_main_args^M
        ../../../repos/binutils-gdb/gdb/main.c:1343^M
0xaaaaabc3b457 main^M
        ../../../repos/binutils-gdb/gdb/gdb.c:39^M

It is also worth noting that the v registers are *not* pseudo-registers when the aarch64-fpu feature is used. If SVE
is available though, then that means the v registers are pseudo-registers as well. You can tell them apart by their types.

Real register (no SVE support):

type = union aarch64v {
    vnd d;
    vns s;
    vnh h;
    vnb b;
    vnq q;
}

Pseudo-register (SVE support):

union __gdb_builtin_type_vnv {
    __gdb_builtin_type_vnd d;
    __gdb_builtin_type_vns s;
    __gdb_builtin_type_vnh h;
    __gdb_builtin_type_vnb b;
    __gdb_builtin_type_vnq q;
}

Please let me know if you want me to narrow this down or provide more info.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08 11:57 ` Luis Machado
@ 2023-11-08 15:47   ` Simon Marchi
  2023-11-08 17:08     ` Luis Machado
  0 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08 15:47 UTC (permalink / raw)
  To: Luis Machado, gdb-patches

> I haven't tracked the particular patch that causes this, but for aarch64 systems supporting SVE
> (and probably SME as well) I'm seeing internal errors related to one assertion:
Thanks a lot for testing.

> Running gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp ...
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to callee
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $v8.q.u
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $s8.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $s8.u
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: up
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $v8.q.u
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $s8.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x44454647 (GDB internal error)
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: down
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $s8.u
> PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
> Running gdb/testsuite/gdb.arch/aarch64-fp.exp ...
> PASS: gdb.arch/aarch64-fp.exp: set the breakpoint after setting the fp registers
> PASS: gdb.arch/aarch64-fp.exp: continue until breakpoint
> PASS: gdb.arch/aarch64-fp.exp: check register q0 value
> PASS: gdb.arch/aarch64-fp.exp: check register q1 value
> PASS: gdb.arch/aarch64-fp.exp: check register v0 value
> PASS: gdb.arch/aarch64-fp.exp: check register v1 value
> PASS: gdb.arch/aarch64-fp.exp: check register fpsr value
> PASS: gdb.arch/aarch64-fp.exp: check register fpcr value
> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set h0.bf to 129 (GDB internal error)
> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid
> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 0 (GDB internal error)
> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 0
> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 129 (GDB internal error)
> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 129
> 
> The failure mode is:
> 
> set $s8.u = 0x34353637^M
> ../../../repos/binutils-gdb/gdb/frame.c:1441: internal-error: put_frame_register: Assertion `buf.size () == size' failed.^M
> A problem internal to GDB has been detected,^M
> further debugging may prove unreliable.^M
> ----- Backtrace -----^M
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
> Resyncing due to internal error.
> 0xaaaaabd6e08f gdb_internal_backtrace_1^M
>         ../../../repos/binutils-gdb/gdb/bt-utils.c:122^M
> 0xaaaaabd6e08f _Z22gdb_internal_backtracev^M
>         ../../../repos/binutils-gdb/gdb/bt-utils.c:168^M
> 0xaaaaac315307 internal_vproblem^M
>         ../../../repos/binutils-gdb/gdb/utils.c:396^M
> 0xaaaaac31556f _Z15internal_verrorPKciS0_St9__va_list^M
>         ../../../repos/binutils-gdb/gdb/utils.c:476^M
> 0xaaaaac8604e3 _Z18internal_error_locPKciS0_z^M
>         ../../../repos/binutils-gdb/gdbsupport/errors.cc:58^M
> 0xaaaaabec89c7 _Z18put_frame_register14frame_info_ptriN3gdb10array_viewIKhEE^M
>         ../../../repos/binutils-gdb/gdb/frame.c:1441^M
> 0xaaaaabc55243 aarch64_pseudo_write_1^M
>         ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3316^M
> 0xaaaaabc5c943 aarch64_pseudo_write^M
>         ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3414^M

Ok, then I presume the regression is introduced with patch "gdb: migrate
aarch64 to new gdbarch_pseudo_register_write".

> It is also worth noting that the v registers are *not* pseudo-registers when the aarch64-fpu feature is used. If SVE
> is available though, then that means the v registers are pseudo-registers as well. You can tell them apart by their types.
> 
> Real register (no SVE support):
> 
> type = union aarch64v {
>     vnd d;
>     vns s;
>     vnh h;
>     vnb b;
>     vnq q;
> }
> 
> Pseudo-register (SVE support):
> 
> union __gdb_builtin_type_vnv {
>     __gdb_builtin_type_vnd d;
>     __gdb_builtin_type_vns s;
>     __gdb_builtin_type_vnh h;
>     __gdb_builtin_type_vnb b;
>     __gdb_builtin_type_vnq q;
> }

Ok, thanks for that explanation.  If you recall, I asked you on IRC if
the V registers were always 16 bytes long, and you said yes.  That's why
I hardcoded the size of 16 in aarch64_pseudo_write_1.  But then
according to your explanation, when SVE is available, the raw register
behind S registers, for instance, is an SVE register whose length is
unknown at compile time.  That's why the existing code calls
register_size to get the size of the raw register.

If you apply the patch below, does it help?

Simon


diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 1815d78dec40..87e6f9e10ae2 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3300,7 +3300,7 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
 			int regnum_offset,
 			gdb::array_view<const gdb_byte> buf)
 {
-  unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
+  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
   gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);

   /* Enough space for a full vector register.
@@ -3309,11 +3309,11 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
      various 'scalar' pseudo registers to behavior like architectural
      writes, register width bytes are written the remainder are set to
      zero.  */
-  constexpr int raw_reg_size = 16;
-  gdb_byte raw_buf[raw_reg_size] {};
+  int raw_reg_size = register_size (gdbarch, raw_regnum);
+  gdb::byte_vector raw_buf (raw_reg_size);
   gdb::array_view<gdb_byte> raw_view (raw_buf);
   copy (buf, raw_view.slice (0, buf.size ()));
-  put_frame_register (next_frame, v_regnum, raw_view);
+  put_frame_register (next_frame, raw_regnum, raw_view);
 }

 /* Given REGNUM, a SME pseudo-register number, store the bytes from DATA to the


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08 15:47   ` Simon Marchi
@ 2023-11-08 17:08     ` Luis Machado
  2023-11-08 19:34       ` Simon Marchi
  0 siblings, 1 reply; 51+ messages in thread
From: Luis Machado @ 2023-11-08 17:08 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

On 11/8/23 15:47, Simon Marchi wrote:
>> I haven't tracked the particular patch that causes this, but for aarch64 systems supporting SVE
>> (and probably SME as well) I'm seeing internal errors related to one assertion:
> Thanks a lot for testing.
> 
>> Running gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp ...
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to callee
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $v8.q.u
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $s8.u
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $s8.u
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: up
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $v8.q.u
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $s8.u
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x44454647 (GDB internal error)
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: down
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $s8.u
>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
>> Running gdb/testsuite/gdb.arch/aarch64-fp.exp ...
>> PASS: gdb.arch/aarch64-fp.exp: set the breakpoint after setting the fp registers
>> PASS: gdb.arch/aarch64-fp.exp: continue until breakpoint
>> PASS: gdb.arch/aarch64-fp.exp: check register q0 value
>> PASS: gdb.arch/aarch64-fp.exp: check register q1 value
>> PASS: gdb.arch/aarch64-fp.exp: check register v0 value
>> PASS: gdb.arch/aarch64-fp.exp: check register v1 value
>> PASS: gdb.arch/aarch64-fp.exp: check register fpsr value
>> PASS: gdb.arch/aarch64-fp.exp: check register fpcr value
>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set h0.bf to 129 (GDB internal error)
>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid
>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 0 (GDB internal error)
>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 0
>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 129 (GDB internal error)
>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 129
>>
>> The failure mode is:
>>
>> set $s8.u = 0x34353637^M
>> ../../../repos/binutils-gdb/gdb/frame.c:1441: internal-error: put_frame_register: Assertion `buf.size () == size' failed.^M
>> A problem internal to GDB has been detected,^M
>> further debugging may prove unreliable.^M
>> ----- Backtrace -----^M
>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
>> Resyncing due to internal error.
>> 0xaaaaabd6e08f gdb_internal_backtrace_1^M
>>         ../../../repos/binutils-gdb/gdb/bt-utils.c:122^M
>> 0xaaaaabd6e08f _Z22gdb_internal_backtracev^M
>>         ../../../repos/binutils-gdb/gdb/bt-utils.c:168^M
>> 0xaaaaac315307 internal_vproblem^M
>>         ../../../repos/binutils-gdb/gdb/utils.c:396^M
>> 0xaaaaac31556f _Z15internal_verrorPKciS0_St9__va_list^M
>>         ../../../repos/binutils-gdb/gdb/utils.c:476^M
>> 0xaaaaac8604e3 _Z18internal_error_locPKciS0_z^M
>>         ../../../repos/binutils-gdb/gdbsupport/errors.cc:58^M
>> 0xaaaaabec89c7 _Z18put_frame_register14frame_info_ptriN3gdb10array_viewIKhEE^M
>>         ../../../repos/binutils-gdb/gdb/frame.c:1441^M
>> 0xaaaaabc55243 aarch64_pseudo_write_1^M
>>         ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3316^M
>> 0xaaaaabc5c943 aarch64_pseudo_write^M
>>         ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3414^M
> 
> Ok, then I presume the regression is introduced with patch "gdb: migrate
> aarch64 to new gdbarch_pseudo_register_write".
> 
>> It is also worth noting that the v registers are *not* pseudo-registers when the aarch64-fpu feature is used. If SVE
>> is available though, then that means the v registers are pseudo-registers as well. You can tell them apart by their types.
>>
>> Real register (no SVE support):
>>
>> type = union aarch64v {
>>     vnd d;
>>     vns s;
>>     vnh h;
>>     vnb b;
>>     vnq q;
>> }
>>
>> Pseudo-register (SVE support):
>>
>> union __gdb_builtin_type_vnv {
>>     __gdb_builtin_type_vnd d;
>>     __gdb_builtin_type_vns s;
>>     __gdb_builtin_type_vnh h;
>>     __gdb_builtin_type_vnb b;
>>     __gdb_builtin_type_vnq q;
>> }
> 
> Ok, thanks for that explanation.  If you recall, I asked you on IRC if
> the V registers were always 16 bytes long, and you said yes.  That's why
> I hardcoded the size of 16 in aarch64_pseudo_write_1.  But then

Right. The V registers are always 16 bytes long, being real or pseudo-registers. So
their sizes are fixed.

The other pseudo-registers are also fixed size. What chages is the size of the SVE
register the V/Q/D/S/H/B pseudo-registers map to.

> according to your explanation, when SVE is available, the raw register
> behind S registers, for instance, is an SVE register whose length is
> unknown at compile time.  That's why the existing code calls
> register_size to get the size of the raw register.
> 
> If you apply the patch below, does it help?

It gets rid of the internal error due to the assertion, but I still see FAIL's due to wrong
values being printed.

FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid

p/x $v8.q.u
$3 = {0xaaab34353637}
(gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
--
p/x $v8.q.u
$7 = {0xaaab34353637}
(gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
--
p/x $s8.u
$8 = 0x34353637
(gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
--
p/x $v8.q.u
$9 = {0xaaab34353637}
(gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
--
Program received signal SIGSEGV, Segmentation fault.
0x0000aaab0fad6e60 in ?? ()
(gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
p/x value
No symbol "value" in current context.
(gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
testcase binutils-gdb/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp completed in 1 seconds
Running binutils-gdb/gdb/testsuite/gdb.arch/aarch64-fp.exp ...
--
p $h0
$7 = {bf = 1.136e-28, f = 0.00061798, u = 4368, s = 4368}
(gdb) FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid

> 
> Simon
> 
> 
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 1815d78dec40..87e6f9e10ae2 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -3300,7 +3300,7 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>  			int regnum_offset,
>  			gdb::array_view<const gdb_byte> buf)
>  {
> -  unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
> +  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
>    gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
> 
>    /* Enough space for a full vector register.
> @@ -3309,11 +3309,11 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>       various 'scalar' pseudo registers to behavior like architectural
>       writes, register width bytes are written the remainder are set to
>       zero.  */
> -  constexpr int raw_reg_size = 16;
> -  gdb_byte raw_buf[raw_reg_size] {};
> +  int raw_reg_size = register_size (gdbarch, raw_regnum);
> +  gdb::byte_vector raw_buf (raw_reg_size);
>    gdb::array_view<gdb_byte> raw_view (raw_buf);
>    copy (buf, raw_view.slice (0, buf.size ()));
> -  put_frame_register (next_frame, v_regnum, raw_view);
> +  put_frame_register (next_frame, raw_regnum, raw_view);
>  }
> 
>  /* Given REGNUM, a SME pseudo-register number, store the bytes from DATA to the
> 


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08 17:08     ` Luis Machado
@ 2023-11-08 19:34       ` Simon Marchi
  2023-11-09 19:04         ` Simon Marchi
  0 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-08 19:34 UTC (permalink / raw)
  To: Luis Machado, gdb-patches

On 11/8/23 12:08, Luis Machado wrote:
> On 11/8/23 15:47, Simon Marchi wrote:
>>> I haven't tracked the particular patch that causes this, but for aarch64 systems supporting SVE
>>> (and probably SME as well) I'm seeing internal errors related to one assertion:
>> Thanks a lot for testing.
>>
>>> Running gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp ...
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to callee
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $v8.q.u
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: callee, before change: p/x $s8.u
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $s8.u
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: up
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $v8.q.u
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: caller, before change: p/x $s8.u
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x44454647 (GDB internal error)
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: down
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $s8.u
>>> PASS: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
>>> Running gdb/testsuite/gdb.arch/aarch64-fp.exp ...
>>> PASS: gdb.arch/aarch64-fp.exp: set the breakpoint after setting the fp registers
>>> PASS: gdb.arch/aarch64-fp.exp: continue until breakpoint
>>> PASS: gdb.arch/aarch64-fp.exp: check register q0 value
>>> PASS: gdb.arch/aarch64-fp.exp: check register q1 value
>>> PASS: gdb.arch/aarch64-fp.exp: check register v0 value
>>> PASS: gdb.arch/aarch64-fp.exp: check register v1 value
>>> PASS: gdb.arch/aarch64-fp.exp: check register fpsr value
>>> PASS: gdb.arch/aarch64-fp.exp: check register fpcr value
>>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set h0.bf to 129 (GDB internal error)
>>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid
>>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 0 (GDB internal error)
>>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 0
>>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: set v0.h.bf[0] to 129 (GDB internal error)
>>> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: v0.h.s[0] is 129
>>>
>>> The failure mode is:
>>>
>>> set $s8.u = 0x34353637^M
>>> ../../../repos/binutils-gdb/gdb/frame.c:1441: internal-error: put_frame_register: Assertion `buf.size () == size' failed.^M
>>> A problem internal to GDB has been detected,^M
>>> further debugging may prove unreliable.^M
>>> ----- Backtrace -----^M
>>> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: set $s8.u = 0x34353637 (GDB internal error)
>>> Resyncing due to internal error.
>>> 0xaaaaabd6e08f gdb_internal_backtrace_1^M
>>>         ../../../repos/binutils-gdb/gdb/bt-utils.c:122^M
>>> 0xaaaaabd6e08f _Z22gdb_internal_backtracev^M
>>>         ../../../repos/binutils-gdb/gdb/bt-utils.c:168^M
>>> 0xaaaaac315307 internal_vproblem^M
>>>         ../../../repos/binutils-gdb/gdb/utils.c:396^M
>>> 0xaaaaac31556f _Z15internal_verrorPKciS0_St9__va_list^M
>>>         ../../../repos/binutils-gdb/gdb/utils.c:476^M
>>> 0xaaaaac8604e3 _Z18internal_error_locPKciS0_z^M
>>>         ../../../repos/binutils-gdb/gdbsupport/errors.cc:58^M
>>> 0xaaaaabec89c7 _Z18put_frame_register14frame_info_ptriN3gdb10array_viewIKhEE^M
>>>         ../../../repos/binutils-gdb/gdb/frame.c:1441^M
>>> 0xaaaaabc55243 aarch64_pseudo_write_1^M
>>>         ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3316^M
>>> 0xaaaaabc5c943 aarch64_pseudo_write^M
>>>         ../../../repos/binutils-gdb/gdb/aarch64-tdep.c:3414^M
>>
>> Ok, then I presume the regression is introduced with patch "gdb: migrate
>> aarch64 to new gdbarch_pseudo_register_write".
>>
>>> It is also worth noting that the v registers are *not* pseudo-registers when the aarch64-fpu feature is used. If SVE
>>> is available though, then that means the v registers are pseudo-registers as well. You can tell them apart by their types.
>>>
>>> Real register (no SVE support):
>>>
>>> type = union aarch64v {
>>>     vnd d;
>>>     vns s;
>>>     vnh h;
>>>     vnb b;
>>>     vnq q;
>>> }
>>>
>>> Pseudo-register (SVE support):
>>>
>>> union __gdb_builtin_type_vnv {
>>>     __gdb_builtin_type_vnd d;
>>>     __gdb_builtin_type_vns s;
>>>     __gdb_builtin_type_vnh h;
>>>     __gdb_builtin_type_vnb b;
>>>     __gdb_builtin_type_vnq q;
>>> }
>>
>> Ok, thanks for that explanation.  If you recall, I asked you on IRC if
>> the V registers were always 16 bytes long, and you said yes.  That's why
>> I hardcoded the size of 16 in aarch64_pseudo_write_1.  But then
> 
> Right. The V registers are always 16 bytes long, being real or pseudo-registers. So
> their sizes are fixed.
> 
> The other pseudo-registers are also fixed size. What chages is the size of the SVE
> register the V/Q/D/S/H/B pseudo-registers map to.
> 
>> according to your explanation, when SVE is available, the raw register
>> behind S registers, for instance, is an SVE register whose length is
>> unknown at compile time.  That's why the existing code calls
>> register_size to get the size of the raw register.
>>
>> If you apply the patch below, does it help?
> 
> It gets rid of the internal error due to the assertion, but I still see FAIL's due to wrong
> values being printed.
> 
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
> FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid
> 
> p/x $v8.q.u
> $3 = {0xaaab34353637}
> (gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change: p/x $v8.q.u
> --
> p/x $v8.q.u
> $7 = {0xaaab34353637}
> (gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
> --
> p/x $s8.u
> $8 = 0x34353637
> (gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
> --
> p/x $v8.q.u
> $9 = {0xaaab34353637}
> (gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: callee, after change in caller: p/x $v8.q.u
> --
> Program received signal SIGSEGV, Segmentation fault.
> 0x0000aaab0fad6e60 in ?? ()
> (gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
> p/x value
> No symbol "value" in current context.
> (gdb) FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
> testcase binutils-gdb/gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp completed in 1 seconds
> Running binutils-gdb/gdb/testsuite/gdb.arch/aarch64-fp.exp ...
> --
> p $h0
> $7 = {bf = 1.136e-28, f = 0.00061798, u = 4368, s = 4368}
> (gdb) FAIL: gdb.arch/aarch64-fp.exp: bfloat16: h0 fields are valid


Ah, damn, probably because I switched to byte_vector,  which doesn't do
the zero-initialization we want to do.  Here's a new patch (that applies
on the series directly) that doesn't use byte_vector.

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 1815d78dec4..200e740e013 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3300,7 +3300,7 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
                        int regnum_offset,
                        gdb::array_view<const gdb_byte> buf)
 {
-  unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
+  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
   gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);

   /* Enough space for a full vector register.
@@ -3309,11 +3309,11 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
      various 'scalar' pseudo registers to behavior like architectural
      writes, register width bytes are written the remainder are set to
      zero.  */
-  constexpr int raw_reg_size = 16;
+  int raw_reg_size = register_size (gdbarch, raw_regnum);
   gdb_byte raw_buf[raw_reg_size] {};
-  gdb::array_view<gdb_byte> raw_view (raw_buf);
+  gdb::array_view<gdb_byte> raw_view (raw_buf, raw_reg_size);
   copy (buf, raw_view.slice (0, buf.size ()));
-  put_frame_register (next_frame, v_regnum, raw_view);
+  put_frame_register (next_frame, raw_regnum, raw_view);
 }

 /* Given REGNUM, a SME pseudo-register number, store the bytes from DATA to the

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08  5:16 ` [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
@ 2023-11-09  3:05   ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-09  3:05 UTC (permalink / raw)
  To: gdb-patches



On 2023-11-08 00:16, Simon Marchi wrote:
> 
> 
> On 2023-11-08 00:00, Simon Marchi wrote:
>> This series fixes reading/writing pseudo registers from/to non-current
>> frames (that is, frames other than frame 0).  Currently, we get this:
>>
>>     (gdb) frame 0
>>     #0  break_here_asm () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:38
>>     38              pop %rbx
>>     (gdb) p/x $rbx
>>     $1 = 0x2021222324252627
>>     (gdb) p/x $ebx
>>     $2 = 0x24252627
>>     (gdb) frame 1
>>     #1  0x000055555555517d in caller () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:58
>>     58              call callee
>>     (gdb) p/x $rbx
>>     $3 = 0x1011121314151617
>>     (gdb) p/x $ebx
>>     $4 = 0x24252627
>>
>> This is a bit surprising, we would expect the last value to be
>> 0x14151617, the bottom half of the rbx value from frame 1 (the currently
>> selected frame at that point).  Instead, we got the bottom half of the
>> rbx value from frame 0.  This is because pseudo registers are always
>> read/written from/to the current thread's regcache.
>>
>> This series fixes this (as well as writing to pseudo registers) by
>> making it so pseudo registers are read/written using a frame.
> 
> Ah, I forgot because it's been so long, but this can be considered a v3
> of this series  here...
> 
>   https://inbox.sourceware.org/gdb-patches/20181024014333.14143-1-simon.marchi@polymtl.ca/
> 
> Simon

A bit more information that I realized I left out: this series fixes the
behavior for AArch64, ARM and AMD64/i386, because those are the three
architectures I had tests already written for.  It shouldn't be too hard
to update other architectures, the slightly more difficult part is to
write a test, since it involves writing assembly and CFI directives, not
something most people do everyday.

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08 19:34       ` Simon Marchi
@ 2023-11-09 19:04         ` Simon Marchi
  2023-11-13 13:10           ` Luis Machado
  0 siblings, 1 reply; 51+ messages in thread
From: Simon Marchi @ 2023-11-09 19:04 UTC (permalink / raw)
  To: Luis Machado, gdb-patches

On 11/8/23 14:34, Simon Marchi wrote:
> Ah, damn, probably because I switched to byte_vector,  which doesn't do
> the zero-initialization we want to do.  Here's a new patch (that applies
> on the series directly) that doesn't use byte_vector.
> 
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 1815d78dec4..200e740e013 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -3300,7 +3300,7 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>                         int regnum_offset,
>                         gdb::array_view<const gdb_byte> buf)
>  {
> -  unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
> +  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
>    gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
> 
>    /* Enough space for a full vector register.
> @@ -3309,11 +3309,11 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>       various 'scalar' pseudo registers to behavior like architectural
>       writes, register width bytes are written the remainder are set to
>       zero.  */
> -  constexpr int raw_reg_size = 16;
> +  int raw_reg_size = register_size (gdbarch, raw_regnum);
>    gdb_byte raw_buf[raw_reg_size] {};
> -  gdb::array_view<gdb_byte> raw_view (raw_buf);
> +  gdb::array_view<gdb_byte> raw_view (raw_buf, raw_reg_size);
>    copy (buf, raw_view.slice (0, buf.size ()));
> -  put_frame_register (next_frame, v_regnum, raw_view);
> +  put_frame_register (next_frame, raw_regnum, raw_view);
>  }
> 
>  /* Given REGNUM, a SME pseudo-register number, store the bytes from DATA to the
> 
> Simon

I managed to run a Debian AArch64 image in qemu, with SVE support, so I
was able to reproduce the failures you mentioned.  In the end, here's a
version of aarch64_pseudo_write_1 that works for me (written as to
minimize the number of unnecessary changes, since that seems to
introduce unexpected bugs...).

static void
aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
			int regnum_offset,
			gdb::array_view<const gdb_byte> buf)
{
  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;

  /* Enough space for a full vector register.  */
  int raw_reg_size = register_size (gdbarch, raw_regnum);
  gdb_byte raw_buf[raw_reg_size];
  gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);

  /* Ensure the register buffer is zero, we want gdb writes of the
     various 'scalar' pseudo registers to behavior like architectural
     writes, register width bytes are written the remainder are set to
     zero.  */
  memset (raw_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));

  gdb::array_view<gdb_byte> raw_view (raw_buf, raw_reg_size);
  copy (buf, raw_view.slice (0, buf.size ()));
  put_frame_register (next_frame, raw_regnum, raw_view);
}

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers
  2023-11-08  5:00 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
@ 2023-11-11 19:29   ` John Baldwin
  0 siblings, 0 replies; 51+ messages in thread
From: John Baldwin @ 2023-11-11 19:29 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

On 11/7/23 9:00 PM, Simon Marchi wrote:
> From: Simon Marchi <simon.marchi@efficios.com>
> 
> I think that i386 k registers are raw registers, and therefore shouldn't
> be handled in the various functions handling pseudo registers.
> 
> What tipped me off is the code in i386_pseudo_register_read_into_value:
> 
>        else if (i386_k_regnum_p (gdbarch, regnum))
> 	{
> 	  regnum -= tdep->k0_regnum;
> 
> 	  /* Extract (always little endian).  */
> 	  status = regcache->raw_read (tdep->k0_regnum + regnum, raw_buf);
> 
> We take regnum (the pseudo register number we want to read), subtract
> k0_regnum, add k0_regnum, and pass the result to raw_read.  So we would
> end up calling raw_read with the same regnum as the function received
> which is supposedly a pseudo register number.
> 
> Other hints are:
> 
>   - The command `maint print raw-registers` shows the k registers.
>   - Printing $k0 doesn't cause i386_pseudo_register_read_into_value to be
>     called.
>   - There's code in i387-tdep.c to save/restore the k registers.

Also, the bottom of i386_gdbarch_init (where all of the pseudo register numbers
are allocated) doesn't allocate any pseudo register numbers for the k registers.
I think this bit of code in i386_gdbarch_init is the definitive set of what
counts as pseudo registers on i386/amd64.
  
> Remove handling of the k registers from:
> 
>   - i386_pseudo_register_read_into_value
>   - i386_pseudo_register_write
>   - i386_ax_pseudo_register_collect

Reviewed-by: John Baldwin <jhb@FreeBSD.org>

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h
  2023-11-08  5:00 ` [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h Simon Marchi
@ 2023-11-11 19:42   ` John Baldwin
  0 siblings, 0 replies; 51+ messages in thread
From: John Baldwin @ 2023-11-11 19:42 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

On 11/7/23 9:00 PM, Simon Marchi wrote:
> From: Simon Marchi <simon.marchi@efficios.com>
> 
> Right now, gdbsupport/common-regcache.h contains two abstractons for a
> regcache.  An opaque type `regcache` (gdb and gdbserver both have their
> own regcache that is the concrete version of this) and an abstract base
> class `reg_buffer_common`, that is the base of regcaches on both sides.
> These abstractions allow code to be written for both gdb and gdbserver,
> for instance in the gdb/arch sub-directory.
> 
> However, having two
> different abstractions is impractical.  If some common code has a regcache,
> and wants to use an operation defined on reg_buffer_common, it can't.
> It would be better to have just one.  Change all instances of `regcache
> *` in gdbsupport/common-regcache.h to be `reg_buffer_common *`, then fix
> fallouts.
> 
> Implementations in gdb and gdbserver now need to down-cast (using
> gdb::checked_static_cast) from reg_buffer_common to their concrete
> regcache type.  Some of them could be avoided by changing free functions
> (like regcache_register_size) to be virtual methods on
> reg_buffer_common.  I tried it, it seems to work, but I did not include
> it in this series to avoid adding unnecessary changes.

I agree that just using reg_buffer_common in shared code is cleaner.
Adding more virtual methods to remove static_casts in a followup seems
sensible, but definitely fine to defer that to a separate followup.

Reviewed-by: John Baldwin <jhb@FreeBSD.org>

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 14/24] gdb: read pseudo register through frame
  2023-11-08  5:00 ` [PATCH 14/24] gdb: read pseudo register through frame Simon Marchi
@ 2023-11-11 20:11   ` John Baldwin
  0 siblings, 0 replies; 51+ messages in thread
From: John Baldwin @ 2023-11-11 20:11 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

On 11/7/23 9:00 PM, Simon Marchi wrote:
> From: Simon Marchi <simon.marchi@efficios.com>
> 
> Change gdbarch_pseudo_register_read_value to take a frame instead of a
> regcache.  The frame (and formerly the regcache) is used to read raw
> registers needed to make up the pseudo register value.  The problem with
> using the regcache is that it always provides raw register values for
> the current frame (frame 0).
> 
> Let's say the user wants to read the ebx register on amd64.  ebx is a pseudo
> register, obtained by reading the bottom half (bottom 4 bytes) of the
> rbx register, which is a raw register.  If the currently selected frame
> is frame 0, it works fine:
> 
>      (gdb) frame 0
>      #0  break_here_asm () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:36
>      36      in /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
>      (gdb) p/x $ebx
>      $1 = 0x24252627
>      (gdb) p/x $rbx
>      $2 = 0x2021222324252627
> 
> But if the user is looking at another frame, and the raw register behind
> the pseudo register has been saved at some point in the call stack, then
> we get a wrong answer:
> 
>      (gdb) frame 1
>      #1  0x000055555555517d in caller () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:56
>      56      in /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
>      (gdb) p/x $ebx
>      $3 = 0x24252627
>      (gdb) p/x $rbx
>      $4 = 0x1011121314151617
> 
> Here, the value of ebx was computed using the value of rbx in frame 0
> (through the regcache), it should have been computed using the value of
> rbx in frame 1.
> 
> In other to make this work properly, make the following changes:
> 
>   - Make dwarf2_frame_prev_register return nullptr if it doesn't know how
>     to unwind a register and that register is a pseudo register.
>     Previously, it returned `frame_unwind_got_register`, meaning, in our
>     example, "the value of ebx in frame 1 is the same as the value of ebx
>     in frame 0", which is obviously false.  Return nullptr as a way to
>     say "I don't know".
> 
>   - In frame_unwind_register_value, when prev_register (for instance
>     dwarf2_frame_prev_register) returns nullptr, and we are trying to
>     read a pseudo register, try to get the register value through
>     gdbarch_pseudo_register_read_value or gdbarch_pseudo_register_read.
>     If using gdbarch_pseudo_register_read, the behavior is known to be
>     broken.  Implementations should be migrated to use
>     gdbarch_pseudo_register_read_value to fix that.
> 
>   - Change gdbarch_pseudo_register_read_value to take a frame_info
>     instead of a regcache, update implementations (aarch64, amd64, i386).
>     In i386-tdep.c, I made a copy of i386_mmx_regnum_to_fp_regnum that
>     uses a frame instead of a regcache.  The version using the regcache
>     is still used by i386_pseudo_register_write.  It will get removed in
>     a subsequent patch.
> 
>   - Add some helpers in value.{c,h} to implement the common cases of
>     pseudo registers: taking part of a raw register and concatenating
>     multiple raw registers.

These are quite a nice change and reduce a lot of the copy/paste that would
otherwise be present.

>   - Update readable_regcache::{cooked_read,cooked_read_value} to pass the
>     current frame to gdbarch_pseudo_register_read_value.  Passing the
>     current frame will give the same behavior as before: for frame 0, raw
>     registers will be read from the current thread's regcache.
> 
> Notes:
> 
>   - I do not plan on changing gdbarch_pseudo_register_read to receive a
>     frame instead of a regcache. That method is considered deprecated.
>     Instead, we should be working on migrating implementations to use
>     gdbarch_pseudo_register_read_value instead.
> 
>   - In frame_unwind_register_value, we still ask the unwinder to try to
>     unwind pseudo register values.  It's apparently possible for the
>     debug info to provide information about [1] pseudo registers, so we
>     want to try that first, before falling back to computing them
>     ourselves.

Only updating read_value to use a frame seems sensible to me as a plan.

I had to stare at the update to handle reading the BND pseudo registers
for i386 for a while, but it does look correct to me.

Reviewed-by: John Baldwin <jhb@FreeBSD.org>

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write
  2023-11-08  5:01 ` [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write Simon Marchi
@ 2023-11-11 20:16   ` John Baldwin
  2023-11-13  2:59     ` Simon Marchi
  0 siblings, 1 reply; 51+ messages in thread
From: John Baldwin @ 2023-11-11 20:16 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

On 11/7/23 9:01 PM, Simon Marchi wrote:
> From: Simon Marchi <simon.marchi@efficios.com>
> 
> Make i386 and amd64 use the new gdbarch_pseudo_register_write.  This
> fixes writing to pseudo registers in non-current frames for those
> architectures.
> 
> Change-Id: I4977e8fe12d2cef116f8834c34cdf6fec618554f
> ---
>   gdb/amd64-tdep.c |  39 +++----------
>   gdb/i386-tdep.c  | 146 +++++++++++++++++------------------------------
>   gdb/i386-tdep.h  |   6 +-
>   gdb/value.c      |   3 +-
>   gdb/value.h      |   6 +-
>   5 files changed, 66 insertions(+), 134 deletions(-)
> 
> diff --git a/gdb/value.c b/gdb/value.c
> index 51dca972a587..5c18bb17a781 100644
> --- a/gdb/value.c
> +++ b/gdb/value.c
> @@ -4062,7 +4062,8 @@ pseudo_to_raw_part (frame_info_ptr next_frame,
>   		    gdb::array_view<const gdb_byte> pseudo_buf,
>   		    int raw_reg_num, int raw_offset)
>   {
> -  int raw_reg_size = register_size (get_frame_arch (next_frame), raw_reg_num);
> +  int raw_reg_size
> +    = register_size (frame_unwind_arch (next_frame), raw_reg_num);
>   
>     /* When overflowing a register, put_frame_register_bytes writes to the
>        subsequent registers.  We don't want that behavior here, so make sure
> diff --git a/gdb/value.h b/gdb/value.h
> index 6a74d4e2c2ee..3b3cb9c00982 100644
> --- a/gdb/value.h
> +++ b/gdb/value.h
> @@ -1664,7 +1664,7 @@ value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
>   /* Write PSEUDO_BUF, the contents of a pseudo register, to part of raw register
>      RAW_REG_NUM starting at RAW_OFFSET.  */
>   
> -void pseudo_to_raw_part (frame_info_ptr this_frame,
> +void pseudo_to_raw_part (frame_info_ptr next_frame,
>   			 gdb::array_view<const gdb_byte> pseudo_buf,
>   			 int raw_reg_num, int raw_offset);
>   
> @@ -1680,7 +1680,7 @@ value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>   /* Write PSEUDO_BUF, the contents of a pseudo register, to the two raw registers
>      RAW_REG_1_NUM and RAW_REG_2_NUM.  */
>   
> -void pseudo_to_concat_raw (frame_info_ptr this_frame,
> +void pseudo_to_concat_raw (frame_info_ptr next_frame,
>   			   gdb::array_view<const gdb_byte> pseudo_buf,
>   			   int raw_reg_1_num, int raw_reg_2_num);
>   
> @@ -1693,7 +1693,7 @@ value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>   /* Write PSEUDO_BUF, the contents of a pseudo register, to the tthreewo raw
>      registers RAW_REG_1_NUM, RAW_REG_2_NUM and RAW_REG_3_NUM.  */
>   
> -void pseudo_to_concat_raw (frame_info_ptr this_frame,
> +void pseudo_to_concat_raw (frame_info_ptr next_frame,
>   			   gdb::array_view<const gdb_byte> pseudo_buf,
>   			   int raw_reg_1_num, int raw_reg_2_num,
>   			   int raw_reg_3_num);

Should these value.[ch] changes be part of the previous patch (17) instead?

Otherwise, the amd64/i386 parts look good to me.

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (25 preceding siblings ...)
  2023-11-08 11:57 ` Luis Machado
@ 2023-11-11 20:26 ` John Baldwin
  2023-11-13  3:03   ` Simon Marchi
  26 siblings, 1 reply; 51+ messages in thread
From: John Baldwin @ 2023-11-11 20:26 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

On 11/7/23 9:00 PM, Simon Marchi wrote:
> This series fixes reading/writing pseudo registers from/to non-current
> frames (that is, frames other than frame 0).  Currently, we get this:
> 
>      (gdb) frame 0
>      #0  break_here_asm () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:38
>      38              pop %rbx
>      (gdb) p/x $rbx
>      $1 = 0x2021222324252627
>      (gdb) p/x $ebx
>      $2 = 0x24252627
>      (gdb) frame 1
>      #1  0x000055555555517d in caller () at /home/smarchi/src/binutils-gdb/gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S:58
>      58              call callee
>      (gdb) p/x $rbx
>      $3 = 0x1011121314151617
>      (gdb) p/x $ebx
>      $4 = 0x24252627
> 
> This is a bit surprising, we would expect the last value to be
> 0x14151617, the bottom half of the rbx value from frame 1 (the currently
> selected frame at that point).  Instead, we got the bottom half of the
> rbx value from frame 0.  This is because pseudo registers are always
> read/written from/to the current thread's regcache.
> 
> This series fixes this (as well as writing to pseudo registers) by
> making it so pseudo registers are read/written using a frame.
> 
> Simon Marchi (24):
>    gdb: don't handle i386 k registers as pseudo registers
>    gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h
>    gdb: make store_integer take an array_view
>    gdb: simplify conditions in
>      regcache::{read,write,raw_collect,raw_supply}_part
>    gdb: change regcache interface to use array_view
>    gdb: fix bugs in {get,put}_frame_register_bytes
>    gdb: make put_frame_register take an array_view
>    gdb: change value_of_register and value_of_register_lazy to take the
>      next frame
>    gdb: remove frame_register
>    gdb: make put_frame_register take the next frame
>    gdb: make put_frame_register_bytes take the next frame
>    gdb: make get_frame_register_bytes take the next frame
>    gdb: add value::allocate_register
>    gdb: read pseudo register through frame
>    gdb: change parameter name in frame_unwind_register_unsigned
>      declaration
>    gdb: rename gdbarch_pseudo_register_write to
>      gdbarch_deprecated_pseudo_register_write
>    gdb: add gdbarch_pseudo_register_write that takes a frame
>    gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write
>    gdb: make aarch64_za_offsets_from_regnum return za_offsets
>    gdb: add missing raw register read in
>      aarch64_sme_pseudo_register_write
>    gdb: migrate aarch64 to new gdbarch_pseudo_register_write
>    gdb: migrate arm to gdbarch_pseudo_register_read_value
>    gdb: migrate arm to new gdbarch_pseudo_register_write
>    gdb/testsuite: add tests for unwinding of pseudo registers
> 
>   gdb/aarch64-tdep.c                            | 297 +++++-----
>   gdb/alpha-tdep.c                              |  11 +-
>   gdb/amd64-tdep.c                              |  82 +--
>   gdb/arch/arm-get-next-pcs.c                   |   6 +-
>   gdb/arch/arm-get-next-pcs.h                   |   5 +-
>   gdb/arch/arm.c                                |   2 +-
>   gdb/arch/arm.h                                |   4 +-
>   gdb/arm-linux-tdep.c                          |  11 +-
>   gdb/arm-tdep.c                                | 145 +++--
>   gdb/avr-tdep.c                                |   3 +-
>   gdb/bfin-tdep.c                               |   3 +-
>   gdb/csky-tdep.c                               |   4 +-
>   gdb/defs.h                                    |  39 +-
>   gdb/dwarf2/expr.c                             |  22 +-
>   gdb/dwarf2/frame.c                            |   5 +-
>   gdb/eval.c                                    |   3 +-
>   gdb/findvar.c                                 |  50 +-
>   gdb/frame-unwind.c                            |   3 +-
>   gdb/frame.c                                   | 174 +++---
>   gdb/frame.h                                   |  28 +-
>   gdb/frv-tdep.c                                |   3 +-
>   gdb/gdbarch-gen.h                             |  28 +-
>   gdb/gdbarch.c                                 |  40 +-
>   gdb/gdbarch_components.py                     |  29 +-
>   gdb/guile/scm-frame.c                         |   3 +-
>   gdb/h8300-tdep.c                              |   3 +-
>   gdb/i386-tdep.c                               | 380 ++++--------
>   gdb/i386-tdep.h                               |  15 +-
>   gdb/i387-tdep.c                               |  16 +-
>   gdb/ia64-tdep.c                               |  18 +-
>   gdb/infcmd.c                                  |   6 +-
>   gdb/loongarch-tdep.c                          |   3 +-
>   gdb/m32c-tdep.c                               |   3 +-
>   gdb/m68hc11-tdep.c                            |   3 +-
>   gdb/m68k-tdep.c                               |  17 +-
>   gdb/mep-tdep.c                                |   3 +-
>   gdb/mi/mi-main.c                              |   3 +-
>   gdb/mips-tdep.c                               |  29 +-
>   gdb/msp430-tdep.c                             |   3 +-
>   gdb/nat/aarch64-hw-point.c                    |   3 +-
>   gdb/nat/aarch64-scalable-linux-ptrace.c       |  20 +-
>   gdb/nat/linux-btrace.c                        |   3 +-
>   gdb/nds32-tdep.c                              |   8 +-
>   gdb/python/py-frame.c                         |   3 +-
>   gdb/python/py-unwind.c                        |   4 +-
>   gdb/regcache.c                                | 548 +++++++++++-------
>   gdb/regcache.h                                | 113 +++-
>   gdb/riscv-tdep.c                              |  13 +-
>   gdb/rl78-tdep.c                               |   3 +-
>   gdb/rs6000-tdep.c                             |  21 +-
>   gdb/s12z-tdep.c                               |   2 +-
>   gdb/s390-tdep.c                               |   3 +-
>   gdb/sh-tdep.c                                 |   9 +-
>   gdb/sparc-tdep.c                              |   3 +-
>   gdb/sparc64-tdep.c                            |   3 +-
>   gdb/std-regs.c                                |  11 +-
>   .../gdb.arch/aarch64-pseudo-unwind-asm.S      |  82 +++
>   .../gdb.arch/aarch64-pseudo-unwind.c          |  33 ++
>   .../gdb.arch/aarch64-pseudo-unwind.exp        |  90 +++
>   .../gdb.arch/amd64-pseudo-unwind-asm.S        |  66 +++
>   gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c  |  33 ++
>   .../gdb.arch/amd64-pseudo-unwind.exp          |  91 +++
>   .../gdb.arch/arm-pseudo-unwind-asm.S          |  81 +++
>   .../gdb.arch/arm-pseudo-unwind-legacy-asm.S   |  84 +++
>   .../gdb.arch/arm-pseudo-unwind-legacy.c       |  33 ++
>   .../gdb.arch/arm-pseudo-unwind-legacy.exp     |  86 +++
>   gdb/testsuite/gdb.arch/arm-pseudo-unwind.c    |  33 ++
>   gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp  |  88 +++
>   gdb/valops.c                                  |  31 +-
>   gdb/value.c                                   | 149 +++++
>   gdb/value.h                                   |  64 +-
>   gdb/xtensa-tdep.c                             |   3 +-
>   gdbserver/linux-arm-low.cc                    |   4 +-
>   gdbserver/regcache.cc                         |  69 ++-
>   gdbserver/regcache.h                          |   6 +-
>   gdbsupport/common-regcache.cc                 |   2 +-
>   gdbsupport/common-regcache.h                  |  58 +-
>   gdbsupport/rsp-low.cc                         |   8 +
>   gdbsupport/rsp-low.h                          |   2 +
>   79 files changed, 2324 insertions(+), 1144 deletions(-)
>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind-asm.S
>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.c
>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-pseudo-unwind.exp
>   create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind-asm.S
>   create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.c
>   create mode 100644 gdb/testsuite/gdb.arch/amd64-pseudo-unwind.exp
>   create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
>   create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy-asm.S
>   create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.c
>   create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind-legacy.exp
>   create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.c
>   create mode 100644 gdb/testsuite/gdb.arch/arm-pseudo-unwind.exp
> 
> 
> base-commit: 1185b5b79a12ba67eb60bee3f75babf7a222fde0

I did not review the aarch64/arm changes very thoroughly (patches 19-23),
but the rest all look fine to me aside from the one comment I had on
patch 18.

(So to be clear, you can add my Reviewed-by on all of 1-18.)  Looks like
Luis is helping to validate the arm changes.

I certainly have a use case for this for CHERI support where GPRs are
also extended (so ideally I'd like to treat the 64-bit GPRs as pseudos
of the 129-bit capability registers), and also for Morello in particular
where the stack pointer is banked in userland and can vary by stack
frame depending on a permission in the PC as to which real register it
maps on to.

-- 
John Baldwin


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write
  2023-11-11 20:16   ` John Baldwin
@ 2023-11-13  2:59     ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-13  2:59 UTC (permalink / raw)
  To: John Baldwin, gdb-patches; +Cc: Simon Marchi

On 11/11/23 15:16, John Baldwin wrote:
> On 11/7/23 9:01 PM, Simon Marchi wrote:
>> From: Simon Marchi <simon.marchi@efficios.com>
>>
>> Make i386 and amd64 use the new gdbarch_pseudo_register_write.  This
>> fixes writing to pseudo registers in non-current frames for those
>> architectures.
>>
>> Change-Id: I4977e8fe12d2cef116f8834c34cdf6fec618554f
>> ---
>>   gdb/amd64-tdep.c |  39 +++----------
>>   gdb/i386-tdep.c  | 146 +++++++++++++++++------------------------------
>>   gdb/i386-tdep.h  |   6 +-
>>   gdb/value.c      |   3 +-
>>   gdb/value.h      |   6 +-
>>   5 files changed, 66 insertions(+), 134 deletions(-)
>>
>> diff --git a/gdb/value.c b/gdb/value.c
>> index 51dca972a587..5c18bb17a781 100644
>> --- a/gdb/value.c
>> +++ b/gdb/value.c
>> @@ -4062,7 +4062,8 @@ pseudo_to_raw_part (frame_info_ptr next_frame,
>>               gdb::array_view<const gdb_byte> pseudo_buf,
>>               int raw_reg_num, int raw_offset)
>>   {
>> -  int raw_reg_size = register_size (get_frame_arch (next_frame), raw_reg_num);
>> +  int raw_reg_size
>> +    = register_size (frame_unwind_arch (next_frame), raw_reg_num);
>>       /* When overflowing a register, put_frame_register_bytes writes to the
>>        subsequent registers.  We don't want that behavior here, so make sure
>> diff --git a/gdb/value.h b/gdb/value.h
>> index 6a74d4e2c2ee..3b3cb9c00982 100644
>> --- a/gdb/value.h
>> +++ b/gdb/value.h
>> @@ -1664,7 +1664,7 @@ value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
>>   /* Write PSEUDO_BUF, the contents of a pseudo register, to part of raw register
>>      RAW_REG_NUM starting at RAW_OFFSET.  */
>>   -void pseudo_to_raw_part (frame_info_ptr this_frame,
>> +void pseudo_to_raw_part (frame_info_ptr next_frame,
>>                gdb::array_view<const gdb_byte> pseudo_buf,
>>                int raw_reg_num, int raw_offset);
>>   @@ -1680,7 +1680,7 @@ value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>>   /* Write PSEUDO_BUF, the contents of a pseudo register, to the two raw registers
>>      RAW_REG_1_NUM and RAW_REG_2_NUM.  */
>>   -void pseudo_to_concat_raw (frame_info_ptr this_frame,
>> +void pseudo_to_concat_raw (frame_info_ptr next_frame,
>>                  gdb::array_view<const gdb_byte> pseudo_buf,
>>                  int raw_reg_1_num, int raw_reg_2_num);
>>   @@ -1693,7 +1693,7 @@ value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>>   /* Write PSEUDO_BUF, the contents of a pseudo register, to the tthreewo raw
>>      registers RAW_REG_1_NUM, RAW_REG_2_NUM and RAW_REG_3_NUM.  */
>>   -void pseudo_to_concat_raw (frame_info_ptr this_frame,
>> +void pseudo_to_concat_raw (frame_info_ptr next_frame,
>>                  gdb::array_view<const gdb_byte> pseudo_buf,
>>                  int raw_reg_1_num, int raw_reg_2_num,
>>                  int raw_reg_3_num);
> 
> Should these value.[ch] changes be part of the previous patch (17) instead?

Oh, you're right, will fix.

> Otherwise, the amd64/i386 parts look good to me.

Thanks,

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-11 20:26 ` John Baldwin
@ 2023-11-13  3:03   ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-13  3:03 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

On 11/11/23 15:26, John Baldwin wrote:
> I did not review the aarch64/arm changes very thoroughly (patches 19-23),
> but the rest all look fine to me aside from the one comment I had on
> patch 18.
> 
> (So to be clear, you can add my Reviewed-by on all of 1-18.)  Looks like
> Luis is helping to validate the arm changes.

Thanks for reviewing, will add.

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-09 19:04         ` Simon Marchi
@ 2023-11-13 13:10           ` Luis Machado
  2023-11-13 15:08             ` Luis Machado
  0 siblings, 1 reply; 51+ messages in thread
From: Luis Machado @ 2023-11-13 13:10 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

Simon,

On 11/9/23 19:04, Simon Marchi wrote:
> On 11/8/23 14:34, Simon Marchi wrote:
>> Ah, damn, probably because I switched to byte_vector,  which doesn't do
>> the zero-initialization we want to do.  Here's a new patch (that applies
>> on the series directly) that doesn't use byte_vector.
>>
>> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
>> index 1815d78dec4..200e740e013 100644
>> --- a/gdb/aarch64-tdep.c
>> +++ b/gdb/aarch64-tdep.c
>> @@ -3300,7 +3300,7 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>>                         int regnum_offset,
>>                         gdb::array_view<const gdb_byte> buf)
>>  {
>> -  unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
>> +  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
>>    gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
>>
>>    /* Enough space for a full vector register.
>> @@ -3309,11 +3309,11 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>>       various 'scalar' pseudo registers to behavior like architectural
>>       writes, register width bytes are written the remainder are set to
>>       zero.  */
>> -  constexpr int raw_reg_size = 16;
>> +  int raw_reg_size = register_size (gdbarch, raw_regnum);
>>    gdb_byte raw_buf[raw_reg_size] {};
>> -  gdb::array_view<gdb_byte> raw_view (raw_buf);
>> +  gdb::array_view<gdb_byte> raw_view (raw_buf, raw_reg_size);
>>    copy (buf, raw_view.slice (0, buf.size ()));
>> -  put_frame_register (next_frame, v_regnum, raw_view);
>> +  put_frame_register (next_frame, raw_regnum, raw_view);
>>  }
>>
>>  /* Given REGNUM, a SME pseudo-register number, store the bytes from DATA to the
>>
>> Simon
> 
> I managed to run a Debian AArch64 image in qemu, with SVE support, so I
> was able to reproduce the failures you mentioned.  In the end, here's a
> version of aarch64_pseudo_write_1 that works for me (written as to
> minimize the number of unnecessary changes, since that seems to
> introduce unexpected bugs...).
> 
> static void
> aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
> 			int regnum_offset,
> 			gdb::array_view<const gdb_byte> buf)
> {
>   unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
> 
>   /* Enough space for a full vector register.  */
>   int raw_reg_size = register_size (gdbarch, raw_regnum);
>   gdb_byte raw_buf[raw_reg_size];
>   gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
> 
>   /* Ensure the register buffer is zero, we want gdb writes of the
>      various 'scalar' pseudo registers to behavior like architectural
>      writes, register width bytes are written the remainder are set to
>      zero.  */
>   memset (raw_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));
> 
>   gdb::array_view<gdb_byte> raw_view (raw_buf, raw_reg_size);
>   copy (buf, raw_view.slice (0, buf.size ()));
>   put_frame_register (next_frame, raw_regnum, raw_view);
> }
> 
> Simon

Sorry for the late reply. I was out a couple days last week.

The above seems to make gdb.arch/aarch64-fp.exp happy, but gdb.arch/aarch64-pseudo-unwind.exp is still slightly unhappy:

FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value

This was tested on hardware (AWS's Graviton 3). Let me play with it a bit to understand what's up. I also see a
SIGSEGV in the test that shouldn't be there. I'm guessing some sort of raw register corruption, as it leads to
pc == 0x0.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 05/24] gdb: change regcache interface to use array_view
  2023-11-08  5:00 ` [PATCH 05/24] gdb: change regcache interface to use array_view Simon Marchi
@ 2023-11-13 13:43   ` Andrew Burgess
  2023-11-13 14:00     ` Andrew Burgess
  0 siblings, 1 reply; 51+ messages in thread
From: Andrew Burgess @ 2023-11-13 13:43 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi


I accidentally sent this email only to Simon originally, dropping
gdb-patches.  Re-sending it now, including the list.

Simon Marchi <simon.marchi@polymtl.ca> writes:

> From: Simon Marchi <simon.marchi@efficios.com>
>
> Change most of regcache (and base classes) to use array_view when
> possible, instead of raw pointers.  By propagating the use of array_view
> further, it enables having some runtime checks to make sure the what we
> read from or write to regcaches has the expected length (such as the one
> in the `copy(array_view, array_view)` function.  It also integrates well
> when connecting with other APIs already using gdb::array_view.
>
> Add some overloads of the methods using raw pointers to avoid having to
> change all call sites at once (which is both a lot of work and risky).
>
> I tried to do this change in small increments, but since many of these
> functions use each other, it ended up simpler to do it in one shot than
> having a lot of intermediary / transient changes.
>
> This change extends into gdbserver as well, because there is some part
> of the regcache interface that is shared.
>
> Changing the reg_buffer_common interface to use array_view caused some
> build failures in nat/aarch64-scalable-linux-ptrace.c.  That file
> currently "takes advantage" of the fact that
> reg_buffer_common::{raw_supply,raw_collect} operates on `void *`, which
> IMO is dangerous.  It uses raw_supply/raw_collect directly on
> uint64_t's, which I guess is fine because it is expected that native
> code will have the same endianness as the debugged process.  To
> accomodate that, add some overloads of raw_collect and raw_supply that
> work on uint64_t.
>
> This file also uses raw_collect and raw_supply on `char` pointers.
> Change it to use `gdb_byte` pointers instead.  Add overloads of
> raw_collect and raw_supply that work on `gdb_byte *` and make an
> array_view on the fly using the register's size.  Those call sites could
> be converted to use array_view with not much work, in which case these
> overloads could be removed, but I didn't want to do it in this patch, to
> avoid starting to dig in arch-specific code.
>
> Change-Id: I9005f04114543ddff738949e12d85a31855304c2
> ---
>  gdb/frame.c                             |   4 +-
>  gdb/nat/aarch64-scalable-linux-ptrace.c |  20 +-
>  gdb/regcache.c                          | 478 ++++++++++++++----------
>  gdb/regcache.h                          | 113 ++++--
>  gdbserver/regcache.cc                   |  50 +--
>  gdbserver/regcache.h                    |   6 +-
>  gdbsupport/common-regcache.h            |  41 +-
>  gdbsupport/rsp-low.cc                   |   8 +
>  gdbsupport/rsp-low.h                    |   2 +
>  9 files changed, 451 insertions(+), 271 deletions(-)
>
> diff --git a/gdb/frame.c b/gdb/frame.c
> index 7077016ccba4..afadb8ac4e73 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -1107,9 +1107,9 @@ get_frame_func (frame_info_ptr this_frame)
>  std::unique_ptr<readonly_detached_regcache>
>  frame_save_as_regcache (frame_info_ptr this_frame)
>  {
> -  auto cooked_read = [this_frame] (int regnum, gdb_byte *buf)
> +  auto cooked_read = [this_frame] (int regnum, gdb::array_view<gdb_byte> buf)
>      {
> -      if (!deprecated_frame_register_read (this_frame, regnum, buf))
> +      if (!deprecated_frame_register_read (this_frame, regnum, buf.data ()))
>  	return REG_UNAVAILABLE;
>        else
>  	return REG_VALID;
> diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c
> index dc0e45fa91ee..b8fb317edaca 100644
> --- a/gdb/nat/aarch64-scalable-linux-ptrace.c
> +++ b/gdb/nat/aarch64-scalable-linux-ptrace.c
> @@ -613,7 +613,7 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
>  {
>    gdb::byte_vector sve_state = aarch64_fetch_sve_regset (tid);
>  
> -  char *base = (char *) sve_state.data ();
> +  gdb_byte *base = sve_state.data ();
>    struct user_sve_header *header
>      = (struct user_sve_header *) sve_state.data ();
>  
> @@ -684,8 +684,10 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
>  	  reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg);
>  	}
>  
> -      reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
> -      reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
> +      reg_buf->raw_supply (AARCH64_FPSR_REGNUM,
> +			   (const gdb_byte *) &fpsimd->fpsr);
> +      reg_buf->raw_supply (AARCH64_FPCR_REGNUM,
> +			   (const gdb_byte *) &fpsimd->fpcr);
>  
>        /* Clear the SVE only registers.  */
>        memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
> @@ -720,7 +722,7 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
>    gdb::byte_vector new_state (SVE_PT_SIZE (32, SVE_PT_REGS_SVE), 0);
>    memcpy (new_state.data (), sve_state.data (), sve_state.size ());
>    header = (struct user_sve_header *) new_state.data ();
> -  char *base = (char *) new_state.data ();
> +  gdb_byte *base = new_state.data ();
>  
>    /* Sanity check the data in the header.  */
>    if (!sve_vl_valid (header->vl)
> @@ -805,9 +807,11 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
>  	    }
>  
>  	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM))
> -	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
> +	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM,
> +				  (gdb_byte *) &fpsimd->fpsr);
>  	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM))
> -	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
> +	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM,
> +				  (gdb_byte *) &fpsimd->fpcr);
>  
>  	  /* At this point we have collected all the data from the register
>  	     cache and we are ready to update the FPSIMD register content
> @@ -894,7 +898,7 @@ aarch64_za_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf,
>    /* Sanity check.  */
>    gdb_assert (!za_state.empty ());
>  
> -  char *base = (char *) za_state.data ();
> +  gdb_byte *base = za_state.data ();
>    struct user_za_header *header = (struct user_za_header *) base;
>  
>    /* If we have ZA state, read it.  Otherwise, make the contents of ZA
> @@ -1027,7 +1031,7 @@ aarch64_za_regs_copy_from_reg_buf (int tid,
>        /* Fetch the current ZA state from the thread.  */
>        gdb::byte_vector za_state = aarch64_fetch_za_regset (tid);
>  
> -      char *base = (char *) za_state.data ();
> +      gdb_byte *base = za_state.data ();
>        struct user_za_header *za_header = (struct user_za_header *) base;
>        uint64_t svq = sve_vq_from_vl (za_header->vl);
>  
> diff --git a/gdb/regcache.c b/gdb/regcache.c
> index ae5411ba5563..4f3881386f34 100644
> --- a/gdb/regcache.c
> +++ b/gdb/regcache.c
> @@ -221,10 +221,9 @@ regcache::regcache (inferior *inf_for_target_calls, gdbarch *gdbarch,
>  
>  readonly_detached_regcache::readonly_detached_regcache (regcache &src)
>    : readonly_detached_regcache (src.arch (),
> -				[&src] (int regnum, gdb_byte *buf)
> -				  {
> -				    return src.cooked_read (regnum, buf);
> -				  })
> +				[&src] (int regnum,
> +					gdb::array_view<gdb_byte> buf)
> +				  { return src.cooked_read (regnum, buf, 1.0f); })
>  {
>  }
>  
> @@ -234,19 +233,36 @@ reg_buffer::arch () const
>    return m_descr->gdbarch;
>  }
>  
> -/* Return  a pointer to register REGNUM's buffer cache.  */
> +template<typename ElemType>
> +gdb::array_view<ElemType>
> +reg_buffer::register_buffer (int regnum) const
> +{
> +  assert_regnum (regnum);
> +  ElemType *start = &m_registers[m_descr->register_offset[regnum]];
> +  int size = m_descr->sizeof_register[regnum];
> +  return gdb::array_view<ElemType> (start, size);
> +}
>  
> -gdb_byte *
> +/* See regcache.h.  */
> +
> +gdb::array_view<const gdb_byte>
>  reg_buffer::register_buffer (int regnum) const
>  {
> -  return m_registers.get () + m_descr->register_offset[regnum];
> +  return register_buffer<const gdb_byte> (regnum);
> +}
> +
> +/* See regcache.h.  */
> +
> +gdb::array_view<gdb_byte>
> +reg_buffer::register_buffer (int regnum)
> +{
> +  return register_buffer<gdb_byte> (regnum);
>  }
>  
>  void
>  reg_buffer::save (register_read_ftype cooked_read)
>  {
>    struct gdbarch *gdbarch = m_descr->gdbarch;
> -  int regnum;
>  
>    /* It should have pseudo registers.  */
>    gdb_assert (m_has_pseudo);
> @@ -257,17 +273,17 @@ reg_buffer::save (register_read_ftype cooked_read)
>       save_reggroup) and mark them as valid.  The full [0 .. gdbarch_num_regs +
>       gdbarch_num_pseudo_regs) range is checked since some architectures need
>       to save/restore `cooked' registers that live in memory.  */
> -  for (regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
> +  for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
>      {
>        if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
>  	{
> -	  gdb_byte *dst_buf = register_buffer (regnum);
> -	  enum register_status status = cooked_read (regnum, dst_buf);
> +	  gdb::array_view<gdb_byte> dst_buf = register_buffer (regnum);
> +	  register_status status = cooked_read (regnum, dst_buf);
>  
>  	  gdb_assert (status != REG_UNKNOWN);
>  
>  	  if (status != REG_VALID)
> -	    memset (dst_buf, 0, register_size (gdbarch, regnum));
> +	    memset (dst_buf.data (), 0, dst_buf.size ());
>  
>  	  m_register_status[regnum] = status;
>  	}
> @@ -294,7 +310,7 @@ regcache::restore (readonly_detached_regcache *src)
>        if (gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup))
>  	{
>  	  if (src->m_register_status[regnum] == REG_VALID)
> -	    cooked_write (regnum, src->register_buffer (regnum));
> +	    cooked_write (regnum, src->register_buffer (regnum), 1.0f);
>  	}
>      }
>  }
> @@ -610,21 +626,30 @@ regcache::raw_update (int regnum)
>      }
>  }
>  
> -enum register_status
> -readable_regcache::raw_read (int regnum, gdb_byte *buf)
> +register_status
> +readable_regcache::raw_read (int regnum, gdb::array_view<gdb_byte> dst, float)
>  {
> -  gdb_assert (buf != NULL);
> +  assert_regnum (regnum);
> +  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
> +
>    raw_update (regnum);
>  
>    if (m_register_status[regnum] != REG_VALID)
> -    memset (buf, 0, m_descr->sizeof_register[regnum]);
> +    memset (dst.data (), 0, dst.size ());
>    else
> -    memcpy (buf, register_buffer (regnum),
> -	    m_descr->sizeof_register[regnum]);
> +    copy (register_buffer (regnum), dst);
>  
>    return m_register_status[regnum];
>  }
>  
> +register_status
> +readable_regcache::raw_read (int regnum, gdb_byte *dst)
> +{
> +  assert_regnum (regnum);
> +  int size = m_descr->sizeof_register[regnum];
> +  return raw_read (regnum, gdb::make_array_view (dst, size), 1.0f);
> +}
> +
>  enum register_status
>  regcache_raw_read_signed (struct regcache *regcache, int regnum, LONGEST *val)
>  {
> @@ -639,12 +664,14 @@ readable_regcache::raw_read (int regnum, T *val)
>    assert_regnum (regnum);
>    size_t len = m_descr->sizeof_register[regnum];
>    gdb_byte *buf = (gdb_byte *) alloca (len);
> -  register_status status = raw_read (regnum, buf);
> +  auto view = gdb::make_array_view (buf, len);
> +  register_status status = raw_read (regnum, view, 1.0f);
> +
>    if (status == REG_VALID)
> -    *val = extract_integer<T> ({buf, len},
> -			       gdbarch_byte_order (m_descr->gdbarch));
> +    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
>    else
>      *val = 0;
> +
>    return status;
>  }
>  
> @@ -668,13 +695,13 @@ template<typename T, typename>
>  void
>  regcache::raw_write (int regnum, T val)
>  {
> -  gdb_byte *buf;
> -
>    assert_regnum (regnum);
> -  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
> -  store_integer (buf, m_descr->sizeof_register[regnum],
> -		 gdbarch_byte_order (m_descr->gdbarch), val);
> -  raw_write (regnum, buf);
> +
> +  int len = m_descr->sizeof_register[regnum];
> +  gdb_byte *buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
> +  auto view = gdb::make_array_view (buf, len);
> +  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
> +  raw_write (regnum, view, 1.0f);
>  }
>  
>  void
> @@ -698,47 +725,61 @@ regcache_raw_get_signed (struct regcache *regcache, int regnum)
>    return value;
>  }
>  
> -enum register_status
> -readable_regcache::cooked_read (int regnum, gdb_byte *buf)
> +/* See regcache.h.  */
> +
> +register_status
> +readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst,
> +				float)
>  {
>    gdb_assert (regnum >= 0);
>    gdb_assert (regnum < m_descr->nr_cooked_registers);
> +
>    if (regnum < num_raw_registers ())
> -    return raw_read (regnum, buf);
> -  else if (m_has_pseudo
> -	   && m_register_status[regnum] != REG_UNKNOWN)
> +    return raw_read (regnum, dst, 1.0f);
> +
> +  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
> +
> +  if (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
>      {
>        if (m_register_status[regnum] == REG_VALID)
> -	memcpy (buf, register_buffer (regnum),
> -		m_descr->sizeof_register[regnum]);
> +	copy (register_buffer (regnum), dst);
>        else
> -	memset (buf, 0, m_descr->sizeof_register[regnum]);
> +	memset (dst.data (), 0, dst.size ());
>  
>        return m_register_status[regnum];
>      }
>    else if (gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
>      {
> -      struct value *computed;
> -      enum register_status result = REG_VALID;
> -
> +      register_status result = REG_VALID;
>        scoped_value_mark mark;
> +      value *computed
> +	= gdbarch_pseudo_register_read_value (m_descr->gdbarch, this, regnum);
>  
> -      computed = gdbarch_pseudo_register_read_value (m_descr->gdbarch,
> -						     this, regnum);
>        if (computed->entirely_available ())
> -	memcpy (buf, computed->contents_raw ().data (),
> -		m_descr->sizeof_register[regnum]);
> +	copy (computed->contents_raw (), dst);
>        else
>  	{
> -	  memset (buf, 0, m_descr->sizeof_register[regnum]);
> +	  memset (dst.data (), 0, dst.size ());
>  	  result = REG_UNAVAILABLE;
>  	}
>  
>        return result;
>      }
>    else
> -    return gdbarch_pseudo_register_read (m_descr->gdbarch, this,
> -					 regnum, buf);
> +    return gdbarch_pseudo_register_read (m_descr->gdbarch, this, regnum,
> +					 dst.data ());
> +}
> +
> +/* See regcache.h.  */
> +
> +register_status
> +readable_regcache::cooked_read (int regnum, gdb_byte *dst)
> +{
> +  gdb_assert (regnum >= 0);
> +  gdb_assert (regnum < m_descr->nr_cooked_registers);
> +
> +  int size = m_descr->sizeof_register[regnum];
> +  return cooked_read (regnum, gdb::make_array_view (dst, size), 1.0f);
>  }
>  
>  struct value *
> @@ -760,8 +801,8 @@ readable_regcache::cooked_read_value (int regnum)
>        /* It is more efficient in general to do this delegation in this
>  	 direction than in the other one, even though the value-based
>  	 API is preferred.  */
> -      if (cooked_read (regnum,
> -		       result->contents_raw ().data ()) == REG_UNAVAILABLE)
> +      if (cooked_read (regnum, result->contents_raw (), 1.0f)
> +	  == REG_UNAVAILABLE)
>  	result->mark_bytes_unavailable (0,
>  					result->type ()->length ());
>  
> @@ -787,10 +828,10 @@ readable_regcache::cooked_read (int regnum, T *val)
>    gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
>    size_t len = m_descr->sizeof_register[regnum];
>    gdb_byte *buf = (gdb_byte *) alloca (len);
> -  register_status status = cooked_read (regnum, buf);
> +  auto view = gdb::make_array_view (buf, len);
> +  register_status status = cooked_read (regnum, view, 1.0f);
>    if (status == REG_VALID)
> -    *val = extract_integer<T> ({buf, len},
> -			       gdbarch_byte_order (m_descr->gdbarch));
> +    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
>    else
>      *val = 0;
>    return status;
> @@ -816,13 +857,14 @@ template<typename T, typename>
>  void
>  regcache::cooked_write (int regnum, T val)
>  {
> -  gdb_byte *buf;
> +  gdb_assert (regnum >= 0);
> +  gdb_assert (regnum < m_descr->nr_cooked_registers);
>  
> -  gdb_assert (regnum >=0 && regnum < m_descr->nr_cooked_registers);
> -  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
> -  store_integer (buf, m_descr->sizeof_register[regnum],
> -		 gdbarch_byte_order (m_descr->gdbarch), val);
> -  cooked_write (regnum, buf);
> +  int size = m_descr->sizeof_register[regnum];
> +  gdb_byte *buf = (gdb_byte *) alloca (size);
> +  auto view = gdb::make_array_view (buf, size);
> +  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
> +  cooked_write (regnum, view, 1.0f);
>  }
>  
>  void
> @@ -834,11 +876,10 @@ regcache_cooked_write_unsigned (struct regcache *regcache, int regnum,
>  }
>  
>  void
> -regcache::raw_write (int regnum, const gdb_byte *buf)
> +regcache::raw_write (int regnum, gdb::array_view<const gdb_byte> src, float)
>  {
> -
> -  gdb_assert (buf != NULL);
>    assert_regnum (regnum);
> +  gdb_assert (src.size () == m_descr->sizeof_register[regnum]);
>  
>    /* On the sparc, writing %g0 is a no-op, so we don't even want to
>       change the registers array if something writes to this register.  */
> @@ -848,15 +889,15 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
>    /* If we have a valid copy of the register, and new value == old
>       value, then don't bother doing the actual store.  */
>    if (get_register_status (regnum) == REG_VALID
> -      && (memcmp (register_buffer (regnum), buf,
> -		  m_descr->sizeof_register[regnum]) == 0))
> +      && (memcmp (register_buffer (regnum).data (), src.data (), src.size ())
> +	  == 0))
>      return;
>  
>    gdb::optional<scoped_restore_current_thread> maybe_restore_thread
>      = maybe_switch_inferior (m_inf_for_target_calls);
>  
>    target_prepare_to_store (this);
> -  raw_supply (regnum, buf);
> +  raw_supply (regnum, src, 1.0f);
>  
>    /* Invalidate the register after it is written, in case of a
>       failure.  */
> @@ -871,211 +912,248 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
>  }
>  
>  void
> -regcache::cooked_write (int regnum, const gdb_byte *buf)
> +regcache::raw_write (int regnum, const gdb_byte *src)
> +{
> +  assert_regnum (regnum);
> +
> +  int size = m_descr->sizeof_register[regnum];
> +  raw_write (regnum, gdb::make_array_view (src, size), 1.0f);
> +}
> +
> +/* See regcache.h.  */
> +
> +void
> +regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float)
>  {
>    gdb_assert (regnum >= 0);
>    gdb_assert (regnum < m_descr->nr_cooked_registers);
> +
>    if (regnum < num_raw_registers ())
> -    raw_write (regnum, buf);
> +    raw_write (regnum, src, 1.0f);
>    else
> -    gdbarch_pseudo_register_write (m_descr->gdbarch, this,
> -				   regnum, buf);
> +    gdbarch_pseudo_register_write (m_descr->gdbarch, this, regnum,
> +				   src.data ());
>  }
>  
>  /* See regcache.h.  */
>  
> -enum register_status
> -readable_regcache::read_part (int regnum, int offset, int len,
> -			      gdb_byte *out, bool is_raw)
> +void
> +regcache::cooked_write (int regnum, const gdb_byte *src)
> +{
> +  gdb_assert (regnum >= 0);
> +  gdb_assert (regnum < m_descr->nr_cooked_registers);
> +
> +  int size = m_descr->sizeof_register[regnum];
> +  return cooked_write (regnum, gdb::make_array_view (src, size), 1.0f);
> +}
> +
> +/* See regcache.h.  */
> +
> +register_status
> +readable_regcache::read_part (int regnum, int offset,
> +			      gdb::array_view<gdb_byte> dst, bool is_raw)
>  {
>    int reg_size = register_size (arch (), regnum);
>  
> -  gdb_assert (out != NULL);
>    gdb_assert (offset >= 0);
> -  gdb_assert (len >= 0 && offset + len <= reg_size);
> +  gdb_assert (offset + dst.size () <= reg_size);
>  
> -  if (len == 0)
> +  if (dst.size () == 0)
>      {
>        /* Nothing to do.  */
>        return REG_VALID;
>      }
>  
> -  if (len == reg_size)
> +  if (dst.size () == reg_size)
>      {
>        /* Read the full register.  */
> -      return (is_raw) ? raw_read (regnum, out) : cooked_read (regnum, out);
> +      if (is_raw)
> +	return raw_read (regnum, dst, 1.0f);
> +      else
> +	return cooked_read (regnum, dst, 1.0f);
>      }
>  
> -  enum register_status status;
> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
> -
>    /* Read full register to buffer.  */
> -  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
> +  register_status status;
> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
> +
> +  if (is_raw)
> +    status = raw_read (regnum, reg, 1.0f);
> +  else
> +    status = cooked_read (regnum, reg, 1.0f);
> +
>    if (status != REG_VALID)
>      return status;
>  
>    /* Copy out.  */
> -  memcpy (out, reg + offset, len);
> +  copy (reg.slice (offset, dst.size ()), dst);
>    return REG_VALID;
>  }
>  
>  /* See regcache.h.  */
>  
>  void
> -reg_buffer::raw_collect_part (int regnum, int offset, int len,
> -			      gdb_byte *out) const
> +reg_buffer::raw_collect_part (int regnum, int offset,
> +			      gdb::array_view<gdb_byte> dst) const
>  {
>    int reg_size = register_size (arch (), regnum);
>  
> -  gdb_assert (out != nullptr);
>    gdb_assert (offset >= 0);
> -  gdb_assert (len >= 0 && offset + len <= reg_size);
> +  gdb_assert (offset + dst.size () <= reg_size);
>  
> -  if (len == 0)
> +  if (dst.size () == 0)
>      {
>        /* Nothing to do.  */
>        return;
>      }
>  
> -  if (len == reg_size)
> +  if (dst.size () == reg_size)
>      {
>        /* Collect the full register.  */
> -      return raw_collect (regnum, out);
> +      return raw_collect (regnum, dst, 1.0f);
>      }
>  
>    /* Read to buffer, then write out.  */
> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
> -  raw_collect (regnum, reg);
> -  memcpy (out, reg + offset, len);
> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
> +  raw_collect (regnum, reg, 1.0f);
> +  copy (reg.slice (offset, dst.size ()), dst);
>  }
>  
>  /* See regcache.h.  */
>  
> -enum register_status
> -regcache::write_part (int regnum, int offset, int len,
> -		      const gdb_byte *in, bool is_raw)
> +register_status
> +regcache::write_part (int regnum, int offset,
> +		      gdb::array_view<const gdb_byte> src, bool is_raw)
>  {
>    int reg_size = register_size (arch (), regnum);
>  
> -  gdb_assert (in != NULL);
>    gdb_assert (offset >= 0);
> -  gdb_assert (len >= 0 && offset + len <= reg_size);
> +  gdb_assert (offset + src.size () <= reg_size);
>  
> -  if (len == 0)
> +  if (src.size () == 0)
>      {
>        /* Nothing to do.  */
>        return REG_VALID;
>      }
>  
> -  if (len == reg_size)
> +  if (src.size () == reg_size)
>      {
>        /* Write the full register.  */
> -      (is_raw) ? raw_write (regnum, in) : cooked_write (regnum, in);
> +      if (is_raw)
> +	raw_write (regnum, src, 1.0f);
> +      else
> +	cooked_write (regnum, src, 1.0f);
> +
>        return REG_VALID;
>      }
>  
> -  enum register_status status;
> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
> -
>    /* Read existing register to buffer.  */
> -  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
> +  register_status status;
> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
> +
> +  if (is_raw)
> +    status = raw_read (regnum, reg, 1.0f);
> +  else
> +    status = cooked_read (regnum, reg, 1.0f);
> +
>    if (status != REG_VALID)
>      return status;
>  
>    /* Update buffer, then write back to regcache.  */
> -  memcpy (reg + offset, in, len);
> -  is_raw ? raw_write (regnum, reg) : cooked_write (regnum, reg);
> +  copy (src, reg.slice (offset, src.size ()));
> +
> +  if (is_raw)
> +    raw_write (regnum, reg, 1.0f);
> +  else
> +    cooked_write (regnum, reg, 1.0f);
> +
>    return REG_VALID;
>  }
>  
>  /* See regcache.h.  */
>  
>  void
> -reg_buffer::raw_supply_part (int regnum, int offset, int len,
> -			     const gdb_byte *in)
> +reg_buffer::raw_supply_part (int regnum, int offset,
> +			     gdb::array_view<const gdb_byte> src)
>  {
>    int reg_size = register_size (arch (), regnum);
>  
> -  gdb_assert (in != nullptr);
>    gdb_assert (offset >= 0);
> -  gdb_assert (len >= 0 && offset + len <= reg_size);
> +  gdb_assert (offset + src.size () <= reg_size);
>  
> -  if (len == 0)
> +  if (src.size () == 0)
>      {
>        /* Nothing to do.  */
>        return;
>      }
>  
> -  if (len == reg_size)
> +  if (src.size () == reg_size)
>      {
>        /* Supply the full register.  */
> -      return raw_supply (regnum, in);
> +      return raw_supply (regnum, src, 1.0f);
>      }
>  
> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
> -
>    /* Read existing value to buffer.  */
> -  raw_collect (regnum, reg);
> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
> +  raw_collect (regnum, reg, 1.0f);
>  
>    /* Write to buffer, then write out.  */
> -  memcpy (reg + offset, in, len);
> -  raw_supply (regnum, reg);
> +  copy (src, reg.slice (offset, src.size ()));
> +  raw_supply (regnum, reg, 1.0f);
>  }
>  
> -enum register_status
> -readable_regcache::raw_read_part (int regnum, int offset, int len,
> -				  gdb_byte *buf)
> +register_status
> +readable_regcache::raw_read_part (int regnum, int offset,
> +				  gdb::array_view<gdb_byte> dst)
>  {
>    assert_regnum (regnum);
> -  return read_part (regnum, offset, len, buf, true);
> +  return read_part (regnum, offset, dst, true);
>  }
>  
>  /* See regcache.h.  */
>  
>  void
> -regcache::raw_write_part (int regnum, int offset, int len,
> -			  const gdb_byte *buf)
> +regcache::raw_write_part (int regnum, int offset,
> +			  gdb::array_view<const gdb_byte> src)
>  {
>    assert_regnum (regnum);
> -  write_part (regnum, offset, len, buf, true);
> +  write_part (regnum, offset, src, true);
>  }
>  
>  /* See regcache.h.  */
>  
> -enum register_status
> -readable_regcache::cooked_read_part (int regnum, int offset, int len,
> -				     gdb_byte *buf)
> +register_status
> +readable_regcache::cooked_read_part (int regnum, int offset,
> +				     gdb::array_view<gdb_byte> dst)
>  {
>    gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
> -  return read_part (regnum, offset, len, buf, false);
> +  return read_part (regnum, offset, dst, false);
>  }
>  
>  /* See regcache.h.  */
>  
>  void
> -regcache::cooked_write_part (int regnum, int offset, int len,
> -			     const gdb_byte *buf)
> +regcache::cooked_write_part (int regnum, int offset,
> +			     gdb::array_view<const gdb_byte> src)
>  {
>    gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
> -  write_part (regnum, offset, len, buf, false);
> +  write_part (regnum, offset, src, false);
>  }
>  
>  /* See gdbsupport/common-regcache.h.  */
>  
>  void
> -reg_buffer::raw_supply (int regnum, const void *buf)
> +reg_buffer::raw_supply (int regnum, gdb::array_view<const gdb_byte> src, float)
>  {
> -  void *regbuf;
> -  size_t size;
> -
> -  assert_regnum (regnum);
> -
> -  regbuf = register_buffer (regnum);
> -  size = m_descr->sizeof_register[regnum];
> +  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
>  
> -  if (buf)
> +  if (src.data () != nullptr)
>      {
> -      memcpy (regbuf, buf, size);
> +      copy (src, dst);
>        m_register_status[regnum] = REG_VALID;
>      }
>    else
> @@ -1083,7 +1161,7 @@ reg_buffer::raw_supply (int regnum, const void *buf)
>        /* This memset not strictly necessary, but better than garbage
>  	 in case the register value manages to escape somewhere (due
>  	 to a bug, no less).  */
> -      memset (regbuf, 0, size);
> +      memset (dst.data (), 0, dst.size ());
>        m_register_status[regnum] = REG_UNAVAILABLE;
>      }
>  }
> @@ -1091,19 +1169,25 @@ reg_buffer::raw_supply (int regnum, const void *buf)
>  /* See regcache.h.  */
>  
>  void
> -reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
> -				int addr_len, bool is_signed)
> +reg_buffer::raw_supply (int regnum, const void *src)
>  {
> -  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
> -  gdb_byte *regbuf;
> -  size_t regsize;
> -
>    assert_regnum (regnum);
>  
> -  regbuf = register_buffer (regnum);
> -  regsize = m_descr->sizeof_register[regnum];
> +  int size = m_descr->sizeof_register[regnum];
> +  raw_supply (regnum, gdb::make_array_view ((const gdb_byte *) src, size),
> +	      1.0f);
> +}
> +
> +/* See regcache.h.  */
> +
> +void
> +reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr, int addr_len,
> +				bool is_signed)
> +{
> +  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
> +  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
>  
> -  copy_integer_to_size (regbuf, regsize, addr, addr_len, is_signed,
> +  copy_integer_to_size (dst.data (), dst.size (), addr, addr_len, is_signed,
>  			byte_order);
>    m_register_status[regnum] = REG_VALID;
>  }
> @@ -1113,32 +1197,31 @@ reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
>  void
>  reg_buffer::raw_supply_zeroed (int regnum)
>  {
> -  void *regbuf;
> -  size_t size;
> -
> -  assert_regnum (regnum);
> -
> -  regbuf = register_buffer (regnum);
> -  size = m_descr->sizeof_register[regnum];
> -
> -  memset (regbuf, 0, size);
> +  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
> +  memset (dst.data (), 0, dst.size ());
>    m_register_status[regnum] = REG_VALID;
>  }
>  
>  /* See gdbsupport/common-regcache.h.  */
>  
>  void
> -reg_buffer::raw_collect (int regnum, void *buf) const
> +reg_buffer::raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
> +			 float) const
>  {
> -  const void *regbuf;
> -  size_t size;
> +  gdb::array_view<const gdb_byte> src = register_buffer (regnum);
> +  copy (src, dst);
> +}
>  
> -  gdb_assert (buf != NULL);
> +/* See regcache.h.  */
> +
> +void
> +reg_buffer::raw_collect (int regnum, void *dst) const
> +{
>    assert_regnum (regnum);
>  
> -  regbuf = register_buffer (regnum);
> -  size = m_descr->sizeof_register[regnum];
> -  memcpy (buf, regbuf, size);
> +  int size = m_descr->sizeof_register[regnum];
> +  return raw_collect (regnum, gdb::make_array_view ((gdb_byte *) dst, size),
> +		      1.0f);
>  }
>  
>  /* See regcache.h.  */
> @@ -1147,16 +1230,9 @@ void
>  reg_buffer::raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
>  				 bool is_signed) const
>  {
> -  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
> -  const gdb_byte *regbuf;
> -  size_t regsize;
> -
> -  assert_regnum (regnum);
> -
> -  regbuf = register_buffer (regnum);
> -  regsize = m_descr->sizeof_register[regnum];
> -
> -  copy_integer_to_size (addr, addr_len, regbuf, regsize, is_signed,
> +  gdb::array_view<const gdb_byte> dst = register_buffer (regnum);
> +  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
> +  copy_integer_to_size (addr, addr_len, dst.data (), dst.size (), is_signed,
>  			byte_order);
>  }
>  
> @@ -1175,7 +1251,8 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
>  
>    if (out_buf != nullptr)
>      {
> -      raw_collect_part (regnum, 0, reg_size, out_buf + offs);
> +      raw_collect_part (regnum, 0,
> +			gdb::make_array_view (out_buf + offs, reg_size));
>  
>        /* Ensure any additional space is cleared.  */
>        if (slot_size > reg_size)
> @@ -1186,12 +1263,14 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
>        /* Zero-extend the register value if the slot is smaller than the register.  */
>        if (slot_size < register_size (gdbarch, regnum))
>  	out_regcache->raw_supply_zeroed (regnum);
> -      out_regcache->raw_supply_part (regnum, 0, reg_size, in_buf + offs);
> +      out_regcache->raw_supply_part (regnum, 0,
> +				     gdb::make_array_view (in_buf + offs,
> +							   reg_size));
>      }
>    else
>      {
>        /* Invalidate the register.  */
> -      out_regcache->raw_supply (regnum, nullptr);
> +      out_regcache->raw_supply (regnum, {});
>      }
>  }
>  
> @@ -1322,13 +1401,12 @@ bool
>  reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
>  {
>    gdb_assert (buf != NULL);
> -  assert_regnum (regnum);
>  
> -  const char *regbuf = (const char *) register_buffer (regnum);
> -  size_t size = m_descr->sizeof_register[regnum];
> -  gdb_assert (size >= offset);
> +  gdb::array_view<const gdb_byte> regbuf = register_buffer (regnum);
> +  gdb_assert (offset < regbuf.size ());
> +  regbuf = regbuf.slice (offset);
>  
> -  return (memcmp (buf, regbuf + offset, size - offset) == 0);
> +  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
>  }
>  
>  /* Special handling for register PC.  */
> @@ -1417,17 +1495,15 @@ regcache::debug_print_register (const char *func,  int regno)
>    if (regno >= 0 && regno < gdbarch_num_regs (gdbarch))
>      {
>        enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> -      int size = register_size (gdbarch, regno);
> -      gdb_byte *buf = register_buffer (regno);
> +      gdb::array_view<gdb_byte> buf = register_buffer (regno);
>  
>        gdb_printf (gdb_stdlog, " = ");
> -      for (int i = 0; i < size; i++)
> -	{
> -	  gdb_printf (gdb_stdlog, "%02x", buf[i]);
> -	}
> -      if (size <= sizeof (LONGEST))
> +      for (gdb_byte byte : buf)
> +	gdb_printf (gdb_stdlog, "%02x", byte);
> +
> +      if (buf.size () <= sizeof (LONGEST))
>  	{
> -	  ULONGEST val = extract_unsigned_integer (buf, size, byte_order);
> +	  ULONGEST val = extract_unsigned_integer (buf, byte_order);
>  
>  	  gdb_printf (gdb_stdlog, " %s %s",
>  		      core_addr_to_string_nz (val), plongest (val));
> @@ -1889,7 +1965,7 @@ cooked_read_test (struct gdbarch *gdbarch)
>    readwrite.set_ptid (mockctx.mock_ptid);
>    gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum));
>  
> -  readwrite.raw_read (nonzero_regnum, buf.data ());
> +  readwrite.raw_read (nonzero_regnum, buf, 1.0f);
>  
>    /* raw_read calls target_fetch_registers.  */
>    SELF_CHECK (mockctx.mock_target.fetch_registers_called > 0);
> @@ -1910,9 +1986,8 @@ cooked_read_test (struct gdbarch *gdbarch)
>  
>        gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
>  
> -      SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum,
> -						      inner_buf.data ()));
> -
> +      SELF_CHECK (REG_VALID
> +		  == readwrite.cooked_read (regnum, inner_buf, 1.0f));
>        SELF_CHECK (mockctx.mock_target.fetch_registers_called == 0);
>        SELF_CHECK (mockctx.mock_target.store_registers_called == 0);
>        SELF_CHECK (mockctx.mock_target.xfer_partial_called == 0);
> @@ -1932,8 +2007,7 @@ cooked_read_test (struct gdbarch *gdbarch)
>  	continue;
>  
>        gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
> -      enum register_status status = readonly.cooked_read (regnum,
> -							  inner_buf.data ());
> +      register_status status = readonly.cooked_read (regnum, inner_buf, 1.0f);
>  
>        if (regnum < gdbarch_num_regs (gdbarch))
>  	{
> @@ -2023,8 +2097,8 @@ cooked_write_test (struct gdbarch *gdbarch)
>  	      && regnum <= gdbarch_num_regs (gdbarch) + 4))
>  	continue;
>  
> -      std::vector<gdb_byte> expected (register_size (gdbarch, regnum), 0);
> -      std::vector<gdb_byte> buf (register_size (gdbarch, regnum), 0);
> +      gdb::byte_vector expected (register_size (gdbarch, regnum), 0);
> +      gdb::byte_vector buf (register_size (gdbarch, regnum), 0);
>        const auto type = register_type (gdbarch, regnum);
>  
>        if (type->code () == TYPE_CODE_FLT
> @@ -2079,9 +2153,9 @@ cooked_write_test (struct gdbarch *gdbarch)
>  	  SELF_CHECK (0);
>  	}
>  
> -      readwrite.cooked_write (regnum, expected.data ());
> +      readwrite.cooked_write (regnum, expected, 1.0f);
>  
> -      SELF_CHECK (readwrite.cooked_read (regnum, buf.data ()) == REG_VALID);
> +      SELF_CHECK (readwrite.cooked_read (regnum, buf, 1.0f) == REG_VALID);
>        SELF_CHECK (expected == buf);
>      }
>  }
> diff --git a/gdb/regcache.h b/gdb/regcache.h
> index 57ddac465f09..123c9248c345 100644
> --- a/gdb/regcache.h
> +++ b/gdb/regcache.h
> @@ -20,6 +20,7 @@
>  #ifndef REGCACHE_H
>  #define REGCACHE_H
>  
> +#include "gdbsupport/array-view.h"
>  #include "gdbsupport/common-regcache.h"
>  #include "gdbsupport/function-view.h"
>  
> @@ -172,8 +173,8 @@ extern struct type *register_type (struct gdbarch *gdbarch, int regnum);
>     
>  extern int register_size (struct gdbarch *gdbarch, int regnum);
>  
> -typedef gdb::function_view<register_status (int regnum, gdb_byte *buf)>
> -  register_read_ftype;
> +using register_read_ftype
> +  = gdb::function_view<register_status (int, gdb::array_view<gdb_byte>)>;
>  
>  /* A (register_number, register_value) pair.  */
>  
> @@ -199,7 +200,11 @@ class reg_buffer : public reg_buffer_common
>    enum register_status get_register_status (int regnum) const override;
>  
>    /* See gdbsupport/common-regcache.h.  */
> -  void raw_collect (int regnum, void *buf) const override;
> +  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
> +		    float) const override;
> +
> +  /* Deprecated overload of the above.  */
> +  void raw_collect (int regnum, void *dst) const;
>  
>    /* Collect register REGNUM from REGCACHE.  Store collected value as an integer
>       at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
> @@ -209,17 +214,24 @@ class reg_buffer : public reg_buffer_common
>    void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
>  			    bool is_signed) const;
>  
> -  /* Collect register REGNUM from REGCACHE, starting at OFFSET in register,
> -     reading only LEN.  */
> -  void raw_collect_part (int regnum, int offset, int len, gdb_byte *out) const;
> +  /* Collect part of register REGNUM from this register buffer.  Start at OFFSET
> +     in register.  The size is given by the size of DST.  */
> +  void raw_collect_part (int regnum, int offset,
> +			 gdb::array_view<gdb_byte> dst) const;
> +
> +  /* Deprecated overload of the above.  */
> +  void raw_collect_part (int regnum, int offset, int len, gdb_byte *dst) const
> +  { raw_collect_part (regnum, offset, gdb::make_array_view (dst, len)); }
>  
>    /* See gdbsupport/common-regcache.h.  */
> -  void raw_supply (int regnum, const void *buf) override;
> +  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
> +		   float) override;
> +
> +  /* Deprecated overload of the above.  */
> +  void raw_supply (int regnum, const void *src);
>  
>    void raw_supply (int regnum, const reg_buffer &src)
> -  {
> -    raw_supply (regnum, src.register_buffer (regnum));
> -  }
> +  { raw_supply (regnum, src.register_buffer (regnum), 1.0f); }
>  
>    /* Supply register REGNUM to REGCACHE.  Value to supply is an integer stored
>       at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
> @@ -234,9 +246,11 @@ class reg_buffer : public reg_buffer_common
>       unavailable).  */
>    void raw_supply_zeroed (int regnum);
>  
> -  /* Supply register REGNUM to REGCACHE, starting at OFFSET in register, writing
> -     only LEN, without editing the rest of the register.  */
> -  void raw_supply_part (int regnum, int offset, int len, const gdb_byte *in);
> +  /* Supply part of register REGNUM to this register buffer.  Start at OFFSET in
> +     the register.  The size is given by the size of SRC.  The rest of the
> +     register left untouched.  */
> +  void raw_supply_part (int regnum, int offset,
> +			gdb::array_view<const gdb_byte> src);
>  
>    void invalidate (int regnum);
>  
> @@ -251,7 +265,11 @@ class reg_buffer : public reg_buffer_common
>  
>    int num_raw_registers () const;
>  
> -  gdb_byte *register_buffer (int regnum) const;
> +  /* Return a view on register REGNUM's buffer cache.  */
> +  template <typename ElemType>
> +  gdb::array_view<ElemType> register_buffer (int regnum) const;
> +  gdb::array_view<const gdb_byte> register_buffer (int regnum) const;
> +  gdb::array_view<gdb_byte> register_buffer (int regnum);
>  
>    /* Save a register cache.  The set of registers saved into the
>       regcache determined by the save_reggroup.  COOKED_READ returns
> @@ -281,27 +299,42 @@ class readable_regcache : public reg_buffer
>  
>    /* Transfer a raw register [0..NUM_REGS) from core-gdb to this regcache,
>       return its value in *BUF and return its availability status.  */
> +  register_status raw_read (int regnum, gdb::array_view<gdb_byte> dst, float);
> +
> +  /* Deprecated overload of the above.  */
> +  register_status raw_read (int regnum, gdb_byte *dst);
>  
> -  enum register_status raw_read (int regnum, gdb_byte *buf);
>    template<typename T, typename = RequireLongest<T>>
> -  enum register_status raw_read (int regnum, T *val);
> +  register_status raw_read (int regnum, T *val);
>  
>    /* Partial transfer of raw registers.  Return the status of the register.  */
> -  enum register_status raw_read_part (int regnum, int offset, int len,
> -				      gdb_byte *buf);
> +  register_status raw_read_part (int regnum, int offset,
> +				 gdb::array_view<gdb_byte> dst);
> +
> +  /* Deprecated overload of the above.  */
> +  register_status raw_read_part (int regnum, int offset, int len,
> +				 gdb_byte *dst)
> +  { return raw_read_part (regnum, offset, gdb::make_array_view (dst, len)); }
>  
>    /* Make certain that the register REGNUM is up-to-date.  */
>    virtual void raw_update (int regnum) = 0;
>  
>    /* Transfer a raw register [0..NUM_REGS+NUM_PSEUDO_REGS) from core-gdb to
> -     this regcache, return its value in *BUF and return its availability status.  */
> -  enum register_status cooked_read (int regnum, gdb_byte *buf);
> +     this regcache, return its value in DST and return its availability status.  */
> +  register_status cooked_read (int regnum, gdb::array_view<gdb_byte> dst,
> +			       float);
> +  register_status cooked_read (int regnum, gdb_byte *dst);
> +
>    template<typename T, typename = RequireLongest<T>>
> -  enum register_status cooked_read (int regnum, T *val);
> +  register_status cooked_read (int regnum, T *val);
>  
>    /* Partial transfer of a cooked register.  */
> -  enum register_status cooked_read_part (int regnum, int offset, int len,
> -					 gdb_byte *buf);
> +  register_status cooked_read_part (int regnum, int offset,
> +				    gdb::array_view<gdb_byte> dst);
> +
> +  /* Deprecated overload of the above.  */
> +  register_status cooked_read_part (int regnum, int offset, int len, gdb_byte *src)
> +  { return cooked_read_part (regnum, offset, gdb::make_array_view (src, len)); }
>  
>    /* Read register REGNUM from the regcache and return a new value.  This
>       will call mark_value_bytes_unavailable as appropriate.  */
> @@ -311,8 +344,8 @@ class readable_regcache : public reg_buffer
>  
>    /* Perform a partial register transfer using a read, modify, write
>       operation.  Will fail if register is currently invalid.  */
> -  enum register_status read_part (int regnum, int offset, int len,
> -				  gdb_byte *out, bool is_raw);
> +  register_status read_part (int regnum, int offset,
> +			     gdb::array_view<gdb_byte> dst, bool is_raw);
>  };
>  
>  /* Buffer of registers, can be read and written.  */
> @@ -354,13 +387,19 @@ class regcache : public detached_regcache
>    /* Update the value of raw register REGNUM (in the range [0..NUM_REGS)) and
>       transfer its value to core-gdb.  */
>  
> -  void raw_write (int regnum, const gdb_byte *buf);
> +  void raw_write (int regnum, gdb::array_view<const gdb_byte> src, float);
> +
> +  /* Deprecated overload of the above.  */
> +  void raw_write (int regnum, const gdb_byte *src);
>  
>    template<typename T, typename = RequireLongest<T>>
>    void raw_write (int regnum, T val);
>  
>    /* Transfer of pseudo-registers.  */
> -  void cooked_write (int regnum, const gdb_byte *buf);
> +  void cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float);
> +
> +  /* Deprecated overload of the above.  */
> +  void cooked_write (int regnum, const gdb_byte *src);
>  
>    template<typename T, typename = RequireLongest<T>>
>    void cooked_write (int regnum, T val);
> @@ -369,12 +408,21 @@ class regcache : public detached_regcache
>  
>    /* Partial transfer of raw registers.  Perform read, modify, write style
>       operations.  */
> -  void raw_write_part (int regnum, int offset, int len, const gdb_byte *buf);
> +  void raw_write_part (int regnum, int offset,
> +		       gdb::array_view<const gdb_byte> src);
> +
> +  /* Deprecated overload of the above.  */
> +  void raw_write_part (int regnum, int offset, int len, const gdb_byte *src)
> +  { raw_write_part (regnum, offset, gdb::make_array_view (src, len)); }
>  
>    /* Partial transfer of a cooked register.  Perform read, modify, write style
>       operations.  */
> -  void cooked_write_part (int regnum, int offset, int len,
> -			  const gdb_byte *buf);
> +  void cooked_write_part (int regnum, int offset,
> +			  gdb::array_view<const gdb_byte> src);
> +
> +  /* Deprecated overload of the above.  */
> +  void cooked_write_part (int regnum, int offset, int len, const gdb_byte *src)
> +  { cooked_write_part (regnum, offset, gdb::make_array_view (src, len)); }
>  
>    /* Transfer a set of registers (as described by REGSET) between
>       REGCACHE and BUF.  If REGNUM == -1, transfer all registers
> @@ -442,8 +490,9 @@ class regcache : public detached_regcache
>  
>    /* Perform a partial register transfer using a read, modify, write
>       operation.  */
> -  enum register_status write_part (int regnum, int offset, int len,
> -				   const gdb_byte *in, bool is_raw);
> +  register_status write_part (int regnum, int offset,
> +			      gdb::array_view<const gdb_byte> src,
> +			      bool is_raw);
>  
>    /* The address space of this register cache (for registers where it
>       makes sense, like PC or SP).  */
> diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
> index 4ac0fb659c3b..e1c174b56cb6 100644
> --- a/gdbserver/regcache.cc
> +++ b/gdbserver/regcache.cc
> @@ -315,27 +315,33 @@ regcache_register_size (const reg_buffer_common *regcache, int n)
>      (gdb::checked_static_cast<const struct regcache *> (regcache)->tdesc, n);
>  }
>  
> -static unsigned char *
> +static gdb::array_view<gdb_byte>
>  register_data (const struct regcache *regcache, int n)
>  {
> -  return (regcache->registers
> -	  + find_register_by_number (regcache->tdesc, n).offset / 8);
> +  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
> +  return gdb::make_array_view (regcache->registers + reg.offset / 8,
> +			       reg.size / 8);
>  }
>  
>  void
> -supply_register (struct regcache *regcache, int n, const void *buf)
> +supply_register (struct regcache *regcache, int n, const void *vbuf)
>  {
> -  return regcache->raw_supply (n, buf);
> +  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
> +  const gdb_byte *buf = static_cast<const gdb_byte *> (vbuf);
> +  return regcache->raw_supply (n, gdb::make_array_view (buf, reg.size / 8),
> +			       1.0f);
>  }
>  
>  /* See gdbsupport/common-regcache.h.  */
>  
>  void
> -regcache::raw_supply (int n, const void *buf)
> +regcache::raw_supply (int n, gdb::array_view<const gdb_byte> src, float)
>  {
> -  if (buf)
> +  auto dst = register_data (this, n);
> +
> +  if (src.data () != nullptr)
>      {
> -      memcpy (register_data (this, n), buf, register_size (tdesc, n));
> +      copy (src, dst);
>  #ifndef IN_PROCESS_AGENT
>        if (register_status != NULL)
>  	register_status[n] = REG_VALID;
> @@ -343,7 +349,7 @@ regcache::raw_supply (int n, const void *buf)
>      }
>    else
>      {
> -      memset (register_data (this, n), 0, register_size (tdesc, n));
> +      memset (dst.data (), 0, dst.size ());
>  #ifndef IN_PROCESS_AGENT
>        if (register_status != NULL)
>  	register_status[n] = REG_UNAVAILABLE;
> @@ -356,8 +362,8 @@ regcache::raw_supply (int n, const void *buf)
>  void
>  supply_register_zeroed (struct regcache *regcache, int n)
>  {
> -  memset (register_data (regcache, n), 0,
> -	  register_size (regcache->tdesc, n));
> +  auto dst = register_data (regcache, n);
> +  memset (dst.data (), 0, dst.size ());
>  #ifndef IN_PROCESS_AGENT
>    if (regcache->register_status != NULL)
>      regcache->register_status[n] = REG_VALID;
> @@ -426,17 +432,20 @@ supply_register_by_name (struct regcache *regcache,
>  #endif
>  
>  void
> -collect_register (struct regcache *regcache, int n, void *buf)
> +collect_register (struct regcache *regcache, int n, void *vbuf)
>  {
> -  regcache->raw_collect (n, buf);
> +  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
> +  gdb_byte *buf = static_cast<gdb_byte *> (vbuf);
> +  regcache->raw_collect (n, gdb::make_array_view (buf, reg.size / 8), 1.0f);
>  }
>  
>  /* See gdbsupport/common-regcache.h.  */
>  
>  void
> -regcache::raw_collect (int n, void *buf) const
> +regcache::raw_collect (int n, gdb::array_view<gdb_byte> dst, float) const
>  {
> -  memcpy (buf, register_data (this, n), register_size (tdesc, n));
> +  auto src = register_data (this, n);
> +  copy (src, dst);
>  }
>  
>  enum register_status
> @@ -476,8 +485,7 @@ regcache_raw_get_unsigned_by_name (struct regcache *regcache,
>  void
>  collect_register_as_string (struct regcache *regcache, int n, char *buf)
>  {
> -  bin2hex (register_data (regcache, n), buf,
> -	   register_size (regcache->tdesc, n));
> +  bin2hex (register_data (regcache, n), buf);
>  }
>  
>  void
> @@ -524,9 +532,9 @@ regcache::raw_compare (int regnum, const void *buf, int offset) const
>  {
>    gdb_assert (buf != NULL);
>  
> -  const unsigned char *regbuf = register_data (this, regnum);
> -  int size = register_size (tdesc, regnum);
> -  gdb_assert (size >= offset);
> +  gdb::array_view<const gdb_byte> regbuf = register_data (this, regnum);
> +  gdb_assert (offset < regbuf.size ());
> +  regbuf = regbuf.slice (offset);
>  
> -  return (memcmp (buf, regbuf + offset, size - offset) == 0);
> +  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
>  }
> diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
> index 7248bcf5808a..ba093c97c1f0 100644
> --- a/gdbserver/regcache.h
> +++ b/gdbserver/regcache.h
> @@ -50,10 +50,12 @@ struct regcache : public reg_buffer_common
>    enum register_status get_register_status (int regnum) const override;
>  
>    /* See gdbsupport/common-regcache.h.  */
> -  void raw_supply (int regnum, const void *buf) override;
> +  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
> +		   float) override;
>  
>    /* See gdbsupport/common-regcache.h.  */
> -  void raw_collect (int regnum, void *buf) const override;
> +  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
> +		    float) const override;
>  
>    /* See gdbsupport/common-regcache.h.  */
>    bool raw_compare (int regnum, const void *buf, int offset) const override;
> diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
> index 6d98ca8c92ed..75d209f7cbc0 100644
> --- a/gdbsupport/common-regcache.h
> +++ b/gdbsupport/common-regcache.h
> @@ -78,11 +78,44 @@ struct reg_buffer_common
>       buffer.  */
>    virtual register_status get_register_status (int regnum) const = 0;
>  
> -  /* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE.  */
> -  virtual void raw_supply (int regnum, const void *buf) = 0;
> +  /* Supply register REGNUM, whose contents are stored in SRC, to this register
> +     buffer.  */
> +  virtual void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
> +			   float) = 0;

The addition of the 'float' argument is pretty unexpected here.  It's
not mentioned in the commit message or the function comment, nor is it
actually used in any of the function implementations as far as I can
tell.  I'm assuming this is resolving some C++ overload problem.

Sorry if I've missed something ... I've only looked at the diff so far.
I thought I'd reach out before I start playing with the code trying to
figure out what's going on.

If this is an overload workaround then I think this needs a comment
explaining what the problem is that it's working around.

If I'm just missing something, then the comment here still needs an
explanation of what the extra parameter is for.

Thanks,
Andrew


> +
> +  void raw_supply (int regnum, const uint64_t *src)
> +  {
> +    raw_supply (regnum,
> +		gdb::make_array_view ((const gdb_byte *) src, sizeof (*src)),
> +		1.0f);
> +  }
> +
> +  void raw_supply (int regnum, const gdb_byte *src)
> +  {
> +    raw_supply (regnum,
> +		gdb::make_array_view (src,
> +				      regcache_register_size (this, regnum)),
> +		1.0f);
> +  }
> +
> +  /* Collect register REGNUM from this register buffer and store its contents in
> +     DST.  */
> +  virtual void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
> +			    float) const = 0;
> +
> +  void raw_collect (int regnum, uint64_t *dst) const
> +  {
> +    raw_collect (regnum,
> +		 gdb::make_array_view ((gdb_byte *) dst, sizeof (*dst)), 1.0f);
> +  };
>  
> -  /* Collect register REGNUM from REGCACHE and store its contents in BUF.  */
> -  virtual void raw_collect (int regnum, void *buf) const = 0;
> +  void raw_collect (int regnum, gdb_byte *dst)
> +  {
> +    raw_collect (regnum,
> +		 gdb::make_array_view (dst,
> +				       regcache_register_size (this, regnum)),
> +		 1.0f);
> +  }
>  
>    /* Compare the contents of the register stored in the regcache (ignoring the
>       first OFFSET bytes) to the contents of BUF (without any offset).  Returns
> diff --git a/gdbsupport/rsp-low.cc b/gdbsupport/rsp-low.cc
> index 3d8c2002956e..632be265c00c 100644
> --- a/gdbsupport/rsp-low.cc
> +++ b/gdbsupport/rsp-low.cc
> @@ -143,6 +143,14 @@ bin2hex (const gdb_byte *bin, char *hex, int count)
>  
>  /* See rsp-low.h.  */
>  
> +int
> +bin2hex (gdb::array_view<gdb_byte> bin, char *hex)
> +{
> +  return bin2hex (bin.data (), hex, bin.size ());
> +}
> +
> +/* See rsp-low.h.  */
> +
>  std::string
>  bin2hex (const gdb_byte *bin, int count)
>  {
> diff --git a/gdbsupport/rsp-low.h b/gdbsupport/rsp-low.h
> index 327d5f3a0947..1fc2572a7f5c 100644
> --- a/gdbsupport/rsp-low.h
> +++ b/gdbsupport/rsp-low.h
> @@ -54,6 +54,8 @@ extern std::string hex2str (const char *hex, int count);
>  
>  extern int bin2hex (const gdb_byte *bin, char *hex, int count);
>  
> +extern int bin2hex (gdb::array_view<gdb_byte> bin, char *hex);
> +
>  /* Overloaded version of bin2hex that returns a std::string.  */
>  
>  extern std::string bin2hex (const gdb_byte *bin, int count);
> -- 
> 2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 05/24] gdb: change regcache interface to use array_view
  2023-11-13 13:43   ` Andrew Burgess
@ 2023-11-13 14:00     ` Andrew Burgess
  2023-11-13 16:47       ` Simon Marchi
  0 siblings, 1 reply; 51+ messages in thread
From: Andrew Burgess @ 2023-11-13 14:00 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

Andrew Burgess <aburgess@redhat.com> writes:

> I accidentally sent this email only to Simon originally, dropping
> gdb-patches.  Re-sending it now, including the list.
>
> Simon Marchi <simon.marchi@polymtl.ca> writes:
>
>> From: Simon Marchi <simon.marchi@efficios.com>
>>
>> Change most of regcache (and base classes) to use array_view when
>> possible, instead of raw pointers.  By propagating the use of array_view
>> further, it enables having some runtime checks to make sure the what we
>> read from or write to regcaches has the expected length (such as the one
>> in the `copy(array_view, array_view)` function.  It also integrates well
>> when connecting with other APIs already using gdb::array_view.
>>
>> Add some overloads of the methods using raw pointers to avoid having to
>> change all call sites at once (which is both a lot of work and risky).
>>
>> I tried to do this change in small increments, but since many of these
>> functions use each other, it ended up simpler to do it in one shot than
>> having a lot of intermediary / transient changes.
>>
>> This change extends into gdbserver as well, because there is some part
>> of the regcache interface that is shared.
>>
>> Changing the reg_buffer_common interface to use array_view caused some
>> build failures in nat/aarch64-scalable-linux-ptrace.c.  That file
>> currently "takes advantage" of the fact that
>> reg_buffer_common::{raw_supply,raw_collect} operates on `void *`, which
>> IMO is dangerous.  It uses raw_supply/raw_collect directly on
>> uint64_t's, which I guess is fine because it is expected that native
>> code will have the same endianness as the debugged process.  To
>> accomodate that, add some overloads of raw_collect and raw_supply that
>> work on uint64_t.
>>
>> This file also uses raw_collect and raw_supply on `char` pointers.
>> Change it to use `gdb_byte` pointers instead.  Add overloads of
>> raw_collect and raw_supply that work on `gdb_byte *` and make an
>> array_view on the fly using the register's size.  Those call sites could
>> be converted to use array_view with not much work, in which case these
>> overloads could be removed, but I didn't want to do it in this patch, to
>> avoid starting to dig in arch-specific code.
>>
>> Change-Id: I9005f04114543ddff738949e12d85a31855304c2
>> ---
>>  gdb/frame.c                             |   4 +-
>>  gdb/nat/aarch64-scalable-linux-ptrace.c |  20 +-
>>  gdb/regcache.c                          | 478 ++++++++++++++----------
>>  gdb/regcache.h                          | 113 ++++--
>>  gdbserver/regcache.cc                   |  50 +--
>>  gdbserver/regcache.h                    |   6 +-
>>  gdbsupport/common-regcache.h            |  41 +-
>>  gdbsupport/rsp-low.cc                   |   8 +
>>  gdbsupport/rsp-low.h                    |   2 +
>>  9 files changed, 451 insertions(+), 271 deletions(-)
>>
>> diff --git a/gdb/frame.c b/gdb/frame.c
>> index 7077016ccba4..afadb8ac4e73 100644
>> --- a/gdb/frame.c
>> +++ b/gdb/frame.c
>> @@ -1107,9 +1107,9 @@ get_frame_func (frame_info_ptr this_frame)
>>  std::unique_ptr<readonly_detached_regcache>
>>  frame_save_as_regcache (frame_info_ptr this_frame)
>>  {
>> -  auto cooked_read = [this_frame] (int regnum, gdb_byte *buf)
>> +  auto cooked_read = [this_frame] (int regnum, gdb::array_view<gdb_byte> buf)
>>      {
>> -      if (!deprecated_frame_register_read (this_frame, regnum, buf))
>> +      if (!deprecated_frame_register_read (this_frame, regnum, buf.data ()))
>>  	return REG_UNAVAILABLE;
>>        else
>>  	return REG_VALID;
>> diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c
>> index dc0e45fa91ee..b8fb317edaca 100644
>> --- a/gdb/nat/aarch64-scalable-linux-ptrace.c
>> +++ b/gdb/nat/aarch64-scalable-linux-ptrace.c
>> @@ -613,7 +613,7 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
>>  {
>>    gdb::byte_vector sve_state = aarch64_fetch_sve_regset (tid);
>>  
>> -  char *base = (char *) sve_state.data ();
>> +  gdb_byte *base = sve_state.data ();
>>    struct user_sve_header *header
>>      = (struct user_sve_header *) sve_state.data ();
>>  
>> @@ -684,8 +684,10 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
>>  	  reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg);
>>  	}
>>  
>> -      reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
>> -      reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
>> +      reg_buf->raw_supply (AARCH64_FPSR_REGNUM,
>> +			   (const gdb_byte *) &fpsimd->fpsr);
>> +      reg_buf->raw_supply (AARCH64_FPCR_REGNUM,
>> +			   (const gdb_byte *) &fpsimd->fpcr);
>>  
>>        /* Clear the SVE only registers.  */
>>        memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
>> @@ -720,7 +722,7 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
>>    gdb::byte_vector new_state (SVE_PT_SIZE (32, SVE_PT_REGS_SVE), 0);
>>    memcpy (new_state.data (), sve_state.data (), sve_state.size ());
>>    header = (struct user_sve_header *) new_state.data ();
>> -  char *base = (char *) new_state.data ();
>> +  gdb_byte *base = new_state.data ();
>>  
>>    /* Sanity check the data in the header.  */
>>    if (!sve_vl_valid (header->vl)
>> @@ -805,9 +807,11 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
>>  	    }
>>  
>>  	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM))
>> -	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
>> +	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM,
>> +				  (gdb_byte *) &fpsimd->fpsr);
>>  	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM))
>> -	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
>> +	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM,
>> +				  (gdb_byte *) &fpsimd->fpcr);
>>  
>>  	  /* At this point we have collected all the data from the register
>>  	     cache and we are ready to update the FPSIMD register content
>> @@ -894,7 +898,7 @@ aarch64_za_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf,
>>    /* Sanity check.  */
>>    gdb_assert (!za_state.empty ());
>>  
>> -  char *base = (char *) za_state.data ();
>> +  gdb_byte *base = za_state.data ();
>>    struct user_za_header *header = (struct user_za_header *) base;
>>  
>>    /* If we have ZA state, read it.  Otherwise, make the contents of ZA
>> @@ -1027,7 +1031,7 @@ aarch64_za_regs_copy_from_reg_buf (int tid,
>>        /* Fetch the current ZA state from the thread.  */
>>        gdb::byte_vector za_state = aarch64_fetch_za_regset (tid);
>>  
>> -      char *base = (char *) za_state.data ();
>> +      gdb_byte *base = za_state.data ();
>>        struct user_za_header *za_header = (struct user_za_header *) base;
>>        uint64_t svq = sve_vq_from_vl (za_header->vl);
>>  
>> diff --git a/gdb/regcache.c b/gdb/regcache.c
>> index ae5411ba5563..4f3881386f34 100644
>> --- a/gdb/regcache.c
>> +++ b/gdb/regcache.c
>> @@ -221,10 +221,9 @@ regcache::regcache (inferior *inf_for_target_calls, gdbarch *gdbarch,
>>  
>>  readonly_detached_regcache::readonly_detached_regcache (regcache &src)
>>    : readonly_detached_regcache (src.arch (),
>> -				[&src] (int regnum, gdb_byte *buf)
>> -				  {
>> -				    return src.cooked_read (regnum, buf);
>> -				  })
>> +				[&src] (int regnum,
>> +					gdb::array_view<gdb_byte> buf)
>> +				  { return src.cooked_read (regnum, buf, 1.0f); })
>>  {
>>  }
>>  
>> @@ -234,19 +233,36 @@ reg_buffer::arch () const
>>    return m_descr->gdbarch;
>>  }
>>  
>> -/* Return  a pointer to register REGNUM's buffer cache.  */
>> +template<typename ElemType>
>> +gdb::array_view<ElemType>
>> +reg_buffer::register_buffer (int regnum) const
>> +{
>> +  assert_regnum (regnum);
>> +  ElemType *start = &m_registers[m_descr->register_offset[regnum]];
>> +  int size = m_descr->sizeof_register[regnum];
>> +  return gdb::array_view<ElemType> (start, size);
>> +}
>>  
>> -gdb_byte *
>> +/* See regcache.h.  */
>> +
>> +gdb::array_view<const gdb_byte>
>>  reg_buffer::register_buffer (int regnum) const
>>  {
>> -  return m_registers.get () + m_descr->register_offset[regnum];
>> +  return register_buffer<const gdb_byte> (regnum);
>> +}
>> +
>> +/* See regcache.h.  */
>> +
>> +gdb::array_view<gdb_byte>
>> +reg_buffer::register_buffer (int regnum)
>> +{
>> +  return register_buffer<gdb_byte> (regnum);
>>  }
>>  
>>  void
>>  reg_buffer::save (register_read_ftype cooked_read)
>>  {
>>    struct gdbarch *gdbarch = m_descr->gdbarch;
>> -  int regnum;
>>  
>>    /* It should have pseudo registers.  */
>>    gdb_assert (m_has_pseudo);
>> @@ -257,17 +273,17 @@ reg_buffer::save (register_read_ftype cooked_read)
>>       save_reggroup) and mark them as valid.  The full [0 .. gdbarch_num_regs +
>>       gdbarch_num_pseudo_regs) range is checked since some architectures need
>>       to save/restore `cooked' registers that live in memory.  */
>> -  for (regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
>> +  for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
>>      {
>>        if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
>>  	{
>> -	  gdb_byte *dst_buf = register_buffer (regnum);
>> -	  enum register_status status = cooked_read (regnum, dst_buf);
>> +	  gdb::array_view<gdb_byte> dst_buf = register_buffer (regnum);
>> +	  register_status status = cooked_read (regnum, dst_buf);
>>  
>>  	  gdb_assert (status != REG_UNKNOWN);
>>  
>>  	  if (status != REG_VALID)
>> -	    memset (dst_buf, 0, register_size (gdbarch, regnum));
>> +	    memset (dst_buf.data (), 0, dst_buf.size ());
>>  
>>  	  m_register_status[regnum] = status;
>>  	}
>> @@ -294,7 +310,7 @@ regcache::restore (readonly_detached_regcache *src)
>>        if (gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup))
>>  	{
>>  	  if (src->m_register_status[regnum] == REG_VALID)
>> -	    cooked_write (regnum, src->register_buffer (regnum));
>> +	    cooked_write (regnum, src->register_buffer (regnum), 1.0f);
>>  	}
>>      }
>>  }
>> @@ -610,21 +626,30 @@ regcache::raw_update (int regnum)
>>      }
>>  }
>>  
>> -enum register_status
>> -readable_regcache::raw_read (int regnum, gdb_byte *buf)
>> +register_status
>> +readable_regcache::raw_read (int regnum, gdb::array_view<gdb_byte> dst, float)
>>  {
>> -  gdb_assert (buf != NULL);
>> +  assert_regnum (regnum);
>> +  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
>> +
>>    raw_update (regnum);
>>  
>>    if (m_register_status[regnum] != REG_VALID)
>> -    memset (buf, 0, m_descr->sizeof_register[regnum]);
>> +    memset (dst.data (), 0, dst.size ());
>>    else
>> -    memcpy (buf, register_buffer (regnum),
>> -	    m_descr->sizeof_register[regnum]);
>> +    copy (register_buffer (regnum), dst);
>>  
>>    return m_register_status[regnum];
>>  }
>>  
>> +register_status
>> +readable_regcache::raw_read (int regnum, gdb_byte *dst)
>> +{
>> +  assert_regnum (regnum);
>> +  int size = m_descr->sizeof_register[regnum];
>> +  return raw_read (regnum, gdb::make_array_view (dst, size), 1.0f);
>> +}
>> +
>>  enum register_status
>>  regcache_raw_read_signed (struct regcache *regcache, int regnum, LONGEST *val)
>>  {
>> @@ -639,12 +664,14 @@ readable_regcache::raw_read (int regnum, T *val)
>>    assert_regnum (regnum);
>>    size_t len = m_descr->sizeof_register[regnum];
>>    gdb_byte *buf = (gdb_byte *) alloca (len);
>> -  register_status status = raw_read (regnum, buf);
>> +  auto view = gdb::make_array_view (buf, len);
>> +  register_status status = raw_read (regnum, view, 1.0f);
>> +
>>    if (status == REG_VALID)
>> -    *val = extract_integer<T> ({buf, len},
>> -			       gdbarch_byte_order (m_descr->gdbarch));
>> +    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
>>    else
>>      *val = 0;
>> +
>>    return status;
>>  }
>>  
>> @@ -668,13 +695,13 @@ template<typename T, typename>
>>  void
>>  regcache::raw_write (int regnum, T val)
>>  {
>> -  gdb_byte *buf;
>> -
>>    assert_regnum (regnum);
>> -  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
>> -  store_integer (buf, m_descr->sizeof_register[regnum],
>> -		 gdbarch_byte_order (m_descr->gdbarch), val);
>> -  raw_write (regnum, buf);
>> +
>> +  int len = m_descr->sizeof_register[regnum];
>> +  gdb_byte *buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
>> +  auto view = gdb::make_array_view (buf, len);
>> +  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
>> +  raw_write (regnum, view, 1.0f);
>>  }
>>  
>>  void
>> @@ -698,47 +725,61 @@ regcache_raw_get_signed (struct regcache *regcache, int regnum)
>>    return value;
>>  }
>>  
>> -enum register_status
>> -readable_regcache::cooked_read (int regnum, gdb_byte *buf)
>> +/* See regcache.h.  */
>> +
>> +register_status
>> +readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst,
>> +				float)
>>  {
>>    gdb_assert (regnum >= 0);
>>    gdb_assert (regnum < m_descr->nr_cooked_registers);
>> +
>>    if (regnum < num_raw_registers ())
>> -    return raw_read (regnum, buf);
>> -  else if (m_has_pseudo
>> -	   && m_register_status[regnum] != REG_UNKNOWN)
>> +    return raw_read (regnum, dst, 1.0f);
>> +
>> +  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
>> +
>> +  if (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
>>      {
>>        if (m_register_status[regnum] == REG_VALID)
>> -	memcpy (buf, register_buffer (regnum),
>> -		m_descr->sizeof_register[regnum]);
>> +	copy (register_buffer (regnum), dst);
>>        else
>> -	memset (buf, 0, m_descr->sizeof_register[regnum]);
>> +	memset (dst.data (), 0, dst.size ());
>>  
>>        return m_register_status[regnum];
>>      }
>>    else if (gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
>>      {
>> -      struct value *computed;
>> -      enum register_status result = REG_VALID;
>> -
>> +      register_status result = REG_VALID;
>>        scoped_value_mark mark;
>> +      value *computed
>> +	= gdbarch_pseudo_register_read_value (m_descr->gdbarch, this, regnum);
>>  
>> -      computed = gdbarch_pseudo_register_read_value (m_descr->gdbarch,
>> -						     this, regnum);
>>        if (computed->entirely_available ())
>> -	memcpy (buf, computed->contents_raw ().data (),
>> -		m_descr->sizeof_register[regnum]);
>> +	copy (computed->contents_raw (), dst);
>>        else
>>  	{
>> -	  memset (buf, 0, m_descr->sizeof_register[regnum]);
>> +	  memset (dst.data (), 0, dst.size ());
>>  	  result = REG_UNAVAILABLE;
>>  	}
>>  
>>        return result;
>>      }
>>    else
>> -    return gdbarch_pseudo_register_read (m_descr->gdbarch, this,
>> -					 regnum, buf);
>> +    return gdbarch_pseudo_register_read (m_descr->gdbarch, this, regnum,
>> +					 dst.data ());
>> +}
>> +
>> +/* See regcache.h.  */
>> +
>> +register_status
>> +readable_regcache::cooked_read (int regnum, gdb_byte *dst)
>> +{
>> +  gdb_assert (regnum >= 0);
>> +  gdb_assert (regnum < m_descr->nr_cooked_registers);
>> +
>> +  int size = m_descr->sizeof_register[regnum];
>> +  return cooked_read (regnum, gdb::make_array_view (dst, size), 1.0f);
>>  }
>>  
>>  struct value *
>> @@ -760,8 +801,8 @@ readable_regcache::cooked_read_value (int regnum)
>>        /* It is more efficient in general to do this delegation in this
>>  	 direction than in the other one, even though the value-based
>>  	 API is preferred.  */
>> -      if (cooked_read (regnum,
>> -		       result->contents_raw ().data ()) == REG_UNAVAILABLE)
>> +      if (cooked_read (regnum, result->contents_raw (), 1.0f)
>> +	  == REG_UNAVAILABLE)
>>  	result->mark_bytes_unavailable (0,
>>  					result->type ()->length ());
>>  
>> @@ -787,10 +828,10 @@ readable_regcache::cooked_read (int regnum, T *val)
>>    gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
>>    size_t len = m_descr->sizeof_register[regnum];
>>    gdb_byte *buf = (gdb_byte *) alloca (len);
>> -  register_status status = cooked_read (regnum, buf);
>> +  auto view = gdb::make_array_view (buf, len);
>> +  register_status status = cooked_read (regnum, view, 1.0f);
>>    if (status == REG_VALID)
>> -    *val = extract_integer<T> ({buf, len},
>> -			       gdbarch_byte_order (m_descr->gdbarch));
>> +    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
>>    else
>>      *val = 0;
>>    return status;
>> @@ -816,13 +857,14 @@ template<typename T, typename>
>>  void
>>  regcache::cooked_write (int regnum, T val)
>>  {
>> -  gdb_byte *buf;
>> +  gdb_assert (regnum >= 0);
>> +  gdb_assert (regnum < m_descr->nr_cooked_registers);
>>  
>> -  gdb_assert (regnum >=0 && regnum < m_descr->nr_cooked_registers);
>> -  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
>> -  store_integer (buf, m_descr->sizeof_register[regnum],
>> -		 gdbarch_byte_order (m_descr->gdbarch), val);
>> -  cooked_write (regnum, buf);
>> +  int size = m_descr->sizeof_register[regnum];
>> +  gdb_byte *buf = (gdb_byte *) alloca (size);
>> +  auto view = gdb::make_array_view (buf, size);
>> +  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
>> +  cooked_write (regnum, view, 1.0f);
>>  }
>>  
>>  void
>> @@ -834,11 +876,10 @@ regcache_cooked_write_unsigned (struct regcache *regcache, int regnum,
>>  }
>>  
>>  void
>> -regcache::raw_write (int regnum, const gdb_byte *buf)
>> +regcache::raw_write (int regnum, gdb::array_view<const gdb_byte> src, float)
>>  {
>> -
>> -  gdb_assert (buf != NULL);
>>    assert_regnum (regnum);
>> +  gdb_assert (src.size () == m_descr->sizeof_register[regnum]);
>>  
>>    /* On the sparc, writing %g0 is a no-op, so we don't even want to
>>       change the registers array if something writes to this register.  */
>> @@ -848,15 +889,15 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
>>    /* If we have a valid copy of the register, and new value == old
>>       value, then don't bother doing the actual store.  */
>>    if (get_register_status (regnum) == REG_VALID
>> -      && (memcmp (register_buffer (regnum), buf,
>> -		  m_descr->sizeof_register[regnum]) == 0))
>> +      && (memcmp (register_buffer (regnum).data (), src.data (), src.size ())
>> +	  == 0))
>>      return;
>>  
>>    gdb::optional<scoped_restore_current_thread> maybe_restore_thread
>>      = maybe_switch_inferior (m_inf_for_target_calls);
>>  
>>    target_prepare_to_store (this);
>> -  raw_supply (regnum, buf);
>> +  raw_supply (regnum, src, 1.0f);
>>  
>>    /* Invalidate the register after it is written, in case of a
>>       failure.  */
>> @@ -871,211 +912,248 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
>>  }
>>  
>>  void
>> -regcache::cooked_write (int regnum, const gdb_byte *buf)
>> +regcache::raw_write (int regnum, const gdb_byte *src)
>> +{
>> +  assert_regnum (regnum);
>> +
>> +  int size = m_descr->sizeof_register[regnum];
>> +  raw_write (regnum, gdb::make_array_view (src, size), 1.0f);
>> +}
>> +
>> +/* See regcache.h.  */
>> +
>> +void
>> +regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float)
>>  {
>>    gdb_assert (regnum >= 0);
>>    gdb_assert (regnum < m_descr->nr_cooked_registers);
>> +
>>    if (regnum < num_raw_registers ())
>> -    raw_write (regnum, buf);
>> +    raw_write (regnum, src, 1.0f);
>>    else
>> -    gdbarch_pseudo_register_write (m_descr->gdbarch, this,
>> -				   regnum, buf);
>> +    gdbarch_pseudo_register_write (m_descr->gdbarch, this, regnum,
>> +				   src.data ());
>>  }
>>  
>>  /* See regcache.h.  */
>>  
>> -enum register_status
>> -readable_regcache::read_part (int regnum, int offset, int len,
>> -			      gdb_byte *out, bool is_raw)
>> +void
>> +regcache::cooked_write (int regnum, const gdb_byte *src)
>> +{
>> +  gdb_assert (regnum >= 0);
>> +  gdb_assert (regnum < m_descr->nr_cooked_registers);
>> +
>> +  int size = m_descr->sizeof_register[regnum];
>> +  return cooked_write (regnum, gdb::make_array_view (src, size), 1.0f);
>> +}
>> +
>> +/* See regcache.h.  */
>> +
>> +register_status
>> +readable_regcache::read_part (int regnum, int offset,
>> +			      gdb::array_view<gdb_byte> dst, bool is_raw)
>>  {
>>    int reg_size = register_size (arch (), regnum);
>>  
>> -  gdb_assert (out != NULL);
>>    gdb_assert (offset >= 0);
>> -  gdb_assert (len >= 0 && offset + len <= reg_size);
>> +  gdb_assert (offset + dst.size () <= reg_size);
>>  
>> -  if (len == 0)
>> +  if (dst.size () == 0)
>>      {
>>        /* Nothing to do.  */
>>        return REG_VALID;
>>      }
>>  
>> -  if (len == reg_size)
>> +  if (dst.size () == reg_size)
>>      {
>>        /* Read the full register.  */
>> -      return (is_raw) ? raw_read (regnum, out) : cooked_read (regnum, out);
>> +      if (is_raw)
>> +	return raw_read (regnum, dst, 1.0f);
>> +      else
>> +	return cooked_read (regnum, dst, 1.0f);
>>      }
>>  
>> -  enum register_status status;
>> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
>> -
>>    /* Read full register to buffer.  */
>> -  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
>> +  register_status status;
>> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
>> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
>> +
>> +  if (is_raw)
>> +    status = raw_read (regnum, reg, 1.0f);
>> +  else
>> +    status = cooked_read (regnum, reg, 1.0f);
>> +
>>    if (status != REG_VALID)
>>      return status;
>>  
>>    /* Copy out.  */
>> -  memcpy (out, reg + offset, len);
>> +  copy (reg.slice (offset, dst.size ()), dst);
>>    return REG_VALID;
>>  }
>>  
>>  /* See regcache.h.  */
>>  
>>  void
>> -reg_buffer::raw_collect_part (int regnum, int offset, int len,
>> -			      gdb_byte *out) const
>> +reg_buffer::raw_collect_part (int regnum, int offset,
>> +			      gdb::array_view<gdb_byte> dst) const
>>  {
>>    int reg_size = register_size (arch (), regnum);
>>  
>> -  gdb_assert (out != nullptr);
>>    gdb_assert (offset >= 0);
>> -  gdb_assert (len >= 0 && offset + len <= reg_size);
>> +  gdb_assert (offset + dst.size () <= reg_size);
>>  
>> -  if (len == 0)
>> +  if (dst.size () == 0)
>>      {
>>        /* Nothing to do.  */
>>        return;
>>      }
>>  
>> -  if (len == reg_size)
>> +  if (dst.size () == reg_size)
>>      {
>>        /* Collect the full register.  */
>> -      return raw_collect (regnum, out);
>> +      return raw_collect (regnum, dst, 1.0f);
>>      }
>>  
>>    /* Read to buffer, then write out.  */
>> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
>> -  raw_collect (regnum, reg);
>> -  memcpy (out, reg + offset, len);
>> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
>> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
>> +  raw_collect (regnum, reg, 1.0f);
>> +  copy (reg.slice (offset, dst.size ()), dst);
>>  }
>>  
>>  /* See regcache.h.  */
>>  
>> -enum register_status
>> -regcache::write_part (int regnum, int offset, int len,
>> -		      const gdb_byte *in, bool is_raw)
>> +register_status
>> +regcache::write_part (int regnum, int offset,
>> +		      gdb::array_view<const gdb_byte> src, bool is_raw)
>>  {
>>    int reg_size = register_size (arch (), regnum);
>>  
>> -  gdb_assert (in != NULL);
>>    gdb_assert (offset >= 0);
>> -  gdb_assert (len >= 0 && offset + len <= reg_size);
>> +  gdb_assert (offset + src.size () <= reg_size);
>>  
>> -  if (len == 0)
>> +  if (src.size () == 0)
>>      {
>>        /* Nothing to do.  */
>>        return REG_VALID;
>>      }
>>  
>> -  if (len == reg_size)
>> +  if (src.size () == reg_size)
>>      {
>>        /* Write the full register.  */
>> -      (is_raw) ? raw_write (regnum, in) : cooked_write (regnum, in);
>> +      if (is_raw)
>> +	raw_write (regnum, src, 1.0f);
>> +      else
>> +	cooked_write (regnum, src, 1.0f);
>> +
>>        return REG_VALID;
>>      }
>>  
>> -  enum register_status status;
>> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
>> -
>>    /* Read existing register to buffer.  */
>> -  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
>> +  register_status status;
>> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
>> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
>> +
>> +  if (is_raw)
>> +    status = raw_read (regnum, reg, 1.0f);
>> +  else
>> +    status = cooked_read (regnum, reg, 1.0f);
>> +
>>    if (status != REG_VALID)
>>      return status;
>>  
>>    /* Update buffer, then write back to regcache.  */
>> -  memcpy (reg + offset, in, len);
>> -  is_raw ? raw_write (regnum, reg) : cooked_write (regnum, reg);
>> +  copy (src, reg.slice (offset, src.size ()));
>> +
>> +  if (is_raw)
>> +    raw_write (regnum, reg, 1.0f);
>> +  else
>> +    cooked_write (regnum, reg, 1.0f);
>> +
>>    return REG_VALID;
>>  }
>>  
>>  /* See regcache.h.  */
>>  
>>  void
>> -reg_buffer::raw_supply_part (int regnum, int offset, int len,
>> -			     const gdb_byte *in)
>> +reg_buffer::raw_supply_part (int regnum, int offset,
>> +			     gdb::array_view<const gdb_byte> src)
>>  {
>>    int reg_size = register_size (arch (), regnum);
>>  
>> -  gdb_assert (in != nullptr);
>>    gdb_assert (offset >= 0);
>> -  gdb_assert (len >= 0 && offset + len <= reg_size);
>> +  gdb_assert (offset + src.size () <= reg_size);
>>  
>> -  if (len == 0)
>> +  if (src.size () == 0)
>>      {
>>        /* Nothing to do.  */
>>        return;
>>      }
>>  
>> -  if (len == reg_size)
>> +  if (src.size () == reg_size)
>>      {
>>        /* Supply the full register.  */
>> -      return raw_supply (regnum, in);
>> +      return raw_supply (regnum, src, 1.0f);
>>      }
>>  
>> -  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
>> -
>>    /* Read existing value to buffer.  */
>> -  raw_collect (regnum, reg);
>> +  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
>> +  auto reg = gdb::make_array_view (reg_buf, reg_size);
>> +  raw_collect (regnum, reg, 1.0f);
>>  
>>    /* Write to buffer, then write out.  */
>> -  memcpy (reg + offset, in, len);
>> -  raw_supply (regnum, reg);
>> +  copy (src, reg.slice (offset, src.size ()));
>> +  raw_supply (regnum, reg, 1.0f);
>>  }
>>  
>> -enum register_status
>> -readable_regcache::raw_read_part (int regnum, int offset, int len,
>> -				  gdb_byte *buf)
>> +register_status
>> +readable_regcache::raw_read_part (int regnum, int offset,
>> +				  gdb::array_view<gdb_byte> dst)
>>  {
>>    assert_regnum (regnum);
>> -  return read_part (regnum, offset, len, buf, true);
>> +  return read_part (regnum, offset, dst, true);
>>  }
>>  
>>  /* See regcache.h.  */
>>  
>>  void
>> -regcache::raw_write_part (int regnum, int offset, int len,
>> -			  const gdb_byte *buf)
>> +regcache::raw_write_part (int regnum, int offset,
>> +			  gdb::array_view<const gdb_byte> src)
>>  {
>>    assert_regnum (regnum);
>> -  write_part (regnum, offset, len, buf, true);
>> +  write_part (regnum, offset, src, true);
>>  }
>>  
>>  /* See regcache.h.  */
>>  
>> -enum register_status
>> -readable_regcache::cooked_read_part (int regnum, int offset, int len,
>> -				     gdb_byte *buf)
>> +register_status
>> +readable_regcache::cooked_read_part (int regnum, int offset,
>> +				     gdb::array_view<gdb_byte> dst)
>>  {
>>    gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
>> -  return read_part (regnum, offset, len, buf, false);
>> +  return read_part (regnum, offset, dst, false);
>>  }
>>  
>>  /* See regcache.h.  */
>>  
>>  void
>> -regcache::cooked_write_part (int regnum, int offset, int len,
>> -			     const gdb_byte *buf)
>> +regcache::cooked_write_part (int regnum, int offset,
>> +			     gdb::array_view<const gdb_byte> src)
>>  {
>>    gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
>> -  write_part (regnum, offset, len, buf, false);
>> +  write_part (regnum, offset, src, false);
>>  }
>>  
>>  /* See gdbsupport/common-regcache.h.  */
>>  
>>  void
>> -reg_buffer::raw_supply (int regnum, const void *buf)
>> +reg_buffer::raw_supply (int regnum, gdb::array_view<const gdb_byte> src, float)
>>  {
>> -  void *regbuf;
>> -  size_t size;
>> -
>> -  assert_regnum (regnum);
>> -
>> -  regbuf = register_buffer (regnum);
>> -  size = m_descr->sizeof_register[regnum];
>> +  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
>>  
>> -  if (buf)
>> +  if (src.data () != nullptr)
>>      {
>> -      memcpy (regbuf, buf, size);
>> +      copy (src, dst);
>>        m_register_status[regnum] = REG_VALID;
>>      }
>>    else
>> @@ -1083,7 +1161,7 @@ reg_buffer::raw_supply (int regnum, const void *buf)
>>        /* This memset not strictly necessary, but better than garbage
>>  	 in case the register value manages to escape somewhere (due
>>  	 to a bug, no less).  */
>> -      memset (regbuf, 0, size);
>> +      memset (dst.data (), 0, dst.size ());
>>        m_register_status[regnum] = REG_UNAVAILABLE;
>>      }
>>  }
>> @@ -1091,19 +1169,25 @@ reg_buffer::raw_supply (int regnum, const void *buf)
>>  /* See regcache.h.  */
>>  
>>  void
>> -reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
>> -				int addr_len, bool is_signed)
>> +reg_buffer::raw_supply (int regnum, const void *src)
>>  {
>> -  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
>> -  gdb_byte *regbuf;
>> -  size_t regsize;
>> -
>>    assert_regnum (regnum);
>>  
>> -  regbuf = register_buffer (regnum);
>> -  regsize = m_descr->sizeof_register[regnum];
>> +  int size = m_descr->sizeof_register[regnum];
>> +  raw_supply (regnum, gdb::make_array_view ((const gdb_byte *) src, size),
>> +	      1.0f);
>> +}
>> +
>> +/* See regcache.h.  */
>> +
>> +void
>> +reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr, int addr_len,
>> +				bool is_signed)
>> +{
>> +  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
>> +  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
>>  
>> -  copy_integer_to_size (regbuf, regsize, addr, addr_len, is_signed,
>> +  copy_integer_to_size (dst.data (), dst.size (), addr, addr_len, is_signed,
>>  			byte_order);
>>    m_register_status[regnum] = REG_VALID;
>>  }
>> @@ -1113,32 +1197,31 @@ reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
>>  void
>>  reg_buffer::raw_supply_zeroed (int regnum)
>>  {
>> -  void *regbuf;
>> -  size_t size;
>> -
>> -  assert_regnum (regnum);
>> -
>> -  regbuf = register_buffer (regnum);
>> -  size = m_descr->sizeof_register[regnum];
>> -
>> -  memset (regbuf, 0, size);
>> +  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
>> +  memset (dst.data (), 0, dst.size ());
>>    m_register_status[regnum] = REG_VALID;
>>  }
>>  
>>  /* See gdbsupport/common-regcache.h.  */
>>  
>>  void
>> -reg_buffer::raw_collect (int regnum, void *buf) const
>> +reg_buffer::raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
>> +			 float) const
>>  {
>> -  const void *regbuf;
>> -  size_t size;
>> +  gdb::array_view<const gdb_byte> src = register_buffer (regnum);
>> +  copy (src, dst);
>> +}
>>  
>> -  gdb_assert (buf != NULL);
>> +/* See regcache.h.  */
>> +
>> +void
>> +reg_buffer::raw_collect (int regnum, void *dst) const
>> +{
>>    assert_regnum (regnum);
>>  
>> -  regbuf = register_buffer (regnum);
>> -  size = m_descr->sizeof_register[regnum];
>> -  memcpy (buf, regbuf, size);
>> +  int size = m_descr->sizeof_register[regnum];
>> +  return raw_collect (regnum, gdb::make_array_view ((gdb_byte *) dst, size),
>> +		      1.0f);
>>  }
>>  
>>  /* See regcache.h.  */
>> @@ -1147,16 +1230,9 @@ void
>>  reg_buffer::raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
>>  				 bool is_signed) const
>>  {
>> -  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
>> -  const gdb_byte *regbuf;
>> -  size_t regsize;
>> -
>> -  assert_regnum (regnum);
>> -
>> -  regbuf = register_buffer (regnum);
>> -  regsize = m_descr->sizeof_register[regnum];
>> -
>> -  copy_integer_to_size (addr, addr_len, regbuf, regsize, is_signed,
>> +  gdb::array_view<const gdb_byte> dst = register_buffer (regnum);
>> +  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
>> +  copy_integer_to_size (addr, addr_len, dst.data (), dst.size (), is_signed,
>>  			byte_order);
>>  }
>>  
>> @@ -1175,7 +1251,8 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
>>  
>>    if (out_buf != nullptr)
>>      {
>> -      raw_collect_part (regnum, 0, reg_size, out_buf + offs);
>> +      raw_collect_part (regnum, 0,
>> +			gdb::make_array_view (out_buf + offs, reg_size));
>>  
>>        /* Ensure any additional space is cleared.  */
>>        if (slot_size > reg_size)
>> @@ -1186,12 +1263,14 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
>>        /* Zero-extend the register value if the slot is smaller than the register.  */
>>        if (slot_size < register_size (gdbarch, regnum))
>>  	out_regcache->raw_supply_zeroed (regnum);
>> -      out_regcache->raw_supply_part (regnum, 0, reg_size, in_buf + offs);
>> +      out_regcache->raw_supply_part (regnum, 0,
>> +				     gdb::make_array_view (in_buf + offs,
>> +							   reg_size));
>>      }
>>    else
>>      {
>>        /* Invalidate the register.  */
>> -      out_regcache->raw_supply (regnum, nullptr);
>> +      out_regcache->raw_supply (regnum, {});
>>      }
>>  }
>>  
>> @@ -1322,13 +1401,12 @@ bool
>>  reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
>>  {
>>    gdb_assert (buf != NULL);
>> -  assert_regnum (regnum);
>>  
>> -  const char *regbuf = (const char *) register_buffer (regnum);
>> -  size_t size = m_descr->sizeof_register[regnum];
>> -  gdb_assert (size >= offset);
>> +  gdb::array_view<const gdb_byte> regbuf = register_buffer (regnum);
>> +  gdb_assert (offset < regbuf.size ());
>> +  regbuf = regbuf.slice (offset);
>>  
>> -  return (memcmp (buf, regbuf + offset, size - offset) == 0);
>> +  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
>>  }
>>  
>>  /* Special handling for register PC.  */
>> @@ -1417,17 +1495,15 @@ regcache::debug_print_register (const char *func,  int regno)
>>    if (regno >= 0 && regno < gdbarch_num_regs (gdbarch))
>>      {
>>        enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
>> -      int size = register_size (gdbarch, regno);
>> -      gdb_byte *buf = register_buffer (regno);
>> +      gdb::array_view<gdb_byte> buf = register_buffer (regno);
>>  
>>        gdb_printf (gdb_stdlog, " = ");
>> -      for (int i = 0; i < size; i++)
>> -	{
>> -	  gdb_printf (gdb_stdlog, "%02x", buf[i]);
>> -	}
>> -      if (size <= sizeof (LONGEST))
>> +      for (gdb_byte byte : buf)
>> +	gdb_printf (gdb_stdlog, "%02x", byte);
>> +
>> +      if (buf.size () <= sizeof (LONGEST))
>>  	{
>> -	  ULONGEST val = extract_unsigned_integer (buf, size, byte_order);
>> +	  ULONGEST val = extract_unsigned_integer (buf, byte_order);
>>  
>>  	  gdb_printf (gdb_stdlog, " %s %s",
>>  		      core_addr_to_string_nz (val), plongest (val));
>> @@ -1889,7 +1965,7 @@ cooked_read_test (struct gdbarch *gdbarch)
>>    readwrite.set_ptid (mockctx.mock_ptid);
>>    gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum));
>>  
>> -  readwrite.raw_read (nonzero_regnum, buf.data ());
>> +  readwrite.raw_read (nonzero_regnum, buf, 1.0f);
>>  
>>    /* raw_read calls target_fetch_registers.  */
>>    SELF_CHECK (mockctx.mock_target.fetch_registers_called > 0);
>> @@ -1910,9 +1986,8 @@ cooked_read_test (struct gdbarch *gdbarch)
>>  
>>        gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
>>  
>> -      SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum,
>> -						      inner_buf.data ()));
>> -
>> +      SELF_CHECK (REG_VALID
>> +		  == readwrite.cooked_read (regnum, inner_buf, 1.0f));
>>        SELF_CHECK (mockctx.mock_target.fetch_registers_called == 0);
>>        SELF_CHECK (mockctx.mock_target.store_registers_called == 0);
>>        SELF_CHECK (mockctx.mock_target.xfer_partial_called == 0);
>> @@ -1932,8 +2007,7 @@ cooked_read_test (struct gdbarch *gdbarch)
>>  	continue;
>>  
>>        gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
>> -      enum register_status status = readonly.cooked_read (regnum,
>> -							  inner_buf.data ());
>> +      register_status status = readonly.cooked_read (regnum, inner_buf, 1.0f);
>>  
>>        if (regnum < gdbarch_num_regs (gdbarch))
>>  	{
>> @@ -2023,8 +2097,8 @@ cooked_write_test (struct gdbarch *gdbarch)
>>  	      && regnum <= gdbarch_num_regs (gdbarch) + 4))
>>  	continue;
>>  
>> -      std::vector<gdb_byte> expected (register_size (gdbarch, regnum), 0);
>> -      std::vector<gdb_byte> buf (register_size (gdbarch, regnum), 0);
>> +      gdb::byte_vector expected (register_size (gdbarch, regnum), 0);
>> +      gdb::byte_vector buf (register_size (gdbarch, regnum), 0);
>>        const auto type = register_type (gdbarch, regnum);
>>  
>>        if (type->code () == TYPE_CODE_FLT
>> @@ -2079,9 +2153,9 @@ cooked_write_test (struct gdbarch *gdbarch)
>>  	  SELF_CHECK (0);
>>  	}
>>  
>> -      readwrite.cooked_write (regnum, expected.data ());
>> +      readwrite.cooked_write (regnum, expected, 1.0f);
>>  
>> -      SELF_CHECK (readwrite.cooked_read (regnum, buf.data ()) == REG_VALID);
>> +      SELF_CHECK (readwrite.cooked_read (regnum, buf, 1.0f) == REG_VALID);
>>        SELF_CHECK (expected == buf);
>>      }
>>  }
>> diff --git a/gdb/regcache.h b/gdb/regcache.h
>> index 57ddac465f09..123c9248c345 100644
>> --- a/gdb/regcache.h
>> +++ b/gdb/regcache.h
>> @@ -20,6 +20,7 @@
>>  #ifndef REGCACHE_H
>>  #define REGCACHE_H
>>  
>> +#include "gdbsupport/array-view.h"
>>  #include "gdbsupport/common-regcache.h"
>>  #include "gdbsupport/function-view.h"
>>  
>> @@ -172,8 +173,8 @@ extern struct type *register_type (struct gdbarch *gdbarch, int regnum);
>>     
>>  extern int register_size (struct gdbarch *gdbarch, int regnum);
>>  
>> -typedef gdb::function_view<register_status (int regnum, gdb_byte *buf)>
>> -  register_read_ftype;
>> +using register_read_ftype
>> +  = gdb::function_view<register_status (int, gdb::array_view<gdb_byte>)>;
>>  
>>  /* A (register_number, register_value) pair.  */
>>  
>> @@ -199,7 +200,11 @@ class reg_buffer : public reg_buffer_common
>>    enum register_status get_register_status (int regnum) const override;
>>  
>>    /* See gdbsupport/common-regcache.h.  */
>> -  void raw_collect (int regnum, void *buf) const override;
>> +  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
>> +		    float) const override;
>> +
>> +  /* Deprecated overload of the above.  */
>> +  void raw_collect (int regnum, void *dst) const;
>>  
>>    /* Collect register REGNUM from REGCACHE.  Store collected value as an integer
>>       at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
>> @@ -209,17 +214,24 @@ class reg_buffer : public reg_buffer_common
>>    void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
>>  			    bool is_signed) const;
>>  
>> -  /* Collect register REGNUM from REGCACHE, starting at OFFSET in register,
>> -     reading only LEN.  */
>> -  void raw_collect_part (int regnum, int offset, int len, gdb_byte *out) const;
>> +  /* Collect part of register REGNUM from this register buffer.  Start at OFFSET
>> +     in register.  The size is given by the size of DST.  */
>> +  void raw_collect_part (int regnum, int offset,
>> +			 gdb::array_view<gdb_byte> dst) const;
>> +
>> +  /* Deprecated overload of the above.  */
>> +  void raw_collect_part (int regnum, int offset, int len, gdb_byte *dst) const
>> +  { raw_collect_part (regnum, offset, gdb::make_array_view (dst, len)); }
>>  
>>    /* See gdbsupport/common-regcache.h.  */
>> -  void raw_supply (int regnum, const void *buf) override;
>> +  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
>> +		   float) override;
>> +
>> +  /* Deprecated overload of the above.  */
>> +  void raw_supply (int regnum, const void *src);
>>  
>>    void raw_supply (int regnum, const reg_buffer &src)
>> -  {
>> -    raw_supply (regnum, src.register_buffer (regnum));
>> -  }
>> +  { raw_supply (regnum, src.register_buffer (regnum), 1.0f); }
>>  
>>    /* Supply register REGNUM to REGCACHE.  Value to supply is an integer stored
>>       at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
>> @@ -234,9 +246,11 @@ class reg_buffer : public reg_buffer_common
>>       unavailable).  */
>>    void raw_supply_zeroed (int regnum);
>>  
>> -  /* Supply register REGNUM to REGCACHE, starting at OFFSET in register, writing
>> -     only LEN, without editing the rest of the register.  */
>> -  void raw_supply_part (int regnum, int offset, int len, const gdb_byte *in);
>> +  /* Supply part of register REGNUM to this register buffer.  Start at OFFSET in
>> +     the register.  The size is given by the size of SRC.  The rest of the
>> +     register left untouched.  */
>> +  void raw_supply_part (int regnum, int offset,
>> +			gdb::array_view<const gdb_byte> src);
>>  
>>    void invalidate (int regnum);
>>  
>> @@ -251,7 +265,11 @@ class reg_buffer : public reg_buffer_common
>>  
>>    int num_raw_registers () const;
>>  
>> -  gdb_byte *register_buffer (int regnum) const;
>> +  /* Return a view on register REGNUM's buffer cache.  */
>> +  template <typename ElemType>
>> +  gdb::array_view<ElemType> register_buffer (int regnum) const;
>> +  gdb::array_view<const gdb_byte> register_buffer (int regnum) const;
>> +  gdb::array_view<gdb_byte> register_buffer (int regnum);
>>  
>>    /* Save a register cache.  The set of registers saved into the
>>       regcache determined by the save_reggroup.  COOKED_READ returns
>> @@ -281,27 +299,42 @@ class readable_regcache : public reg_buffer
>>  
>>    /* Transfer a raw register [0..NUM_REGS) from core-gdb to this regcache,
>>       return its value in *BUF and return its availability status.  */
>> +  register_status raw_read (int regnum, gdb::array_view<gdb_byte> dst, float);
>> +
>> +  /* Deprecated overload of the above.  */
>> +  register_status raw_read (int regnum, gdb_byte *dst);
>>  
>> -  enum register_status raw_read (int regnum, gdb_byte *buf);
>>    template<typename T, typename = RequireLongest<T>>
>> -  enum register_status raw_read (int regnum, T *val);
>> +  register_status raw_read (int regnum, T *val);
>>  
>>    /* Partial transfer of raw registers.  Return the status of the register.  */
>> -  enum register_status raw_read_part (int regnum, int offset, int len,
>> -				      gdb_byte *buf);
>> +  register_status raw_read_part (int regnum, int offset,
>> +				 gdb::array_view<gdb_byte> dst);
>> +
>> +  /* Deprecated overload of the above.  */
>> +  register_status raw_read_part (int regnum, int offset, int len,
>> +				 gdb_byte *dst)
>> +  { return raw_read_part (regnum, offset, gdb::make_array_view (dst, len)); }
>>  
>>    /* Make certain that the register REGNUM is up-to-date.  */
>>    virtual void raw_update (int regnum) = 0;
>>  
>>    /* Transfer a raw register [0..NUM_REGS+NUM_PSEUDO_REGS) from core-gdb to
>> -     this regcache, return its value in *BUF and return its availability status.  */
>> -  enum register_status cooked_read (int regnum, gdb_byte *buf);
>> +     this regcache, return its value in DST and return its availability status.  */
>> +  register_status cooked_read (int regnum, gdb::array_view<gdb_byte> dst,
>> +			       float);
>> +  register_status cooked_read (int regnum, gdb_byte *dst);
>> +
>>    template<typename T, typename = RequireLongest<T>>
>> -  enum register_status cooked_read (int regnum, T *val);
>> +  register_status cooked_read (int regnum, T *val);
>>  
>>    /* Partial transfer of a cooked register.  */
>> -  enum register_status cooked_read_part (int regnum, int offset, int len,
>> -					 gdb_byte *buf);
>> +  register_status cooked_read_part (int regnum, int offset,
>> +				    gdb::array_view<gdb_byte> dst);
>> +
>> +  /* Deprecated overload of the above.  */
>> +  register_status cooked_read_part (int regnum, int offset, int len, gdb_byte *src)
>> +  { return cooked_read_part (regnum, offset, gdb::make_array_view (src, len)); }
>>  
>>    /* Read register REGNUM from the regcache and return a new value.  This
>>       will call mark_value_bytes_unavailable as appropriate.  */
>> @@ -311,8 +344,8 @@ class readable_regcache : public reg_buffer
>>  
>>    /* Perform a partial register transfer using a read, modify, write
>>       operation.  Will fail if register is currently invalid.  */
>> -  enum register_status read_part (int regnum, int offset, int len,
>> -				  gdb_byte *out, bool is_raw);
>> +  register_status read_part (int regnum, int offset,
>> +			     gdb::array_view<gdb_byte> dst, bool is_raw);
>>  };
>>  
>>  /* Buffer of registers, can be read and written.  */
>> @@ -354,13 +387,19 @@ class regcache : public detached_regcache
>>    /* Update the value of raw register REGNUM (in the range [0..NUM_REGS)) and
>>       transfer its value to core-gdb.  */
>>  
>> -  void raw_write (int regnum, const gdb_byte *buf);
>> +  void raw_write (int regnum, gdb::array_view<const gdb_byte> src, float);
>> +
>> +  /* Deprecated overload of the above.  */
>> +  void raw_write (int regnum, const gdb_byte *src);
>>  
>>    template<typename T, typename = RequireLongest<T>>
>>    void raw_write (int regnum, T val);
>>  
>>    /* Transfer of pseudo-registers.  */
>> -  void cooked_write (int regnum, const gdb_byte *buf);
>> +  void cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float);
>> +
>> +  /* Deprecated overload of the above.  */
>> +  void cooked_write (int regnum, const gdb_byte *src);
>>  
>>    template<typename T, typename = RequireLongest<T>>
>>    void cooked_write (int regnum, T val);
>> @@ -369,12 +408,21 @@ class regcache : public detached_regcache
>>  
>>    /* Partial transfer of raw registers.  Perform read, modify, write style
>>       operations.  */
>> -  void raw_write_part (int regnum, int offset, int len, const gdb_byte *buf);
>> +  void raw_write_part (int regnum, int offset,
>> +		       gdb::array_view<const gdb_byte> src);
>> +
>> +  /* Deprecated overload of the above.  */
>> +  void raw_write_part (int regnum, int offset, int len, const gdb_byte *src)
>> +  { raw_write_part (regnum, offset, gdb::make_array_view (src, len)); }
>>  
>>    /* Partial transfer of a cooked register.  Perform read, modify, write style
>>       operations.  */
>> -  void cooked_write_part (int regnum, int offset, int len,
>> -			  const gdb_byte *buf);
>> +  void cooked_write_part (int regnum, int offset,
>> +			  gdb::array_view<const gdb_byte> src);
>> +
>> +  /* Deprecated overload of the above.  */
>> +  void cooked_write_part (int regnum, int offset, int len, const gdb_byte *src)
>> +  { cooked_write_part (regnum, offset, gdb::make_array_view (src, len)); }
>>  
>>    /* Transfer a set of registers (as described by REGSET) between
>>       REGCACHE and BUF.  If REGNUM == -1, transfer all registers
>> @@ -442,8 +490,9 @@ class regcache : public detached_regcache
>>  
>>    /* Perform a partial register transfer using a read, modify, write
>>       operation.  */
>> -  enum register_status write_part (int regnum, int offset, int len,
>> -				   const gdb_byte *in, bool is_raw);
>> +  register_status write_part (int regnum, int offset,
>> +			      gdb::array_view<const gdb_byte> src,
>> +			      bool is_raw);
>>  
>>    /* The address space of this register cache (for registers where it
>>       makes sense, like PC or SP).  */
>> diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
>> index 4ac0fb659c3b..e1c174b56cb6 100644
>> --- a/gdbserver/regcache.cc
>> +++ b/gdbserver/regcache.cc
>> @@ -315,27 +315,33 @@ regcache_register_size (const reg_buffer_common *regcache, int n)
>>      (gdb::checked_static_cast<const struct regcache *> (regcache)->tdesc, n);
>>  }
>>  
>> -static unsigned char *
>> +static gdb::array_view<gdb_byte>
>>  register_data (const struct regcache *regcache, int n)
>>  {
>> -  return (regcache->registers
>> -	  + find_register_by_number (regcache->tdesc, n).offset / 8);
>> +  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
>> +  return gdb::make_array_view (regcache->registers + reg.offset / 8,
>> +			       reg.size / 8);
>>  }
>>  
>>  void
>> -supply_register (struct regcache *regcache, int n, const void *buf)
>> +supply_register (struct regcache *regcache, int n, const void *vbuf)
>>  {
>> -  return regcache->raw_supply (n, buf);
>> +  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
>> +  const gdb_byte *buf = static_cast<const gdb_byte *> (vbuf);
>> +  return regcache->raw_supply (n, gdb::make_array_view (buf, reg.size / 8),
>> +			       1.0f);
>>  }
>>  
>>  /* See gdbsupport/common-regcache.h.  */
>>  
>>  void
>> -regcache::raw_supply (int n, const void *buf)
>> +regcache::raw_supply (int n, gdb::array_view<const gdb_byte> src, float)
>>  {
>> -  if (buf)
>> +  auto dst = register_data (this, n);
>> +
>> +  if (src.data () != nullptr)
>>      {
>> -      memcpy (register_data (this, n), buf, register_size (tdesc, n));
>> +      copy (src, dst);
>>  #ifndef IN_PROCESS_AGENT
>>        if (register_status != NULL)
>>  	register_status[n] = REG_VALID;
>> @@ -343,7 +349,7 @@ regcache::raw_supply (int n, const void *buf)
>>      }
>>    else
>>      {
>> -      memset (register_data (this, n), 0, register_size (tdesc, n));
>> +      memset (dst.data (), 0, dst.size ());
>>  #ifndef IN_PROCESS_AGENT
>>        if (register_status != NULL)
>>  	register_status[n] = REG_UNAVAILABLE;
>> @@ -356,8 +362,8 @@ regcache::raw_supply (int n, const void *buf)
>>  void
>>  supply_register_zeroed (struct regcache *regcache, int n)
>>  {
>> -  memset (register_data (regcache, n), 0,
>> -	  register_size (regcache->tdesc, n));
>> +  auto dst = register_data (regcache, n);
>> +  memset (dst.data (), 0, dst.size ());
>>  #ifndef IN_PROCESS_AGENT
>>    if (regcache->register_status != NULL)
>>      regcache->register_status[n] = REG_VALID;
>> @@ -426,17 +432,20 @@ supply_register_by_name (struct regcache *regcache,
>>  #endif
>>  
>>  void
>> -collect_register (struct regcache *regcache, int n, void *buf)
>> +collect_register (struct regcache *regcache, int n, void *vbuf)
>>  {
>> -  regcache->raw_collect (n, buf);
>> +  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
>> +  gdb_byte *buf = static_cast<gdb_byte *> (vbuf);
>> +  regcache->raw_collect (n, gdb::make_array_view (buf, reg.size / 8), 1.0f);
>>  }
>>  
>>  /* See gdbsupport/common-regcache.h.  */
>>  
>>  void
>> -regcache::raw_collect (int n, void *buf) const
>> +regcache::raw_collect (int n, gdb::array_view<gdb_byte> dst, float) const
>>  {
>> -  memcpy (buf, register_data (this, n), register_size (tdesc, n));
>> +  auto src = register_data (this, n);
>> +  copy (src, dst);
>>  }
>>  
>>  enum register_status
>> @@ -476,8 +485,7 @@ regcache_raw_get_unsigned_by_name (struct regcache *regcache,
>>  void
>>  collect_register_as_string (struct regcache *regcache, int n, char *buf)
>>  {
>> -  bin2hex (register_data (regcache, n), buf,
>> -	   register_size (regcache->tdesc, n));
>> +  bin2hex (register_data (regcache, n), buf);
>>  }
>>  
>>  void
>> @@ -524,9 +532,9 @@ regcache::raw_compare (int regnum, const void *buf, int offset) const
>>  {
>>    gdb_assert (buf != NULL);
>>  
>> -  const unsigned char *regbuf = register_data (this, regnum);
>> -  int size = register_size (tdesc, regnum);
>> -  gdb_assert (size >= offset);
>> +  gdb::array_view<const gdb_byte> regbuf = register_data (this, regnum);
>> +  gdb_assert (offset < regbuf.size ());
>> +  regbuf = regbuf.slice (offset);
>>  
>> -  return (memcmp (buf, regbuf + offset, size - offset) == 0);
>> +  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
>>  }
>> diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
>> index 7248bcf5808a..ba093c97c1f0 100644
>> --- a/gdbserver/regcache.h
>> +++ b/gdbserver/regcache.h
>> @@ -50,10 +50,12 @@ struct regcache : public reg_buffer_common
>>    enum register_status get_register_status (int regnum) const override;
>>  
>>    /* See gdbsupport/common-regcache.h.  */
>> -  void raw_supply (int regnum, const void *buf) override;
>> +  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
>> +		   float) override;
>>  
>>    /* See gdbsupport/common-regcache.h.  */
>> -  void raw_collect (int regnum, void *buf) const override;
>> +  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
>> +		    float) const override;
>>  
>>    /* See gdbsupport/common-regcache.h.  */
>>    bool raw_compare (int regnum, const void *buf, int offset) const override;
>> diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
>> index 6d98ca8c92ed..75d209f7cbc0 100644
>> --- a/gdbsupport/common-regcache.h
>> +++ b/gdbsupport/common-regcache.h
>> @@ -78,11 +78,44 @@ struct reg_buffer_common
>>       buffer.  */
>>    virtual register_status get_register_status (int regnum) const = 0;
>>  
>> -  /* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE.  */
>> -  virtual void raw_supply (int regnum, const void *buf) = 0;
>> +  /* Supply register REGNUM, whose contents are stored in SRC, to this register
>> +     buffer.  */
>> +  virtual void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
>> +			   float) = 0;
>
> The addition of the 'float' argument is pretty unexpected here.  It's
> not mentioned in the commit message or the function comment, nor is it
> actually used in any of the function implementations as far as I can
> tell.  I'm assuming this is resolving some C++ overload problem.
>
> Sorry if I've missed something ... I've only looked at the diff so far.
> I thought I'd reach out before I start playing with the code trying to
> figure out what's going on.

As an initial experiment, I tried removing the float parameter after
applying every patch up to and including this one, and GDB still rebuilt
fine.

Then I applied every patch in the series, and removed the float
parameter, and again GDB built fine.

Maybe I'm missing something though...

Thanks,
Andrew



>
> If this is an overload workaround then I think this needs a comment
> explaining what the problem is that it's working around.
>
> If I'm just missing something, then the comment here still needs an
> explanation of what the extra parameter is for.
>
> Thanks,
> Andrew
>
>
>> +
>> +  void raw_supply (int regnum, const uint64_t *src)
>> +  {
>> +    raw_supply (regnum,
>> +		gdb::make_array_view ((const gdb_byte *) src, sizeof (*src)),
>> +		1.0f);
>> +  }
>> +
>> +  void raw_supply (int regnum, const gdb_byte *src)
>> +  {
>> +    raw_supply (regnum,
>> +		gdb::make_array_view (src,
>> +				      regcache_register_size (this, regnum)),
>> +		1.0f);
>> +  }
>> +
>> +  /* Collect register REGNUM from this register buffer and store its contents in
>> +     DST.  */
>> +  virtual void raw_collect (int regnum, gdb::array_view<gdb_byte> dst,
>> +			    float) const = 0;
>> +
>> +  void raw_collect (int regnum, uint64_t *dst) const
>> +  {
>> +    raw_collect (regnum,
>> +		 gdb::make_array_view ((gdb_byte *) dst, sizeof (*dst)), 1.0f);
>> +  };
>>  
>> -  /* Collect register REGNUM from REGCACHE and store its contents in BUF.  */
>> -  virtual void raw_collect (int regnum, void *buf) const = 0;
>> +  void raw_collect (int regnum, gdb_byte *dst)
>> +  {
>> +    raw_collect (regnum,
>> +		 gdb::make_array_view (dst,
>> +				       regcache_register_size (this, regnum)),
>> +		 1.0f);
>> +  }
>>  
>>    /* Compare the contents of the register stored in the regcache (ignoring the
>>       first OFFSET bytes) to the contents of BUF (without any offset).  Returns
>> diff --git a/gdbsupport/rsp-low.cc b/gdbsupport/rsp-low.cc
>> index 3d8c2002956e..632be265c00c 100644
>> --- a/gdbsupport/rsp-low.cc
>> +++ b/gdbsupport/rsp-low.cc
>> @@ -143,6 +143,14 @@ bin2hex (const gdb_byte *bin, char *hex, int count)
>>  
>>  /* See rsp-low.h.  */
>>  
>> +int
>> +bin2hex (gdb::array_view<gdb_byte> bin, char *hex)
>> +{
>> +  return bin2hex (bin.data (), hex, bin.size ());
>> +}
>> +
>> +/* See rsp-low.h.  */
>> +
>>  std::string
>>  bin2hex (const gdb_byte *bin, int count)
>>  {
>> diff --git a/gdbsupport/rsp-low.h b/gdbsupport/rsp-low.h
>> index 327d5f3a0947..1fc2572a7f5c 100644
>> --- a/gdbsupport/rsp-low.h
>> +++ b/gdbsupport/rsp-low.h
>> @@ -54,6 +54,8 @@ extern std::string hex2str (const char *hex, int count);
>>  
>>  extern int bin2hex (const gdb_byte *bin, char *hex, int count);
>>  
>> +extern int bin2hex (gdb::array_view<gdb_byte> bin, char *hex);
>> +
>>  /* Overloaded version of bin2hex that returns a std::string.  */
>>  
>>  extern std::string bin2hex (const gdb_byte *bin, int count);
>> -- 
>> 2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes
  2023-11-08  5:00 ` [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes Simon Marchi
@ 2023-11-13 15:00   ` Andrew Burgess
  2023-11-13 19:51     ` Simon Marchi
  0 siblings, 1 reply; 51+ messages in thread
From: Andrew Burgess @ 2023-11-13 15:00 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

Simon Marchi <simon.marchi@polymtl.ca> writes:

> From: Simon Marchi <simon.marchi@efficios.com>
>
> I found this only by inspection: the myaddr pointer in
> {get,put}_frame_register_bytes is reset to `buffer.data ()` at each
> iteration.  This means that we will always use the bytes at the
> beginning of `buffer` to read or write to the registers, instead of
> progressing in `buffer`.
>
> Fix this by re-writing the functions to chip away the beginning of the
> buffer array_view as we progress in reading or writing the data.
>
> These bugs was introduced almost 3 years ago [1], and yet nobody
> complained.  I'm wondering which architecture relies on that register
> "overflow" behavior (reading or writing multiple consecutive registers
> with one {get,put}_frame_register_bytes calls), and in which situation.
> I find these functions a bit dangerous, if a caller mis-calculates
> things, it could end up silently reading or writing to the next
> register, even if it's not the intent.
>
> If I could change it, I would prefer to have functions specifically made
> for that ({get,put}_frame_register_bytes_consecutive or something like
> that) and make {get,put}_frame_register_bytes only able to write within
> a single register (which I presume represents most of the use cases of
> the current {get,put}_frame_register_bytes).  If a caller mis-calculates
> things and an overflow occurs while calling
> {get,put}_frame_register_bytes, it would hit an assert.  The problem is
> knowing which callers rely on the overflow behavior and which don't.

I agree that this overflow behaviour sucks.

I have a memory of being told (years ago now) that this was a result of
some older compilers not emitting correct DWARF for compound value
locations, instead the compiler would just emit a single register
location, and assume that the debugger would read from consecutive DWARF
registers.  Note, this code assumes that the DWARF register numbering is
the same as GDB's register numbering, which is not always the case.

Personally, I'd love for GDB to be more aggressive about removing some
of this legacy behaviour.  What I'd like to do is move things like this
behind a switch, say 'set maintenance deprecated-features on|off', which
would be off by default, but when it is turned on we'd print a message
saying:

  The feature you turned this on for is deprecated, and will be removed
  from future versions of GDB.  To avoid this feature removed, please
  file a bug report here <url> describing the deprecated feature that
  you are using.

Then if nobody complains after a couple of years, we can start deleting
things.

Anyway... just putting my thoughts down.  I think this patch is fine.

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew


>
> [1] https://gitlab.com/gnutools/binutils-gdb/-/commit/bdec2917b1e94c7198ba39919f45060067952f43
>
> Change-Id: I43bd4a9f7fa8419d388a2b20bdc57d652688ddf8
> ---
>  gdb/frame.c | 63 +++++++++++++++++++----------------------------------
>  1 file changed, 23 insertions(+), 40 deletions(-)
>
> diff --git a/gdb/frame.c b/gdb/frame.c
> index afadb8ac4e73..b3d99163b4dc 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -1482,9 +1482,6 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
>  			  int *optimizedp, int *unavailablep)
>  {
>    struct gdbarch *gdbarch = get_frame_arch (frame);
> -  int i;
> -  int maxsize;
> -  int numregs;
>  
>    /* Skip registers wholly inside of OFFSET.  */
>    while (offset >= register_size (gdbarch, regnum))
> @@ -1495,9 +1492,9 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
>  
>    /* Ensure that we will not read beyond the end of the register file.
>       This can only ever happen if the debug information is bad.  */
> -  maxsize = -offset;
> -  numregs = gdbarch_num_cooked_regs (gdbarch);
> -  for (i = regnum; i < numregs; i++)
> +  int maxsize = -offset;
> +  int numregs = gdbarch_num_cooked_regs (gdbarch);
> +  for (int i = regnum; i < numregs; i++)
>      {
>        int thissize = register_size (gdbarch, i);
>  
> @@ -1506,20 +1503,15 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
>        maxsize += thissize;
>      }
>  
> -  int len = buffer.size ();
> -  if (len > maxsize)
> +  if (buffer.size () > maxsize)
>      error (_("Bad debug information detected: "
> -	     "Attempt to read %d bytes from registers."), len);
> +	     "Attempt to read %zu bytes from registers."), buffer.size ());
>  
>    /* Copy the data.  */
> -  while (len > 0)
> +  while (!buffer.empty ())
>      {
> -      int curr_len = register_size (gdbarch, regnum) - offset;
> -
> -      if (curr_len > len)
> -	curr_len = len;
> -
> -      gdb_byte *myaddr = buffer.data ();
> +      int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
> +				    buffer.size ());
>  
>        if (curr_len == register_size (gdbarch, regnum))
>  	{
> @@ -1527,8 +1519,8 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
>  	  CORE_ADDR addr;
>  	  int realnum;
>  
> -	  frame_register (frame, regnum, optimizedp, unavailablep,
> -			  &lval, &addr, &realnum, myaddr);
> +	  frame_register (frame, regnum, optimizedp, unavailablep, &lval,
> +			  &addr, &realnum, buffer.data ());
>  	  if (*optimizedp || *unavailablep)
>  	    return false;
>  	}
> @@ -1547,13 +1539,12 @@ get_frame_register_bytes (frame_info_ptr frame, int regnum,
>  	      return false;
>  	    }
>  
> -	  memcpy (myaddr, value->contents_all ().data () + offset,
> -		  curr_len);
> +	  copy (value->contents_all ().slice (offset, curr_len),
> +		buffer.slice (0, curr_len));
>  	  release_value (value);
>  	}
>  
> -      myaddr += curr_len;
> -      len -= curr_len;
> +      buffer = buffer.slice (curr_len);
>        offset = 0;
>        regnum++;
>      }
> @@ -1578,36 +1569,28 @@ put_frame_register_bytes (frame_info_ptr frame, int regnum,
>        regnum++;
>      }
>  
> -  int len = buffer.size ();
>    /* Copy the data.  */
> -  while (len > 0)
> +  while (!buffer.empty ())
>      {
> -      int curr_len = register_size (gdbarch, regnum) - offset;
> +      int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
> +				    buffer.size ());
>  
> -      if (curr_len > len)
> -	curr_len = len;
> -
> -      const gdb_byte *myaddr = buffer.data ();
>        if (curr_len == register_size (gdbarch, regnum))
> -	{
> -	  put_frame_register (frame, regnum, myaddr);
> -	}
> +	put_frame_register (frame, regnum, buffer.data ());
>        else
>  	{
> -	  struct value *value
> +	  value *value
>  	    = frame_unwind_register_value (frame_info_ptr (frame->next),
>  					   regnum);
> -	  gdb_assert (value != NULL);
> +	  gdb_assert (value != nullptr);
>  
> -	  memcpy ((char *) value->contents_writeable ().data () + offset,
> -		  myaddr, curr_len);
> -	  put_frame_register (frame, regnum,
> -			      value->contents_raw ().data ());
> +	  copy (buffer.slice (0, curr_len),
> +		value->contents_writeable ().slice (offset, curr_len));
> +	  put_frame_register (frame, regnum, value->contents_raw ().data ());
>  	  release_value (value);
>  	}
>  
> -      myaddr += curr_len;
> -      len -= curr_len;
> +      buffer = buffer.slice (curr_len);
>        offset = 0;
>        regnum++;
>      }
> -- 
> 2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-11-13 13:10           ` Luis Machado
@ 2023-11-13 15:08             ` Luis Machado
  0 siblings, 0 replies; 51+ messages in thread
From: Luis Machado @ 2023-11-13 15:08 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches

On 11/13/23 13:10, Luis Machado wrote:
> Simon,
> 
> On 11/9/23 19:04, Simon Marchi wrote:
>> On 11/8/23 14:34, Simon Marchi wrote:
>>> Ah, damn, probably because I switched to byte_vector,  which doesn't do
>>> the zero-initialization we want to do.  Here's a new patch (that applies
>>> on the series directly) that doesn't use byte_vector.
>>>
>>> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
>>> index 1815d78dec4..200e740e013 100644
>>> --- a/gdb/aarch64-tdep.c
>>> +++ b/gdb/aarch64-tdep.c
>>> @@ -3300,7 +3300,7 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>>>                         int regnum_offset,
>>>                         gdb::array_view<const gdb_byte> buf)
>>>  {
>>> -  unsigned v_regnum = AARCH64_V0_REGNUM + regnum_offset;
>>> +  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
>>>    gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
>>>
>>>    /* Enough space for a full vector register.
>>> @@ -3309,11 +3309,11 @@ aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>>>       various 'scalar' pseudo registers to behavior like architectural
>>>       writes, register width bytes are written the remainder are set to
>>>       zero.  */
>>> -  constexpr int raw_reg_size = 16;
>>> +  int raw_reg_size = register_size (gdbarch, raw_regnum);
>>>    gdb_byte raw_buf[raw_reg_size] {};
>>> -  gdb::array_view<gdb_byte> raw_view (raw_buf);
>>> +  gdb::array_view<gdb_byte> raw_view (raw_buf, raw_reg_size);
>>>    copy (buf, raw_view.slice (0, buf.size ()));
>>> -  put_frame_register (next_frame, v_regnum, raw_view);
>>> +  put_frame_register (next_frame, raw_regnum, raw_view);
>>>  }
>>>
>>>  /* Given REGNUM, a SME pseudo-register number, store the bytes from DATA to the
>>>
>>> Simon
>>
>> I managed to run a Debian AArch64 image in qemu, with SVE support, so I
>> was able to reproduce the failures you mentioned.  In the end, here's a
>> version of aarch64_pseudo_write_1 that works for me (written as to
>> minimize the number of unnecessary changes, since that seems to
>> introduce unexpected bugs...).
>>
>> static void
>> aarch64_pseudo_write_1 (gdbarch *gdbarch, frame_info_ptr next_frame,
>> 			int regnum_offset,
>> 			gdb::array_view<const gdb_byte> buf)
>> {
>>   unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
>>
>>   /* Enough space for a full vector register.  */
>>   int raw_reg_size = register_size (gdbarch, raw_regnum);
>>   gdb_byte raw_buf[raw_reg_size];
>>   gdb_static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
>>
>>   /* Ensure the register buffer is zero, we want gdb writes of the
>>      various 'scalar' pseudo registers to behavior like architectural
>>      writes, register width bytes are written the remainder are set to
>>      zero.  */
>>   memset (raw_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));
>>
>>   gdb::array_view<gdb_byte> raw_view (raw_buf, raw_reg_size);
>>   copy (buf, raw_view.slice (0, buf.size ()));
>>   put_frame_register (next_frame, raw_regnum, raw_view);
>> }
>>
>> Simon
> 
> Sorry for the late reply. I was out a couple days last week.
> 
> The above seems to make gdb.arch/aarch64-fp.exp happy, but gdb.arch/aarch64-pseudo-unwind.exp is still slightly unhappy:
> 
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $v8.q.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: caller, after change: p/x $s8.u
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: continue to breakpoint: continue to break_here_c
> FAIL: gdb.arch/aarch64-pseudo-unwind.exp: p/x value
> 
> This was tested on hardware (AWS's Graviton 3). Let me play with it a bit to understand what's up. I also see a
> SIGSEGV in the test that shouldn't be there. I'm guessing some sort of raw register corruption, as it leads to
> pc == 0x0.

I think I understand what's going on here. In the callee, we have CFI stating we're saving the following registers:

.cfi_offset 29, -16  // x29 (SP)
.cfi_offset 30, -8   // x30 (LR)
.cfi_offset 72, -32  // v8 -> 128-bit in size

So we're reserving a slot of 16 bytes to save v8, which is fine. But later, when we go up a frame and try to put a v8 pseudo-register
value to the frame there are two distinct scenarios.

The first one is on a sytem that doesn't support SVE or that does support SVE but the vector length is 128-bit, which
means z8 (the raw register) is the same size as v8 (the pseudo-register). This works fine, because we use the size of
the raw register to put the pseudo-register value to the frame.

Now, if we have SVE support and the vector length is bigger than 128-bit (in my case it is 256-bit), then using the
size of the raw register (z8) will put 32 bytes into a slot of 16 bytes, corrupting (IIUC) both SP and LR, and leading
to a segfault.

Technically we would be fine with using the correct size of the slot when putting a pseudo-register to a frame, but
I think there are further complications.

Take, for instance, the following text from the aadwarf64 (https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst),
note 5 from DWARF registers 64-95:

"In a similar manner to the general register file the size of an FP/Advanced SIMD register is taken from some external context to the register
number. If no context is available then only the least significant 64 bits of the register are referenced. In particular this means that the most
significant part of a SIMD register is unrecoverable by frame unwinding."

So this makes me think we might have situations where we don't know exactly what the size of the slot is. We may have saved all 128 bits of
a v register, but it may also be the case we only saved 64 bits. How we distinguish what size to use for putting a v pseudo-register to a frame
isn't completely clear to me at this point.

I'm wondering if we should instead use x/w registers for testing this functionality for aarch64.

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 05/24] gdb: change regcache interface to use array_view
  2023-11-13 14:00     ` Andrew Burgess
@ 2023-11-13 16:47       ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-13 16:47 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Simon Marchi

On 11/13/23 09:00, Andrew Burgess wrote:
>>> diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
>>> index 6d98ca8c92ed..75d209f7cbc0 100644
>>> --- a/gdbsupport/common-regcache.h
>>> +++ b/gdbsupport/common-regcache.h
>>> @@ -78,11 +78,44 @@ struct reg_buffer_common
>>>       buffer.  */
>>>    virtual register_status get_register_status (int regnum) const = 0;
>>>  
>>> -  /* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE.  */
>>> -  virtual void raw_supply (int regnum, const void *buf) = 0;
>>> +  /* Supply register REGNUM, whose contents are stored in SRC, to this register
>>> +     buffer.  */
>>> +  virtual void raw_supply (int regnum, gdb::array_view<const gdb_byte> src,
>>> +			   float) = 0;
>>
>> The addition of the 'float' argument is pretty unexpected here.  It's
>> not mentioned in the commit message or the function comment, nor is it
>> actually used in any of the function implementations as far as I can
>> tell.  I'm assuming this is resolving some C++ overload problem.
>>
>> Sorry if I've missed something ... I've only looked at the diff so far.
>> I thought I'd reach out before I start playing with the code trying to
>> figure out what's going on.
> 
> As an initial experiment, I tried removing the float parameter after
> applying every patch up to and including this one, and GDB still rebuilt
> fine.
> 
> Then I applied every patch in the series, and removed the float
> parameter, and again GDB built fine.
> 
> Maybe I'm missing something though...
> 
> Thanks,
> Andrew

Haha, good catch, I added them during development as a temporary thing,
to make sure I inspected all call sites, but of course I totally forgot
to remove them.  I added them to differentiate the calls to the raw
pointer and array_view overloads of the functions, I don't recall my
logic exactly.  I remember I was worried about cases like these:

  gdb_byte buf[SOME_ARCH_MAX_REG_SIZE];
  cooked_read (regnum, buf);

We want this to pick up the raw pointer overload, because the size of
register regnum is possibly smaller than SOME_ARCH_MAX_REG_SIZE.  When I
remove the dummy float parameters, clangd tells me that these
cooked_read calls still pick up the raw pointer overload, so it seems
ok.  But I'll see in testing.

For now I did the changes locally, I wull send a v2 once I
fix the regressions identified by Luis.

Thanks,

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes
  2023-11-13 15:00   ` Andrew Burgess
@ 2023-11-13 19:51     ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-13 19:51 UTC (permalink / raw)
  To: Andrew Burgess, Simon Marchi, gdb-patches

On 11/13/23 10:00, Andrew Burgess wrote:
> Simon Marchi <simon.marchi@polymtl.ca> writes:
> 
>> From: Simon Marchi <simon.marchi@efficios.com>
>>
>> I found this only by inspection: the myaddr pointer in
>> {get,put}_frame_register_bytes is reset to `buffer.data ()` at each
>> iteration.  This means that we will always use the bytes at the
>> beginning of `buffer` to read or write to the registers, instead of
>> progressing in `buffer`.
>>
>> Fix this by re-writing the functions to chip away the beginning of the
>> buffer array_view as we progress in reading or writing the data.
>>
>> These bugs was introduced almost 3 years ago [1], and yet nobody
>> complained.  I'm wondering which architecture relies on that register
>> "overflow" behavior (reading or writing multiple consecutive registers
>> with one {get,put}_frame_register_bytes calls), and in which situation.
>> I find these functions a bit dangerous, if a caller mis-calculates
>> things, it could end up silently reading or writing to the next
>> register, even if it's not the intent.
>>
>> If I could change it, I would prefer to have functions specifically made
>> for that ({get,put}_frame_register_bytes_consecutive or something like
>> that) and make {get,put}_frame_register_bytes only able to write within
>> a single register (which I presume represents most of the use cases of
>> the current {get,put}_frame_register_bytes).  If a caller mis-calculates
>> things and an overflow occurs while calling
>> {get,put}_frame_register_bytes, it would hit an assert.  The problem is
>> knowing which callers rely on the overflow behavior and which don't.
> 
> I agree that this overflow behaviour sucks.
> 
> I have a memory of being told (years ago now) that this was a result of
> some older compilers not emitting correct DWARF for compound value
> locations, instead the compiler would just emit a single register
> location, and assume that the debugger would read from consecutive DWARF
> registers.  Note, this code assumes that the DWARF register numbering is
> the same as GDB's register numbering, which is not always the case.
> 
> Personally, I'd love for GDB to be more aggressive about removing some
> of this legacy behaviour.  What I'd like to do is move things like this
> behind a switch, say 'set maintenance deprecated-features on|off', which
> would be off by default, but when it is turned on we'd print a message
> saying:
> 
>   The feature you turned this on for is deprecated, and will be removed
>   from future versions of GDB.  To avoid this feature removed, please
>   file a bug report here <url> describing the deprecated feature that
>   you are using.
> 
> Then if nobody complains after a couple of years, we can start deleting
> things.
> 
> Anyway... just putting my thoughts down.  I think this patch is fine.

Agreed, it would be nice.

> Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write
  2023-11-08  5:01 ` [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write Simon Marchi
@ 2023-11-14 12:12   ` Andrew Burgess
  2023-11-14 15:16     ` Simon Marchi
  0 siblings, 1 reply; 51+ messages in thread
From: Andrew Burgess @ 2023-11-14 12:12 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

Simon Marchi <simon.marchi@polymtl.ca> writes:

> From: Simon Marchi <simon.marchi@efficios.com>
>
> The next patch introduces a new variant of gdbarch_pseudo_register_write
> that takes a frame instead of a regcache for implementations to write
> raw registers.  Rename to old one to make it clear it's deprecated.
>
> Change-Id: If8872c89c6f8a1edfcab983eb064248fd5ff3115
> ---
>  gdb/aarch64-tdep.c        |  2 +-
>  gdb/amd64-tdep.c          |  4 ++--
>  gdb/arm-tdep.c            |  2 +-
>  gdb/avr-tdep.c            |  3 ++-
>  gdb/bfin-tdep.c           |  3 ++-
>  gdb/csky-tdep.c           |  4 ++--
>  gdb/frv-tdep.c            |  3 ++-
>  gdb/gdbarch-gen.h         | 15 +++++++++++----
>  gdb/gdbarch.c             | 30 +++++++++++++++---------------
>  gdb/gdbarch_components.py | 10 +++++++++-
>  gdb/h8300-tdep.c          |  3 ++-
>  gdb/i386-tdep.c           |  3 ++-
>  gdb/ia64-tdep.c           |  3 ++-
>  gdb/m32c-tdep.c           |  3 ++-
>  gdb/m68hc11-tdep.c        |  3 ++-
>  gdb/mep-tdep.c            |  3 ++-
>  gdb/mips-tdep.c           |  3 ++-
>  gdb/msp430-tdep.c         |  3 ++-
>  gdb/nds32-tdep.c          |  3 ++-
>  gdb/regcache.c            |  4 ++--
>  gdb/riscv-tdep.c          |  9 +++++----
>  gdb/rl78-tdep.c           |  3 ++-
>  gdb/rs6000-tdep.c         |  4 ++--
>  gdb/s390-tdep.c           |  3 ++-
>  gdb/sh-tdep.c             |  9 ++++++---
>  gdb/sparc-tdep.c          |  3 ++-
>  gdb/sparc64-tdep.c        |  3 ++-
>  gdb/xtensa-tdep.c         |  3 ++-
>  28 files changed, 90 insertions(+), 54 deletions(-)
>
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index 341a6a19de5f..d1a6d46fb1f2 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -4481,7 +4481,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  
>    set_gdbarch_num_pseudo_regs (gdbarch, num_pseudo_regs);
>    set_gdbarch_pseudo_register_read_value (gdbarch, aarch64_pseudo_read_value);
> -  set_gdbarch_pseudo_register_write (gdbarch, aarch64_pseudo_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch, aarch64_pseudo_write);
>    set_tdesc_pseudo_register_name (gdbarch, aarch64_pseudo_register_name);
>    set_tdesc_pseudo_register_type (gdbarch, aarch64_pseudo_register_type);
>    set_tdesc_pseudo_register_reggroup_p (gdbarch,
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index 955a6860631d..a0b4986d5b6c 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -3205,8 +3205,8 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
>  
>    set_gdbarch_pseudo_register_read_value (gdbarch,
>  					  amd64_pseudo_register_read_value);
> -  set_gdbarch_pseudo_register_write (gdbarch,
> -				     amd64_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						amd64_pseudo_register_write);
>    set_gdbarch_ax_pseudo_register_collect (gdbarch,
>  					  amd64_ax_pseudo_register_collect);
>  
> diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
> index 5c8c4cd4e94b..cd4c760c2ff4 100644
> --- a/gdb/arm-tdep.c
> +++ b/gdb/arm-tdep.c
> @@ -10906,7 +10906,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>      {
>        set_gdbarch_num_pseudo_regs (gdbarch, num_pseudos);
>        set_gdbarch_pseudo_register_read (gdbarch, arm_pseudo_read);
> -      set_gdbarch_pseudo_register_write (gdbarch, arm_pseudo_write);
> +      set_gdbarch_deprecated_pseudo_register_write (gdbarch, arm_pseudo_write);
>      }
>  
>    /* Add standard register aliases.  We add aliases even for those
> diff --git a/gdb/avr-tdep.c b/gdb/avr-tdep.c
> index 92aec3b510f5..5690f6f65c74 100644
> --- a/gdb/avr-tdep.c
> +++ b/gdb/avr-tdep.c
> @@ -1510,7 +1510,8 @@ avr_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  
>    set_gdbarch_num_pseudo_regs (gdbarch, AVR_NUM_PSEUDO_REGS);
>    set_gdbarch_pseudo_register_read (gdbarch, avr_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, avr_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						avr_pseudo_register_write);
>  
>    set_gdbarch_return_value (gdbarch, avr_return_value);
>  
> diff --git a/gdb/bfin-tdep.c b/gdb/bfin-tdep.c
> index 4b9067a2d0f7..0ff4355f573e 100644
> --- a/gdb/bfin-tdep.c
> +++ b/gdb/bfin-tdep.c
> @@ -805,7 +805,8 @@ bfin_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  
>    set_gdbarch_num_regs (gdbarch, BFIN_NUM_REGS);
>    set_gdbarch_pseudo_register_read (gdbarch, bfin_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, bfin_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						bfin_pseudo_register_write);
>    set_gdbarch_num_pseudo_regs (gdbarch, BFIN_NUM_PSEUDO_REGS);
>    set_gdbarch_sp_regnum (gdbarch, BFIN_SP_REGNUM);
>    set_gdbarch_pc_regnum (gdbarch, BFIN_PC_REGNUM);
> diff --git a/gdb/csky-tdep.c b/gdb/csky-tdep.c
> index e82b8dff546e..6cca72041a73 100644
> --- a/gdb/csky-tdep.c
> +++ b/gdb/csky-tdep.c
> @@ -2872,8 +2872,8 @@ csky_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  				   tdep->fv_pseudo_registers_count);
>        set_gdbarch_pseudo_register_read (gdbarch,
>  					csky_pseudo_register_read);
> -      set_gdbarch_pseudo_register_write (gdbarch,
> -					 csky_pseudo_register_write);
> +      set_gdbarch_deprecated_pseudo_register_write
> +	(gdbarch, csky_pseudo_register_write);
>        set_tdesc_pseudo_register_name (gdbarch, csky_pseudo_register_name);
>      }
>  
> diff --git a/gdb/frv-tdep.c b/gdb/frv-tdep.c
> index 3656f6fcb663..e4c10dfb4347 100644
> --- a/gdb/frv-tdep.c
> +++ b/gdb/frv-tdep.c
> @@ -1499,7 +1499,8 @@ frv_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_register_sim_regno (gdbarch, frv_register_sim_regno);
>  
>    set_gdbarch_pseudo_register_read (gdbarch, frv_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, frv_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						frv_pseudo_register_write);
>  
>    set_gdbarch_skip_prologue (gdbarch, frv_skip_prologue);
>    set_gdbarch_skip_main_prologue (gdbarch, frv_skip_main_prologue);
> diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
> index c2de274ed2a9..3160aa8a9613 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -200,11 +200,18 @@ typedef struct value * (gdbarch_pseudo_register_read_value_ftype) (struct gdbarc
>  extern struct value * gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum);
>  extern void set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value);
>  
> -extern bool gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch);
> +/* Write bytes to a pseudo register.
>  
> -typedef void (gdbarch_pseudo_register_write_ftype) (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
> -extern void gdbarch_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
> -extern void set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch, gdbarch_pseudo_register_write_ftype *pseudo_register_write);
> +   This is marked as deprecated because it gets passed a regcache for
> +   implementations to write raw registers in.  This doesn't work for unwound
> +   frames, where the raw registers backing the pseudo registers may have been
> +   saved elsewhere. */

Missing a space before '*/' here.

Thanks,
Andrew

> +
> +extern bool gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch);
> +
> +typedef void (gdbarch_deprecated_pseudo_register_write_ftype) (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
> +extern void gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
> +extern void set_gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch, gdbarch_deprecated_pseudo_register_write_ftype *deprecated_pseudo_register_write);
>  
>  extern int gdbarch_num_regs (struct gdbarch *gdbarch);
>  extern void set_gdbarch_num_regs (struct gdbarch *gdbarch, int num_regs);
> diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
> index 06ff52576bdf..e198d339f6ba 100644
> --- a/gdb/gdbarch.c
> +++ b/gdb/gdbarch.c
> @@ -74,7 +74,7 @@ struct gdbarch
>    gdbarch_virtual_frame_pointer_ftype *virtual_frame_pointer = legacy_virtual_frame_pointer;
>    gdbarch_pseudo_register_read_ftype *pseudo_register_read = nullptr;
>    gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value = nullptr;
> -  gdbarch_pseudo_register_write_ftype *pseudo_register_write = nullptr;
> +  gdbarch_deprecated_pseudo_register_write_ftype *deprecated_pseudo_register_write = nullptr;
>    int num_regs = -1;
>    int num_pseudo_regs = 0;
>    gdbarch_ax_pseudo_register_collect_ftype *ax_pseudo_register_collect = nullptr;
> @@ -330,7 +330,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
>    /* Skip verify of virtual_frame_pointer, invalid_p == 0 */
>    /* Skip verify of pseudo_register_read, has predicate.  */
>    /* Skip verify of pseudo_register_read_value, has predicate.  */
> -  /* Skip verify of pseudo_register_write, has predicate.  */
> +  /* Skip verify of deprecated_pseudo_register_write, has predicate.  */
>    if (gdbarch->num_regs == -1)
>      log.puts ("\n\tnum_regs");
>    /* Skip verify of num_pseudo_regs, invalid_p == 0 */
> @@ -650,11 +650,11 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
>  	      "gdbarch_dump: pseudo_register_read_value = <%s>\n",
>  	      host_address_to_string (gdbarch->pseudo_register_read_value));
>    gdb_printf (file,
> -	      "gdbarch_dump: gdbarch_pseudo_register_write_p() = %d\n",
> -	      gdbarch_pseudo_register_write_p (gdbarch));
> +	      "gdbarch_dump: gdbarch_deprecated_pseudo_register_write_p() = %d\n",
> +	      gdbarch_deprecated_pseudo_register_write_p (gdbarch));
>    gdb_printf (file,
> -	      "gdbarch_dump: pseudo_register_write = <%s>\n",
> -	      host_address_to_string (gdbarch->pseudo_register_write));
> +	      "gdbarch_dump: deprecated_pseudo_register_write = <%s>\n",
> +	      host_address_to_string (gdbarch->deprecated_pseudo_register_write));
>    gdb_printf (file,
>  	      "gdbarch_dump: num_regs = %s\n",
>  	      plongest (gdbarch->num_regs));
> @@ -1903,27 +1903,27 @@ set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch,
>  }
>  
>  bool
> -gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch)
> +gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch)
>  {
>    gdb_assert (gdbarch != NULL);
> -  return gdbarch->pseudo_register_write != NULL;
> +  return gdbarch->deprecated_pseudo_register_write != NULL;
>  }
>  
>  void
> -gdbarch_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf)
> +gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf)
>  {
>    gdb_assert (gdbarch != NULL);
> -  gdb_assert (gdbarch->pseudo_register_write != NULL);
> +  gdb_assert (gdbarch->deprecated_pseudo_register_write != NULL);
>    if (gdbarch_debug >= 2)
> -    gdb_printf (gdb_stdlog, "gdbarch_pseudo_register_write called\n");
> -  gdbarch->pseudo_register_write (gdbarch, regcache, cookednum, buf);
> +    gdb_printf (gdb_stdlog, "gdbarch_deprecated_pseudo_register_write called\n");
> +  gdbarch->deprecated_pseudo_register_write (gdbarch, regcache, cookednum, buf);
>  }
>  
>  void
> -set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch,
> -				   gdbarch_pseudo_register_write_ftype pseudo_register_write)
> +set_gdbarch_deprecated_pseudo_register_write (struct gdbarch *gdbarch,
> +					      gdbarch_deprecated_pseudo_register_write_ftype deprecated_pseudo_register_write)
>  {
> -  gdbarch->pseudo_register_write = pseudo_register_write;
> +  gdbarch->deprecated_pseudo_register_write = deprecated_pseudo_register_write;
>  }
>  
>  int
> diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> index 86754af17cc3..1100da160550 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -419,8 +419,16 @@ never be called.
>  )
>  
>  Method(
> +    comment="""
> +Write bytes to a pseudo register.
> +
> +This is marked as deprecated because it gets passed a regcache for
> +implementations to write raw registers in.  This doesn't work for unwound
> +frames, where the raw registers backing the pseudo registers may have been
> +saved elsewhere.
> +""",
>      type="void",
> -    name="pseudo_register_write",
> +    name="deprecated_pseudo_register_write",
>      params=[
>          ("struct regcache *", "regcache"),
>          ("int", "cookednum"),
> diff --git a/gdb/h8300-tdep.c b/gdb/h8300-tdep.c
> index d1494916f594..20f503c209bf 100644
> --- a/gdb/h8300-tdep.c
> +++ b/gdb/h8300-tdep.c
> @@ -1318,7 +1318,8 @@ h8300_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>      }
>  
>    set_gdbarch_pseudo_register_read (gdbarch, h8300_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, h8300_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						h8300_pseudo_register_write);
>  
>    /*
>     * Basic register fields and methods.
> diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
> index 7f849bac6cd3..5f12f17bc5eb 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -8603,7 +8603,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    /* Pseudo registers may be changed by amd64_init_abi.  */
>    set_gdbarch_pseudo_register_read_value (gdbarch,
>  					  i386_pseudo_register_read_value);
> -  set_gdbarch_pseudo_register_write (gdbarch, i386_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						i386_pseudo_register_write);
>    set_gdbarch_ax_pseudo_register_collect (gdbarch,
>  					  i386_ax_pseudo_register_collect);
>  
> diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
> index d4c1736f0e7f..34b7fa09d5d3 100644
> --- a/gdb/ia64-tdep.c
> +++ b/gdb/ia64-tdep.c
> @@ -3959,7 +3959,8 @@ ia64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_register_type (gdbarch, ia64_register_type);
>  
>    set_gdbarch_pseudo_register_read (gdbarch, ia64_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, ia64_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						ia64_pseudo_register_write);
>    set_gdbarch_dwarf2_reg_to_regnum (gdbarch, ia64_dwarf_reg_to_regnum);
>    set_gdbarch_register_reggroup_p (gdbarch, ia64_register_reggroup_p);
>    set_gdbarch_convert_register_p (gdbarch, ia64_convert_register_p);
> diff --git a/gdb/m32c-tdep.c b/gdb/m32c-tdep.c
> index e06dbbe12bff..d053135e592d 100644
> --- a/gdb/m32c-tdep.c
> +++ b/gdb/m32c-tdep.c
> @@ -977,7 +977,8 @@ make_regs (struct gdbarch *arch)
>    set_gdbarch_register_name (arch, m32c_register_name);
>    set_gdbarch_register_type (arch, m32c_register_type);
>    set_gdbarch_pseudo_register_read (arch, m32c_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (arch, m32c_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (arch,
> +						m32c_pseudo_register_write);
>    set_gdbarch_register_sim_regno (arch, m32c_register_sim_regno);
>    set_gdbarch_stab_reg_to_regnum (arch, m32c_debug_info_reg_to_regnum);
>    set_gdbarch_dwarf2_reg_to_regnum (arch, m32c_debug_info_reg_to_regnum);
> diff --git a/gdb/m68hc11-tdep.c b/gdb/m68hc11-tdep.c
> index 1257c55ce667..668158f15a09 100644
> --- a/gdb/m68hc11-tdep.c
> +++ b/gdb/m68hc11-tdep.c
> @@ -1491,7 +1491,8 @@ m68hc11_gdbarch_init (struct gdbarch_info info,
>    set_gdbarch_register_name (gdbarch, m68hc11_register_name);
>    set_gdbarch_register_type (gdbarch, m68hc11_register_type);
>    set_gdbarch_pseudo_register_read (gdbarch, m68hc11_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, m68hc11_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						m68hc11_pseudo_register_write);
>  
>    set_gdbarch_push_dummy_call (gdbarch, m68hc11_push_dummy_call);
>  
> diff --git a/gdb/mep-tdep.c b/gdb/mep-tdep.c
> index a84df4e1f53c..94d1ddf2a147 100644
> --- a/gdb/mep-tdep.c
> +++ b/gdb/mep-tdep.c
> @@ -2424,7 +2424,8 @@ mep_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_register_type (gdbarch, mep_register_type);
>    set_gdbarch_num_pseudo_regs (gdbarch, MEP_NUM_PSEUDO_REGS);
>    set_gdbarch_pseudo_register_read (gdbarch, mep_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, mep_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						mep_pseudo_register_write);
>    set_gdbarch_dwarf2_reg_to_regnum (gdbarch, mep_debug_reg_to_regnum);
>    set_gdbarch_stab_reg_to_regnum (gdbarch, mep_debug_reg_to_regnum);
>  
> diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
> index 8e25c45dd297..a4ba68748b9c 100644
> --- a/gdb/mips-tdep.c
> +++ b/gdb/mips-tdep.c
> @@ -8518,7 +8518,8 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_long_double_bit (gdbarch, 64);
>    set_gdbarch_register_reggroup_p (gdbarch, mips_register_reggroup_p);
>    set_gdbarch_pseudo_register_read (gdbarch, mips_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, mips_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						mips_pseudo_register_write);
>  
>    set_gdbarch_ax_pseudo_register_collect (gdbarch,
>  					  mips_ax_pseudo_register_collect);
> diff --git a/gdb/msp430-tdep.c b/gdb/msp430-tdep.c
> index 97d9073efbbe..92404045497c 100644
> --- a/gdb/msp430-tdep.c
> +++ b/gdb/msp430-tdep.c
> @@ -937,7 +937,8 @@ msp430_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_sp_regnum (gdbarch, MSP430_SP_REGNUM);
>    set_gdbarch_register_reggroup_p (gdbarch, msp430_register_reggroup_p);
>    set_gdbarch_pseudo_register_read (gdbarch, msp430_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, msp430_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						msp430_pseudo_register_write);
>    set_gdbarch_dwarf2_reg_to_regnum (gdbarch, msp430_dwarf2_reg_to_regnum);
>    set_gdbarch_register_sim_regno (gdbarch, msp430_register_sim_regno);
>  
> diff --git a/gdb/nds32-tdep.c b/gdb/nds32-tdep.c
> index 7618a81fc256..83685d06f3ee 100644
> --- a/gdb/nds32-tdep.c
> +++ b/gdb/nds32-tdep.c
> @@ -2001,7 +2001,8 @@ nds32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    else if (use_pseudo_fsrs == 1)
>      {
>        set_gdbarch_pseudo_register_read (gdbarch, nds32_pseudo_register_read);
> -      set_gdbarch_pseudo_register_write (gdbarch, nds32_pseudo_register_write);
> +      set_gdbarch_deprecated_pseudo_register_write
> +	(gdbarch, nds32_pseudo_register_write);
>        set_tdesc_pseudo_register_name (gdbarch, nds32_pseudo_register_name);
>        set_tdesc_pseudo_register_type (gdbarch, nds32_pseudo_register_type);
>        set_gdbarch_num_pseudo_regs (gdbarch, num_fsr_map[fpu_freg]);
> diff --git a/gdb/regcache.c b/gdb/regcache.c
> index ff5f5b70433e..dadc949434ee 100644
> --- a/gdb/regcache.c
> +++ b/gdb/regcache.c
> @@ -930,8 +930,8 @@ regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float)
>    if (regnum < num_raw_registers ())
>      raw_write (regnum, src, 1.0f);
>    else
> -    gdbarch_pseudo_register_write (m_descr->gdbarch, this, regnum,
> -				   src.data ());
> +    gdbarch_deprecated_pseudo_register_write (m_descr->gdbarch, this, regnum,
> +					      src.data ());
>  }
>  
>  /* See regcache.h.  */
> diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
> index 48f5a9e35e67..c1b9c8d435e5 100644
> --- a/gdb/riscv-tdep.c
> +++ b/gdb/riscv-tdep.c
> @@ -1002,9 +1002,9 @@ riscv_pseudo_register_read (struct gdbarch *gdbarch,
>    return REG_UNKNOWN;
>  }
>  
> -/* Implement gdbarch_pseudo_register_write.  Write the contents of BUF into
> -   pseudo-register REGNUM in REGCACHE.  BUF is sized based on the type of
> -   register REGNUM.  */
> +/* Implement gdbarch_deprecated_pseudo_register_write.  Write the contents of
> +   BUF into pseudo-register REGNUM in REGCACHE.  BUF is sized based on the type
> +   of register REGNUM.  */
>  
>  static void
>  riscv_pseudo_register_write (struct gdbarch *gdbarch,
> @@ -4229,7 +4229,8 @@ riscv_gdbarch_init (struct gdbarch_info info,
>    set_tdesc_pseudo_register_reggroup_p (gdbarch,
>  					riscv_pseudo_register_reggroup_p);
>    set_gdbarch_pseudo_register_read (gdbarch, riscv_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, riscv_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						riscv_pseudo_register_write);
>  
>    /* Finalise the target description registers.  */
>    tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data),
> diff --git a/gdb/rl78-tdep.c b/gdb/rl78-tdep.c
> index 2f40fbddaa1e..19298063186f 100644
> --- a/gdb/rl78-tdep.c
> +++ b/gdb/rl78-tdep.c
> @@ -1436,7 +1436,8 @@ rl78_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_gdbarch_pc_regnum (gdbarch, RL78_PC_REGNUM);
>    set_gdbarch_sp_regnum (gdbarch, RL78_SP_REGNUM);
>    set_gdbarch_pseudo_register_read (gdbarch, rl78_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, rl78_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						rl78_pseudo_register_write);
>    set_gdbarch_dwarf2_reg_to_regnum (gdbarch, rl78_dwarf_reg_to_regnum);
>    set_gdbarch_register_reggroup_p (gdbarch, rl78_register_reggroup_p);
>    set_gdbarch_register_sim_regno (gdbarch, rl78_register_sim_regno);
> diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
> index 819f07b47c0a..78016eb8fccd 100644
> --- a/gdb/rs6000-tdep.c
> +++ b/gdb/rs6000-tdep.c
> @@ -8291,8 +8291,8 @@ rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>        || have_vsx || have_htm_fpu || have_htm_vsx)
>      {
>        set_gdbarch_pseudo_register_read (gdbarch, rs6000_pseudo_register_read);
> -      set_gdbarch_pseudo_register_write (gdbarch,
> -					 rs6000_pseudo_register_write);
> +      set_gdbarch_deprecated_pseudo_register_write
> +	(gdbarch, rs6000_pseudo_register_write);
>        set_gdbarch_ax_pseudo_register_collect (gdbarch,
>  	      rs6000_ax_pseudo_register_collect);
>      }
> diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
> index 54b5c89e5e3c..52faae971ee6 100644
> --- a/gdb/s390-tdep.c
> +++ b/gdb/s390-tdep.c
> @@ -7081,7 +7081,8 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  
>    /* Pseudo registers.  */
>    set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						s390_pseudo_register_write);
>    set_tdesc_pseudo_register_name (gdbarch, s390_pseudo_register_name);
>    set_tdesc_pseudo_register_type (gdbarch, s390_pseudo_register_type);
>    set_tdesc_pseudo_register_reggroup_p (gdbarch,
> diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c
> index babf85eeb906..ae1ce1e44713 100644
> --- a/gdb/sh-tdep.c
> +++ b/gdb/sh-tdep.c
> @@ -2286,7 +2286,8 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>        set_gdbarch_fp0_regnum (gdbarch, 25);
>        set_gdbarch_num_pseudo_regs (gdbarch, 9);
>        set_gdbarch_pseudo_register_read (gdbarch, sh_pseudo_register_read);
> -      set_gdbarch_pseudo_register_write (gdbarch, sh_pseudo_register_write);
> +      set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						    sh_pseudo_register_write);
>        set_gdbarch_return_value (gdbarch, sh_return_value_fpu);
>        set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
>        break;
> @@ -2297,7 +2298,8 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  
>        set_gdbarch_num_pseudo_regs (gdbarch, 1);
>        set_gdbarch_pseudo_register_read (gdbarch, sh_pseudo_register_read);
> -      set_gdbarch_pseudo_register_write (gdbarch, sh_pseudo_register_write);
> +      set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						    sh_pseudo_register_write);
>        break;
>  
>      case bfd_mach_sh_dsp:
> @@ -2337,7 +2339,8 @@ sh_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>        set_gdbarch_fp0_regnum (gdbarch, 25);
>        set_gdbarch_num_pseudo_regs (gdbarch, 13);
>        set_gdbarch_pseudo_register_read (gdbarch, sh_pseudo_register_read);
> -      set_gdbarch_pseudo_register_write (gdbarch, sh_pseudo_register_write);
> +      set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						    sh_pseudo_register_write);
>        set_gdbarch_return_value (gdbarch, sh_return_value_fpu);
>        set_gdbarch_push_dummy_call (gdbarch, sh_push_dummy_call_fpu);
>        break;
> diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c
> index 78f240db04e7..3a053a99e351 100644
> --- a/gdb/sparc-tdep.c
> +++ b/gdb/sparc-tdep.c
> @@ -1844,7 +1844,8 @@ sparc32_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    set_tdesc_pseudo_register_name (gdbarch, sparc32_pseudo_register_name);
>    set_tdesc_pseudo_register_type (gdbarch, sparc32_pseudo_register_type);
>    set_gdbarch_pseudo_register_read (gdbarch, sparc32_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, sparc32_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						sparc32_pseudo_register_write);
>  
>    /* Register numbers of various important registers.  */
>    set_gdbarch_sp_regnum (gdbarch, SPARC_SP_REGNUM); /* %sp */
> diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
> index abbbb73d1db9..61f486689f15 100644
> --- a/gdb/sparc64-tdep.c
> +++ b/gdb/sparc64-tdep.c
> @@ -1825,7 +1825,8 @@ sparc64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>    set_tdesc_pseudo_register_name (gdbarch, sparc64_pseudo_register_name);
>    set_tdesc_pseudo_register_type (gdbarch, sparc64_pseudo_register_type);
>    set_gdbarch_pseudo_register_read (gdbarch, sparc64_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, sparc64_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						sparc64_pseudo_register_write);
>  
>    /* Register numbers of various important registers.  */
>    set_gdbarch_pc_regnum (gdbarch, SPARC64_PC_REGNUM); /* %pc */
> diff --git a/gdb/xtensa-tdep.c b/gdb/xtensa-tdep.c
> index c9935d152641..8f313f0feabd 100644
> --- a/gdb/xtensa-tdep.c
> +++ b/gdb/xtensa-tdep.c
> @@ -3174,7 +3174,8 @@ xtensa_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  
>    /* Pseudo-Register read/write.  */
>    set_gdbarch_pseudo_register_read (gdbarch, xtensa_pseudo_register_read);
> -  set_gdbarch_pseudo_register_write (gdbarch, xtensa_pseudo_register_write);
> +  set_gdbarch_deprecated_pseudo_register_write (gdbarch,
> +						xtensa_pseudo_register_write);
>  
>    /* Set target information.  */
>    set_gdbarch_num_regs (gdbarch, tdep->num_regs);
> -- 
> 2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame
  2023-11-08  5:01 ` [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame Simon Marchi
@ 2023-11-14 12:20   ` Andrew Burgess
  2023-11-14 15:20     ` Simon Marchi
  0 siblings, 1 reply; 51+ messages in thread
From: Andrew Burgess @ 2023-11-14 12:20 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Simon Marchi

Simon Marchi <simon.marchi@polymtl.ca> writes:

> From: Simon Marchi <simon.marchi@efficios.com>
>
> Add a new variant of gdbarch_pseudo_register_write that takes a
> frame_info in order to write raw registers.  Use this new method when
> available:
>
>  - in put_frame_register, when trying to write a pseudo register to a given frame
>  - in regcache::cooked_write
>
> No implementation is migrated to use this new method (that will come in
> subsequent patches), so no behavior change is expected here.
>
> The objective is to fix writing pseudo registers to non-current
> frames.  See previous commit "gdb: read pseudo register through
> frame" for a more detailed explanation.
>
> Change-Id: Ie7fe364a15a4d86c2ecb09de2b4baa08c45555ac
> ---
>  gdb/frame.c               |  9 +++++-
>  gdb/gdbarch-gen.h         | 15 +++++++++-
>  gdb/gdbarch.c             | 32 ++++++++++++++++++++
>  gdb/gdbarch_components.py | 19 ++++++++++++
>  gdb/regcache.c            |  4 +++
>  gdb/value.c               | 63 +++++++++++++++++++++++++++++++++++++++
>  gdb/value.h               | 22 ++++++++++++++
>  7 files changed, 162 insertions(+), 2 deletions(-)
>
> diff --git a/gdb/frame.c b/gdb/frame.c
> index 3035f87369ca..81836d6d5357 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -1452,7 +1452,14 @@ put_frame_register (frame_info_ptr next_frame, int regnum,
>  	break;
>        }
>      case lval_register:
> -      get_current_regcache ()->cooked_write (realnum, buf, 1.0f);
> +      /* Not sure if that's always true... but we have a problem if not.  */
> +      gdb_assert (size == register_size (gdbarch, realnum));
> +
> +      if (realnum < gdbarch_num_regs (gdbarch)
> +	  || !gdbarch_pseudo_register_write_p (gdbarch))
> +	get_current_regcache ()->cooked_write (realnum, buf, 1.0f);
> +      else
> +	gdbarch_pseudo_register_write (gdbarch, next_frame, realnum, buf);
>        break;
>      default:
>        error (_("Attempt to assign to an unmodifiable value."));
> diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
> index 3160aa8a9613..9cf58490d0b6 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -200,12 +200,25 @@ typedef struct value * (gdbarch_pseudo_register_read_value_ftype) (struct gdbarc
>  extern struct value * gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum);
>  extern void set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value);
>  
> +/* Write bytes in BUF to pseudo register with number PSEUDO_REG_NUM.
> +
> +   Raw registers backing the pseudo register should be written to using
> +   NEXT_FRAME. */
> +
> +extern bool gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch);
> +
> +typedef void (gdbarch_pseudo_register_write_ftype) (struct gdbarch *gdbarch, frame_info_ptr next_frame, int pseudo_reg_num, gdb::array_view<const gdb_byte> buf);
> +extern void gdbarch_pseudo_register_write (struct gdbarch *gdbarch, frame_info_ptr next_frame, int pseudo_reg_num, gdb::array_view<const gdb_byte> buf);
> +extern void set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch, gdbarch_pseudo_register_write_ftype *pseudo_register_write);
> +
>  /* Write bytes to a pseudo register.
>  
>     This is marked as deprecated because it gets passed a regcache for
>     implementations to write raw registers in.  This doesn't work for unwound
>     frames, where the raw registers backing the pseudo registers may have been
> -   saved elsewhere. */
> +   saved elsewhere.
> +
> +   Implementations should be migrated to implement pseudo_register_write instead. */
>  
>  extern bool gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch);
>  
> diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
> index e198d339f6ba..d584305fefb2 100644
> --- a/gdb/gdbarch.c
> +++ b/gdb/gdbarch.c
> @@ -74,6 +74,7 @@ struct gdbarch
>    gdbarch_virtual_frame_pointer_ftype *virtual_frame_pointer = legacy_virtual_frame_pointer;
>    gdbarch_pseudo_register_read_ftype *pseudo_register_read = nullptr;
>    gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value = nullptr;
> +  gdbarch_pseudo_register_write_ftype *pseudo_register_write = nullptr;
>    gdbarch_deprecated_pseudo_register_write_ftype *deprecated_pseudo_register_write = nullptr;
>    int num_regs = -1;
>    int num_pseudo_regs = 0;
> @@ -330,6 +331,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
>    /* Skip verify of virtual_frame_pointer, invalid_p == 0 */
>    /* Skip verify of pseudo_register_read, has predicate.  */
>    /* Skip verify of pseudo_register_read_value, has predicate.  */
> +  /* Skip verify of pseudo_register_write, has predicate.  */
>    /* Skip verify of deprecated_pseudo_register_write, has predicate.  */
>    if (gdbarch->num_regs == -1)
>      log.puts ("\n\tnum_regs");
> @@ -649,6 +651,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
>    gdb_printf (file,
>  	      "gdbarch_dump: pseudo_register_read_value = <%s>\n",
>  	      host_address_to_string (gdbarch->pseudo_register_read_value));
> +  gdb_printf (file,
> +	      "gdbarch_dump: gdbarch_pseudo_register_write_p() = %d\n",
> +	      gdbarch_pseudo_register_write_p (gdbarch));
> +  gdb_printf (file,
> +	      "gdbarch_dump: pseudo_register_write = <%s>\n",
> +	      host_address_to_string (gdbarch->pseudo_register_write));
>    gdb_printf (file,
>  	      "gdbarch_dump: gdbarch_deprecated_pseudo_register_write_p() = %d\n",
>  	      gdbarch_deprecated_pseudo_register_write_p (gdbarch));
> @@ -1902,6 +1910,30 @@ set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch,
>    gdbarch->pseudo_register_read_value = pseudo_register_read_value;
>  }
>  
> +bool
> +gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  return gdbarch->pseudo_register_write != NULL;
> +}
> +
> +void
> +gdbarch_pseudo_register_write (struct gdbarch *gdbarch, frame_info_ptr next_frame, int pseudo_reg_num, gdb::array_view<const gdb_byte> buf)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  gdb_assert (gdbarch->pseudo_register_write != NULL);
> +  if (gdbarch_debug >= 2)
> +    gdb_printf (gdb_stdlog, "gdbarch_pseudo_register_write called\n");
> +  gdbarch->pseudo_register_write (gdbarch, next_frame, pseudo_reg_num, buf);
> +}
> +
> +void
> +set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch,
> +				   gdbarch_pseudo_register_write_ftype pseudo_register_write)
> +{
> +  gdbarch->pseudo_register_write = pseudo_register_write;
> +}
> +
>  bool
>  gdbarch_deprecated_pseudo_register_write_p (struct gdbarch *gdbarch)
>  {
> diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> index 1100da160550..ce8169b19a57 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -418,6 +418,23 @@ never be called.
>      predicate=True,
>  )
>  
> +Method(
> +    comment="""
> +Write bytes in BUF to pseudo register with number PSEUDO_REG_NUM.
> +
> +Raw registers backing the pseudo register should be written to using
> +NEXT_FRAME.
> +""",
> +    type="void",
> +    name="pseudo_register_write",
> +    params=[
> +        ("frame_info_ptr", "next_frame"),
> +        ("int", "pseudo_reg_num"),
> +        ("gdb::array_view<const gdb_byte>", "buf"),
> +    ],
> +    predicate=True,
> +)
> +
>  Method(
>      comment="""
>  Write bytes to a pseudo register.
> @@ -426,6 +443,8 @@ This is marked as deprecated because it gets passed a regcache for
>  implementations to write raw registers in.  This doesn't work for unwound
>  frames, where the raw registers backing the pseudo registers may have been
>  saved elsewhere.
> +
> +Implementations should be migrated to implement pseudo_register_write instead.
>  """,
>      type="void",
>      name="deprecated_pseudo_register_write",
> diff --git a/gdb/regcache.c b/gdb/regcache.c
> index dadc949434ee..16b117e767be 100644
> --- a/gdb/regcache.c
> +++ b/gdb/regcache.c
> @@ -929,6 +929,10 @@ regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src, float)
>  
>    if (regnum < num_raw_registers ())
>      raw_write (regnum, src, 1.0f);
> +  else if (gdbarch_pseudo_register_write_p (m_descr->gdbarch))
> +    gdbarch_pseudo_register_write
> +      (m_descr->gdbarch, get_next_frame_sentinel_okay (get_current_frame ()),
> +       regnum, src);
>    else
>      gdbarch_deprecated_pseudo_register_write (m_descr->gdbarch, this, regnum,
>  					      src.data ());
> diff --git a/gdb/value.c b/gdb/value.c
> index 27a79dc62fc1..51dca972a587 100644
> --- a/gdb/value.c
> +++ b/gdb/value.c
> @@ -4057,6 +4057,22 @@ pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
>  
>  /* See value.h.  */
>  
> +void
> +pseudo_to_raw_part (frame_info_ptr next_frame,
> +		    gdb::array_view<const gdb_byte> pseudo_buf,
> +		    int raw_reg_num, int raw_offset)
> +{
> +  int raw_reg_size = register_size (get_frame_arch (next_frame), raw_reg_num);
> +
> +  /* When overflowing a register, put_frame_register_bytes writes to the
> +     subsequent registers.  We don't want that behavior here, so make sure
> +     the write is wholly within register RAW_REG_NUM.  */
> +  gdb_assert (raw_offset + pseudo_buf.size () <= raw_reg_size);
> +  put_frame_register_bytes (next_frame, raw_reg_num, raw_offset, pseudo_buf);
> +}
> +
> +/* See value.h.  */
> +
>  value *
>  pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>  			int raw_reg_1_num, int raw_reg_2_num)
> @@ -4080,6 +4096,27 @@ pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>    return pseudo_reg_val;
>  }
>  
> +void
> +pseudo_to_concat_raw (frame_info_ptr next_frame,
> +		      gdb::array_view<const gdb_byte> pseudo_buf,
> +		      int raw_reg_1_num, int raw_reg_2_num)

This function, and its overload are missing a /* See value.h.  */ comment.

> +{
> +  int src_offset = 0;
> +  gdbarch *arch = frame_unwind_arch (next_frame);
> +
> +  int raw_reg_1_size = register_size (arch, raw_reg_1_num);
> +  put_frame_register_bytes (next_frame, raw_reg_1_num, 0,
> +			    pseudo_buf.slice (src_offset, raw_reg_1_size));
> +  src_offset += raw_reg_1_size;
> +
> +  int raw_reg_2_size = register_size (arch, raw_reg_2_num);
> +  put_frame_register_bytes (next_frame, raw_reg_2_num, 0,
> +			    pseudo_buf.slice (src_offset, raw_reg_2_size));
> +  src_offset += raw_reg_2_size;
> +
> +  gdb_assert (src_offset == pseudo_buf.size ());
> +}
> +
>  /* See value.h.  */
>  
>  value *
> @@ -4111,6 +4148,32 @@ pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>    return pseudo_reg_val;
>  }
>  
> +void
> +pseudo_to_concat_raw (frame_info_ptr next_frame,
> +		      gdb::array_view<const gdb_byte> pseudo_buf,
> +		      int raw_reg_1_num, int raw_reg_2_num, int raw_reg_3_num)
> +{
> +  int src_offset = 0;
> +  gdbarch *arch = frame_unwind_arch (next_frame);
> +
> +  int raw_reg_1_size = register_size (arch, raw_reg_1_num);
> +  put_frame_register_bytes (next_frame, raw_reg_1_num, 0,
> +			    pseudo_buf.slice (src_offset, raw_reg_1_size));
> +  src_offset += raw_reg_1_size;
> +
> +  int raw_reg_2_size = register_size (arch, raw_reg_2_num);
> +  put_frame_register_bytes (next_frame, raw_reg_2_num, 0,
> +			    pseudo_buf.slice (src_offset, raw_reg_2_size));
> +  src_offset += raw_reg_2_size;
> +
> +  int raw_reg_3_size = register_size (arch, raw_reg_3_num);
> +  put_frame_register_bytes (next_frame, raw_reg_3_num, 0,
> +			    pseudo_buf.slice (src_offset, raw_reg_3_size));
> +  src_offset += raw_reg_3_size;
> +
> +  gdb_assert (src_offset == pseudo_buf.size ());
> +}
> +
>  /* Implementation of the convenience function $_isvoid.  */
>  
>  static struct value *
> diff --git a/gdb/value.h b/gdb/value.h
> index b5c097ad58bf..6a74d4e2c2ee 100644
> --- a/gdb/value.h
> +++ b/gdb/value.h
> @@ -1661,6 +1661,13 @@ struct scoped_array_length_limiting
>  value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
>  			     int raw_reg_num, int raw_offset);
>  
> +/* Write PSEUDO_BUF, the contents of a pseudo register, to part of raw register
> +   RAW_REG_NUM starting at RAW_OFFSET.  */
> +
> +void pseudo_to_raw_part (frame_info_ptr this_frame,
> +			 gdb::array_view<const gdb_byte> pseudo_buf,
> +			 int raw_reg_num, int raw_offset);
> +
>  /* Create a value for pseudo register PSEUDO_REG_NUM by concatenating raw
>     registers RAW_REG_1_NUM and RAW_REG_2_NUM.
>  
> @@ -1670,10 +1677,25 @@ value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
>  value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>  			       int raw_reg_1_num, int raw_reg_2_num);
>  
> +/* Write PSEUDO_BUF, the contents of a pseudo register, to the two raw registers
> +   RAW_REG_1_NUM and RAW_REG_2_NUM.  */
> +
> +void pseudo_to_concat_raw (frame_info_ptr this_frame,
> +			   gdb::array_view<const gdb_byte> pseudo_buf,
> +			   int raw_reg_1_num, int raw_reg_2_num);
> +
>  /* Same as the above, but with three raw registers.  */
>  
>  value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>  			       int raw_reg_1_num, int raw_reg_2_num,
>  			       int raw_reg_3_num);
>  
> +/* Write PSEUDO_BUF, the contents of a pseudo register, to the tthreewo raw
> +   registers RAW_REG_1_NUM, RAW_REG_2_NUM and RAW_REG_3_NUM.  */

typo s/tthreewo/three/.

Thanks,
Andrew
> +
> +void pseudo_to_concat_raw (frame_info_ptr this_frame,
> +			   gdb::array_view<const gdb_byte> pseudo_buf,
> +			   int raw_reg_1_num, int raw_reg_2_num,
> +			   int raw_reg_3_num);
> +
>  #endif /* !defined (VALUE_H) */
> -- 
> 2.42.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write
  2023-11-14 12:12   ` Andrew Burgess
@ 2023-11-14 15:16     ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-14 15:16 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Simon Marchi

On 11/14/23 07:12, Andrew Burgess wrote:
>> diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
>> index c2de274ed2a9..3160aa8a9613 100644
>> --- a/gdb/gdbarch-gen.h
>> +++ b/gdb/gdbarch-gen.h
>> @@ -200,11 +200,18 @@ typedef struct value * (gdbarch_pseudo_register_read_value_ftype) (struct gdbarc
>>  extern struct value * gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, frame_info_ptr next_frame, int cookednum);
>>  extern void set_gdbarch_pseudo_register_read_value (struct gdbarch *gdbarch, gdbarch_pseudo_register_read_value_ftype *pseudo_register_read_value);
>>  
>> -extern bool gdbarch_pseudo_register_write_p (struct gdbarch *gdbarch);
>> +/* Write bytes to a pseudo register.
>>  
>> -typedef void (gdbarch_pseudo_register_write_ftype) (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
>> -extern void gdbarch_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, int cookednum, const gdb_byte *buf);
>> -extern void set_gdbarch_pseudo_register_write (struct gdbarch *gdbarch, gdbarch_pseudo_register_write_ftype *pseudo_register_write);
>> +   This is marked as deprecated because it gets passed a regcache for
>> +   implementations to write raw registers in.  This doesn't work for unwound
>> +   frames, where the raw registers backing the pseudo registers may have been
>> +   saved elsewhere. */
> 
> Missing a space before '*/' here.
> 
> Thanks,
> Andrew

Hmm, this is in gdbarch-gen.h, so this is due to how gdbarch.py does
things.

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame
  2023-11-14 12:20   ` Andrew Burgess
@ 2023-11-14 15:20     ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-11-14 15:20 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Simon Marchi

On 11/14/23 07:20, Andrew Burgess wrote:
>> @@ -4080,6 +4096,27 @@ pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>>    return pseudo_reg_val;
>>  }
>>  
>> +void
>> +pseudo_to_concat_raw (frame_info_ptr next_frame,
>> +		      gdb::array_view<const gdb_byte> pseudo_buf,
>> +		      int raw_reg_1_num, int raw_reg_2_num)
> 
> This function, and its overload are missing a /* See value.h.  */ comment.

Thanks, fixed.

>> @@ -1670,10 +1677,25 @@ value *pseudo_from_raw_part (frame_info_ptr next_frame, int pseudo_reg_num,
>>  value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>>  			       int raw_reg_1_num, int raw_reg_2_num);
>>  
>> +/* Write PSEUDO_BUF, the contents of a pseudo register, to the two raw registers
>> +   RAW_REG_1_NUM and RAW_REG_2_NUM.  */
>> +
>> +void pseudo_to_concat_raw (frame_info_ptr this_frame,
>> +			   gdb::array_view<const gdb_byte> pseudo_buf,
>> +			   int raw_reg_1_num, int raw_reg_2_num);
>> +
>>  /* Same as the above, but with three raw registers.  */
>>  
>>  value *pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
>>  			       int raw_reg_1_num, int raw_reg_2_num,
>>  			       int raw_reg_3_num);
>>  
>> +/* Write PSEUDO_BUF, the contents of a pseudo register, to the tthreewo raw
>> +   registers RAW_REG_1_NUM, RAW_REG_2_NUM and RAW_REG_3_NUM.  */
> 
> typo s/tthreewo/three/.

Fixed, thanks,

Simon

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 05/24] gdb: change regcache interface to use array_view
  2023-12-01 16:27 Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  0 siblings, 0 replies; 51+ messages in thread
From: Simon Marchi @ 2023-12-01 16:27 UTC (permalink / raw)
  To: gdb-patches
  Cc: Luis Machado, John Baldwin, Aktemur, Tankut Baris, Simon Marchi,
	John Baldwin

New in v3:

 - fix regression in reg_buffer::raw_compare: the old code allows
   passing an offset equal to the register size, effectively comparing 0
   bytes, the code in v2 does not.  Add a selftest checking that this
   behavior works.
 - rename len to size in some functions

New in v2:

 - remove dummy float parameters

Change most of regcache (and base classes) to use array_view when
possible, instead of raw pointers.  By propagating the use of array_view
further, it enables having some runtime checks to make sure the what we
read from or write to regcaches has the expected length (such as the one
in the `copy(array_view, array_view)` function.  It also integrates well
when connecting with other APIs already using gdb::array_view.

Add some overloads of the methods using raw pointers to avoid having to
change all call sites at once (which is both a lot of work and risky).

I tried to do this change in small increments, but since many of these
functions use each other, it ended up simpler to do it in one shot than
having a lot of intermediary / transient changes.

This change extends into gdbserver as well, because there is some part
of the regcache interface that is shared.

Changing the reg_buffer_common interface to use array_view caused some
build failures in nat/aarch64-scalable-linux-ptrace.c.  That file
currently "takes advantage" of the fact that
reg_buffer_common::{raw_supply,raw_collect} operates on `void *`, which
IMO is dangerous.  It uses raw_supply/raw_collect directly on
uint64_t's, which I guess is fine because it is expected that native
code will have the same endianness as the debugged process.  To
accomodate that, add some overloads of raw_collect and raw_supply that
work on uint64_t.

This file also uses raw_collect and raw_supply on `char` pointers.
Change it to use `gdb_byte` pointers instead.  Add overloads of
raw_collect and raw_supply that work on `gdb_byte *` and make an
array_view on the fly using the register's size.  Those call sites could
be converted to use array_view with not much work, in which case these
overloads could be removed, but I didn't want to do it in this patch, to
avoid starting to dig in arch-specific code.

During development, I inadvertently changed reg_buffer::raw_compare's
behavior to not accept an offset equal to the register size.  This
behavior (effectively comparing 0 bytes, returning true) change was
caught by the AArch64 SME core tests.  Add a selftest to make sure that
this raw_compare behavior is preserved in the future.

Change-Id: I9005f04114543ddff738949e12d85a31855304c2
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/frame.c                             |   4 +-
 gdb/nat/aarch64-scalable-linux-ptrace.c |  20 +-
 gdb/regcache.c                          | 493 ++++++++++++++----------
 gdb/regcache.h                          | 110 ++++--
 gdbserver/regcache.cc                   |  49 ++-
 gdbserver/regcache.h                    |   4 +-
 gdbsupport/common-regcache.h            |  38 +-
 gdbsupport/rsp-low.cc                   |   8 +
 gdbsupport/rsp-low.h                    |   2 +
 9 files changed, 457 insertions(+), 271 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index 2a8a33b072d9..529453efa158 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1108,9 +1108,9 @@ get_frame_func (frame_info_ptr this_frame)
 std::unique_ptr<readonly_detached_regcache>
 frame_save_as_regcache (frame_info_ptr this_frame)
 {
-  auto cooked_read = [this_frame] (int regnum, gdb_byte *buf)
+  auto cooked_read = [this_frame] (int regnum, gdb::array_view<gdb_byte> buf)
     {
-      if (!deprecated_frame_register_read (this_frame, regnum, buf))
+      if (!deprecated_frame_register_read (this_frame, regnum, buf.data ()))
 	return REG_UNAVAILABLE;
       else
 	return REG_VALID;
diff --git a/gdb/nat/aarch64-scalable-linux-ptrace.c b/gdb/nat/aarch64-scalable-linux-ptrace.c
index dc0e45fa91ee..b8fb317edaca 100644
--- a/gdb/nat/aarch64-scalable-linux-ptrace.c
+++ b/gdb/nat/aarch64-scalable-linux-ptrace.c
@@ -613,7 +613,7 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
 {
   gdb::byte_vector sve_state = aarch64_fetch_sve_regset (tid);
 
-  char *base = (char *) sve_state.data ();
+  gdb_byte *base = sve_state.data ();
   struct user_sve_header *header
     = (struct user_sve_header *) sve_state.data ();
 
@@ -684,8 +684,10 @@ aarch64_sve_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf)
 	  reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg);
 	}
 
-      reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
-      reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
+      reg_buf->raw_supply (AARCH64_FPSR_REGNUM,
+			   (const gdb_byte *) &fpsimd->fpsr);
+      reg_buf->raw_supply (AARCH64_FPCR_REGNUM,
+			   (const gdb_byte *) &fpsimd->fpcr);
 
       /* Clear the SVE only registers.  */
       memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
@@ -720,7 +722,7 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
   gdb::byte_vector new_state (SVE_PT_SIZE (32, SVE_PT_REGS_SVE), 0);
   memcpy (new_state.data (), sve_state.data (), sve_state.size ());
   header = (struct user_sve_header *) new_state.data ();
-  char *base = (char *) new_state.data ();
+  gdb_byte *base = new_state.data ();
 
   /* Sanity check the data in the header.  */
   if (!sve_vl_valid (header->vl)
@@ -805,9 +807,11 @@ aarch64_sve_regs_copy_from_reg_buf (int tid,
 	    }
 
 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM))
-	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
+	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM,
+				  (gdb_byte *) &fpsimd->fpsr);
 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM))
-	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
+	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM,
+				  (gdb_byte *) &fpsimd->fpcr);
 
 	  /* At this point we have collected all the data from the register
 	     cache and we are ready to update the FPSIMD register content
@@ -894,7 +898,7 @@ aarch64_za_regs_copy_to_reg_buf (int tid, struct reg_buffer_common *reg_buf,
   /* Sanity check.  */
   gdb_assert (!za_state.empty ());
 
-  char *base = (char *) za_state.data ();
+  gdb_byte *base = za_state.data ();
   struct user_za_header *header = (struct user_za_header *) base;
 
   /* If we have ZA state, read it.  Otherwise, make the contents of ZA
@@ -1027,7 +1031,7 @@ aarch64_za_regs_copy_from_reg_buf (int tid,
       /* Fetch the current ZA state from the thread.  */
       gdb::byte_vector za_state = aarch64_fetch_za_regset (tid);
 
-      char *base = (char *) za_state.data ();
+      gdb_byte *base = za_state.data ();
       struct user_za_header *za_header = (struct user_za_header *) base;
       uint64_t svq = sve_vq_from_vl (za_header->vl);
 
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 7eb54d27b135..19ba353a335c 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -220,10 +220,9 @@ regcache::regcache (inferior *inf_for_target_calls, gdbarch *gdbarch)
 
 readonly_detached_regcache::readonly_detached_regcache (regcache &src)
   : readonly_detached_regcache (src.arch (),
-				[&src] (int regnum, gdb_byte *buf)
-				  {
-				    return src.cooked_read (regnum, buf);
-				  })
+				[&src] (int regnum,
+					gdb::array_view<gdb_byte> buf)
+				  { return src.cooked_read (regnum, buf); })
 {
 }
 
@@ -233,19 +232,38 @@ reg_buffer::arch () const
   return m_descr->gdbarch;
 }
 
-/* Return  a pointer to register REGNUM's buffer cache.  */
+/* Helper for reg_buffer::register_buffer.  */
 
-gdb_byte *
+template<typename ElemType>
+gdb::array_view<ElemType>
 reg_buffer::register_buffer (int regnum) const
 {
-  return m_registers.get () + m_descr->register_offset[regnum];
+  assert_regnum (regnum);
+  ElemType *start = &m_registers[m_descr->register_offset[regnum]];
+  int size = m_descr->sizeof_register[regnum];
+  return gdb::array_view<ElemType> (start, size);
+}
+
+/* See regcache.h.  */
+
+gdb::array_view<const gdb_byte>
+reg_buffer::register_buffer (int regnum) const
+{
+  return register_buffer<const gdb_byte> (regnum);
+}
+
+/* See regcache.h.  */
+
+gdb::array_view<gdb_byte>
+reg_buffer::register_buffer (int regnum)
+{
+  return register_buffer<gdb_byte> (regnum);
 }
 
 void
 reg_buffer::save (register_read_ftype cooked_read)
 {
   struct gdbarch *gdbarch = m_descr->gdbarch;
-  int regnum;
 
   /* It should have pseudo registers.  */
   gdb_assert (m_has_pseudo);
@@ -256,17 +274,17 @@ reg_buffer::save (register_read_ftype cooked_read)
      save_reggroup) and mark them as valid.  The full [0 .. gdbarch_num_regs +
      gdbarch_num_pseudo_regs) range is checked since some architectures need
      to save/restore `cooked' registers that live in memory.  */
-  for (regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
+  for (int regnum = 0; regnum < m_descr->nr_cooked_registers; regnum++)
     {
       if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
 	{
-	  gdb_byte *dst_buf = register_buffer (regnum);
-	  enum register_status status = cooked_read (regnum, dst_buf);
+	  gdb::array_view<gdb_byte> dst_buf = register_buffer (regnum);
+	  register_status status = cooked_read (regnum, dst_buf);
 
 	  gdb_assert (status != REG_UNKNOWN);
 
 	  if (status != REG_VALID)
-	    memset (dst_buf, 0, register_size (gdbarch, regnum));
+	    memset (dst_buf.data (), 0, dst_buf.size ());
 
 	  m_register_status[regnum] = status;
 	}
@@ -592,21 +610,30 @@ regcache::raw_update (int regnum)
     }
 }
 
-enum register_status
-readable_regcache::raw_read (int regnum, gdb_byte *buf)
+register_status
+readable_regcache::raw_read (int regnum, gdb::array_view<gdb_byte> dst)
 {
-  gdb_assert (buf != NULL);
+  assert_regnum (regnum);
+  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+
   raw_update (regnum);
 
   if (m_register_status[regnum] != REG_VALID)
-    memset (buf, 0, m_descr->sizeof_register[regnum]);
+    memset (dst.data (), 0, dst.size ());
   else
-    memcpy (buf, register_buffer (regnum),
-	    m_descr->sizeof_register[regnum]);
+    copy (register_buffer (regnum), dst);
 
   return m_register_status[regnum];
 }
 
+register_status
+readable_regcache::raw_read (int regnum, gdb_byte *dst)
+{
+  assert_regnum (regnum);
+  int size = m_descr->sizeof_register[regnum];
+  return raw_read (regnum, gdb::make_array_view (dst, size));
+}
+
 enum register_status
 regcache_raw_read_signed (struct regcache *regcache, int regnum, LONGEST *val)
 {
@@ -619,14 +646,16 @@ enum register_status
 readable_regcache::raw_read (int regnum, T *val)
 {
   assert_regnum (regnum);
-  size_t len = m_descr->sizeof_register[regnum];
-  gdb_byte *buf = (gdb_byte *) alloca (len);
-  register_status status = raw_read (regnum, buf);
+  size_t size = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (size);
+  auto view = gdb::make_array_view (buf, size);
+  register_status status = raw_read (regnum, view);
+
   if (status == REG_VALID)
-    *val = extract_integer<T> ({buf, len},
-			       gdbarch_byte_order (m_descr->gdbarch));
+    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
   else
     *val = 0;
+
   return status;
 }
 
@@ -650,13 +679,13 @@ template<typename T, typename>
 void
 regcache::raw_write (int regnum, T val)
 {
-  gdb_byte *buf;
-
   assert_regnum (regnum);
-  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
-  store_integer (buf, m_descr->sizeof_register[regnum],
-		 gdbarch_byte_order (m_descr->gdbarch), val);
-  raw_write (regnum, buf);
+
+  int size = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (size);
+  auto view = gdb::make_array_view (buf, size);
+  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
+  raw_write (regnum, view);
 }
 
 void
@@ -680,47 +709,60 @@ regcache_raw_get_signed (struct regcache *regcache, int regnum)
   return value;
 }
 
-enum register_status
-readable_regcache::cooked_read (int regnum, gdb_byte *buf)
+/* See regcache.h.  */
+
+register_status
+readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst)
 {
   gdb_assert (regnum >= 0);
   gdb_assert (regnum < m_descr->nr_cooked_registers);
+
   if (regnum < num_raw_registers ())
-    return raw_read (regnum, buf);
-  else if (m_has_pseudo
-	   && m_register_status[regnum] != REG_UNKNOWN)
+    return raw_read (regnum, dst);
+
+  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+
+  if (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
     {
       if (m_register_status[regnum] == REG_VALID)
-	memcpy (buf, register_buffer (regnum),
-		m_descr->sizeof_register[regnum]);
+	copy (register_buffer (regnum), dst);
       else
-	memset (buf, 0, m_descr->sizeof_register[regnum]);
+	memset (dst.data (), 0, dst.size ());
 
       return m_register_status[regnum];
     }
   else if (gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
     {
-      struct value *computed;
-      enum register_status result = REG_VALID;
-
+      register_status result = REG_VALID;
       scoped_value_mark mark;
+      value *computed
+	= gdbarch_pseudo_register_read_value (m_descr->gdbarch, this, regnum);
 
-      computed = gdbarch_pseudo_register_read_value (m_descr->gdbarch,
-						     this, regnum);
       if (computed->entirely_available ())
-	memcpy (buf, computed->contents_raw ().data (),
-		m_descr->sizeof_register[regnum]);
+	copy (computed->contents_raw (), dst);
       else
 	{
-	  memset (buf, 0, m_descr->sizeof_register[regnum]);
+	  memset (dst.data (), 0, dst.size ());
 	  result = REG_UNAVAILABLE;
 	}
 
       return result;
     }
   else
-    return gdbarch_pseudo_register_read (m_descr->gdbarch, this,
-					 regnum, buf);
+    return gdbarch_pseudo_register_read (m_descr->gdbarch, this, regnum,
+					 dst.data ());
+}
+
+/* See regcache.h.  */
+
+register_status
+readable_regcache::cooked_read (int regnum, gdb_byte *dst)
+{
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+  int size = m_descr->sizeof_register[regnum];
+  return cooked_read (regnum, gdb::make_array_view (dst, size));
 }
 
 struct value *
@@ -742,8 +784,7 @@ readable_regcache::cooked_read_value (int regnum)
       /* It is more efficient in general to do this delegation in this
 	 direction than in the other one, even though the value-based
 	 API is preferred.  */
-      if (cooked_read (regnum,
-		       result->contents_raw ().data ()) == REG_UNAVAILABLE)
+      if (cooked_read (regnum, result->contents_raw ()) == REG_UNAVAILABLE)
 	result->mark_bytes_unavailable (0,
 					result->type ()->length ());
 
@@ -767,12 +808,12 @@ enum register_status
 readable_regcache::cooked_read (int regnum, T *val)
 {
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
-  size_t len = m_descr->sizeof_register[regnum];
-  gdb_byte *buf = (gdb_byte *) alloca (len);
-  register_status status = cooked_read (regnum, buf);
+  size_t size = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (size);
+  auto view = gdb::make_array_view (buf, size);
+  register_status status = cooked_read (regnum, view);
   if (status == REG_VALID)
-    *val = extract_integer<T> ({buf, len},
-			       gdbarch_byte_order (m_descr->gdbarch));
+    *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
   else
     *val = 0;
   return status;
@@ -798,13 +839,14 @@ template<typename T, typename>
 void
 regcache::cooked_write (int regnum, T val)
 {
-  gdb_byte *buf;
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
 
-  gdb_assert (regnum >=0 && regnum < m_descr->nr_cooked_registers);
-  buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
-  store_integer (buf, m_descr->sizeof_register[regnum],
-		 gdbarch_byte_order (m_descr->gdbarch), val);
-  cooked_write (regnum, buf);
+  int size = m_descr->sizeof_register[regnum];
+  gdb_byte *buf = (gdb_byte *) alloca (size);
+  auto view = gdb::make_array_view (buf, size);
+  store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
+  cooked_write (regnum, view);
 }
 
 void
@@ -816,11 +858,10 @@ regcache_cooked_write_unsigned (struct regcache *regcache, int regnum,
 }
 
 void
-regcache::raw_write (int regnum, const gdb_byte *buf)
+regcache::raw_write (int regnum, gdb::array_view<const gdb_byte> src)
 {
-
-  gdb_assert (buf != NULL);
   assert_regnum (regnum);
+  gdb_assert (src.size () == m_descr->sizeof_register[regnum]);
 
   /* On the sparc, writing %g0 is a no-op, so we don't even want to
      change the registers array if something writes to this register.  */
@@ -830,15 +871,15 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
   /* If we have a valid copy of the register, and new value == old
      value, then don't bother doing the actual store.  */
   if (get_register_status (regnum) == REG_VALID
-      && (memcmp (register_buffer (regnum), buf,
-		  m_descr->sizeof_register[regnum]) == 0))
+      && (memcmp (register_buffer (regnum).data (), src.data (), src.size ())
+	  == 0))
     return;
 
   std::optional<scoped_restore_current_thread> maybe_restore_thread
     = maybe_switch_inferior (m_inf_for_target_calls);
 
   target_prepare_to_store (this);
-  raw_supply (regnum, buf);
+  raw_supply (regnum, src);
 
   /* Invalidate the register after it is written, in case of a
      failure.  */
@@ -853,211 +894,248 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
 }
 
 void
-regcache::cooked_write (int regnum, const gdb_byte *buf)
+regcache::raw_write (int regnum, const gdb_byte *src)
+{
+  assert_regnum (regnum);
+
+  int size = m_descr->sizeof_register[regnum];
+  raw_write (regnum, gdb::make_array_view (src, size));
+}
+
+/* See regcache.h.  */
+
+void
+regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src)
 {
   gdb_assert (regnum >= 0);
   gdb_assert (regnum < m_descr->nr_cooked_registers);
+
   if (regnum < num_raw_registers ())
-    raw_write (regnum, buf);
+    raw_write (regnum, src);
   else
-    gdbarch_pseudo_register_write (m_descr->gdbarch, this,
-				   regnum, buf);
+    gdbarch_pseudo_register_write (m_descr->gdbarch, this, regnum,
+				   src.data ());
 }
 
 /* See regcache.h.  */
 
-enum register_status
-readable_regcache::read_part (int regnum, int offset, int len,
-			      gdb_byte *out, bool is_raw)
+void
+regcache::cooked_write (int regnum, const gdb_byte *src)
+{
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+  int size = m_descr->sizeof_register[regnum];
+  return cooked_write (regnum, gdb::make_array_view (src, size));
+}
+
+/* See regcache.h.  */
+
+register_status
+readable_regcache::read_part (int regnum, int offset,
+			      gdb::array_view<gdb_byte> dst, bool is_raw)
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (out != NULL);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + dst.size () <= reg_size);
 
-  if (len == 0)
+  if (dst.size () == 0)
     {
       /* Nothing to do.  */
       return REG_VALID;
     }
 
-  if (len == reg_size)
+  if (dst.size () == reg_size)
     {
       /* Read the full register.  */
-      return (is_raw) ? raw_read (regnum, out) : cooked_read (regnum, out);
+      if (is_raw)
+	return raw_read (regnum, dst);
+      else
+	return cooked_read (regnum, dst);
     }
 
-  enum register_status status;
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
-
   /* Read full register to buffer.  */
-  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
+  register_status status;
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
+
+  if (is_raw)
+    status = raw_read (regnum, reg);
+  else
+    status = cooked_read (regnum, reg);
+
   if (status != REG_VALID)
     return status;
 
   /* Copy out.  */
-  memcpy (out, reg + offset, len);
+  copy (reg.slice (offset, dst.size ()), dst);
   return REG_VALID;
 }
 
 /* See regcache.h.  */
 
 void
-reg_buffer::raw_collect_part (int regnum, int offset, int len,
-			      gdb_byte *out) const
+reg_buffer::raw_collect_part (int regnum, int offset,
+			      gdb::array_view<gdb_byte> dst) const
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (out != nullptr);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + dst.size () <= reg_size);
 
-  if (len == 0)
+  if (dst.size () == 0)
     {
       /* Nothing to do.  */
       return;
     }
 
-  if (len == reg_size)
+  if (dst.size () == reg_size)
     {
       /* Collect the full register.  */
-      return raw_collect (regnum, out);
+      return raw_collect (regnum, dst);
     }
 
   /* Read to buffer, then write out.  */
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
   raw_collect (regnum, reg);
-  memcpy (out, reg + offset, len);
+  copy (reg.slice (offset, dst.size ()), dst);
 }
 
 /* See regcache.h.  */
 
-enum register_status
-regcache::write_part (int regnum, int offset, int len,
-		      const gdb_byte *in, bool is_raw)
+register_status
+regcache::write_part (int regnum, int offset,
+		      gdb::array_view<const gdb_byte> src, bool is_raw)
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (in != NULL);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + src.size () <= reg_size);
 
-  if (len == 0)
+  if (src.size () == 0)
     {
       /* Nothing to do.  */
       return REG_VALID;
     }
 
-  if (len == reg_size)
+  if (src.size () == reg_size)
     {
       /* Write the full register.  */
-      (is_raw) ? raw_write (regnum, in) : cooked_write (regnum, in);
+      if (is_raw)
+	raw_write (regnum, src);
+      else
+	cooked_write (regnum, src);
+
       return REG_VALID;
     }
 
-  enum register_status status;
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
-
   /* Read existing register to buffer.  */
-  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
+  register_status status;
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
+
+  if (is_raw)
+    status = raw_read (regnum, reg);
+  else
+    status = cooked_read (regnum, reg);
+
   if (status != REG_VALID)
     return status;
 
   /* Update buffer, then write back to regcache.  */
-  memcpy (reg + offset, in, len);
-  is_raw ? raw_write (regnum, reg) : cooked_write (regnum, reg);
+  copy (src, reg.slice (offset, src.size ()));
+
+  if (is_raw)
+    raw_write (regnum, reg);
+  else
+    cooked_write (regnum, reg);
+
   return REG_VALID;
 }
 
 /* See regcache.h.  */
 
 void
-reg_buffer::raw_supply_part (int regnum, int offset, int len,
-			     const gdb_byte *in)
+reg_buffer::raw_supply_part (int regnum, int offset,
+			     gdb::array_view<const gdb_byte> src)
 {
   int reg_size = register_size (arch (), regnum);
 
-  gdb_assert (in != nullptr);
   gdb_assert (offset >= 0);
-  gdb_assert (len >= 0 && offset + len <= reg_size);
+  gdb_assert (offset + src.size () <= reg_size);
 
-  if (len == 0)
+  if (src.size () == 0)
     {
       /* Nothing to do.  */
       return;
     }
 
-  if (len == reg_size)
+  if (src.size () == reg_size)
     {
       /* Supply the full register.  */
-      return raw_supply (regnum, in);
+      return raw_supply (regnum, src);
     }
 
-  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
-
   /* Read existing value to buffer.  */
+  gdb_byte *reg_buf = (gdb_byte *) alloca (reg_size);
+  auto reg = gdb::make_array_view (reg_buf, reg_size);
   raw_collect (regnum, reg);
 
   /* Write to buffer, then write out.  */
-  memcpy (reg + offset, in, len);
+  copy (src, reg.slice (offset, src.size ()));
   raw_supply (regnum, reg);
 }
 
-enum register_status
-readable_regcache::raw_read_part (int regnum, int offset, int len,
-				  gdb_byte *buf)
+register_status
+readable_regcache::raw_read_part (int regnum, int offset,
+				  gdb::array_view<gdb_byte> dst)
 {
   assert_regnum (regnum);
-  return read_part (regnum, offset, len, buf, true);
+  return read_part (regnum, offset, dst, true);
 }
 
 /* See regcache.h.  */
 
 void
-regcache::raw_write_part (int regnum, int offset, int len,
-			  const gdb_byte *buf)
+regcache::raw_write_part (int regnum, int offset,
+			  gdb::array_view<const gdb_byte> src)
 {
   assert_regnum (regnum);
-  write_part (regnum, offset, len, buf, true);
+  write_part (regnum, offset, src, true);
 }
 
 /* See regcache.h.  */
 
-enum register_status
-readable_regcache::cooked_read_part (int regnum, int offset, int len,
-				     gdb_byte *buf)
+register_status
+readable_regcache::cooked_read_part (int regnum, int offset,
+				     gdb::array_view<gdb_byte> dst)
 {
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
-  return read_part (regnum, offset, len, buf, false);
+  return read_part (regnum, offset, dst, false);
 }
 
 /* See regcache.h.  */
 
 void
-regcache::cooked_write_part (int regnum, int offset, int len,
-			     const gdb_byte *buf)
+regcache::cooked_write_part (int regnum, int offset,
+			     gdb::array_view<const gdb_byte> src)
 {
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
-  write_part (regnum, offset, len, buf, false);
+  write_part (regnum, offset, src, false);
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-reg_buffer::raw_supply (int regnum, const void *buf)
+reg_buffer::raw_supply (int regnum, gdb::array_view<const gdb_byte> src)
 {
-  void *regbuf;
-  size_t size;
+  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
 
-  assert_regnum (regnum);
-
-  regbuf = register_buffer (regnum);
-  size = m_descr->sizeof_register[regnum];
-
-  if (buf)
+  if (src.data () != nullptr)
     {
-      memcpy (regbuf, buf, size);
+      copy (src, dst);
       m_register_status[regnum] = REG_VALID;
     }
   else
@@ -1065,7 +1143,7 @@ reg_buffer::raw_supply (int regnum, const void *buf)
       /* This memset not strictly necessary, but better than garbage
 	 in case the register value manages to escape somewhere (due
 	 to a bug, no less).  */
-      memset (regbuf, 0, size);
+      memset (dst.data (), 0, dst.size ());
       m_register_status[regnum] = REG_UNAVAILABLE;
     }
 }
@@ -1073,19 +1151,24 @@ reg_buffer::raw_supply (int regnum, const void *buf)
 /* See regcache.h.  */
 
 void
-reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
-				int addr_len, bool is_signed)
+reg_buffer::raw_supply (int regnum, const void *src)
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
-  gdb_byte *regbuf;
-  size_t regsize;
-
   assert_regnum (regnum);
 
-  regbuf = register_buffer (regnum);
-  regsize = m_descr->sizeof_register[regnum];
+  int size = m_descr->sizeof_register[regnum];
+  raw_supply (regnum, gdb::make_array_view ((const gdb_byte *) src, size));
+}
+
+/* See regcache.h.  */
 
-  copy_integer_to_size (regbuf, regsize, addr, addr_len, is_signed,
+void
+reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr, int addr_len,
+				bool is_signed)
+{
+  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
+  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
+
+  copy_integer_to_size (dst.data (), dst.size (), addr, addr_len, is_signed,
 			byte_order);
   m_register_status[regnum] = REG_VALID;
 }
@@ -1095,32 +1178,29 @@ reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
 void
 reg_buffer::raw_supply_zeroed (int regnum)
 {
-  void *regbuf;
-  size_t size;
-
-  assert_regnum (regnum);
-
-  regbuf = register_buffer (regnum);
-  size = m_descr->sizeof_register[regnum];
-
-  memset (regbuf, 0, size);
+  gdb::array_view<gdb_byte> dst = register_buffer (regnum);
+  memset (dst.data (), 0, dst.size ());
   m_register_status[regnum] = REG_VALID;
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-reg_buffer::raw_collect (int regnum, void *buf) const
+reg_buffer::raw_collect (int regnum, gdb::array_view<gdb_byte> dst) const
 {
-  const void *regbuf;
-  size_t size;
+  gdb::array_view<const gdb_byte> src = register_buffer (regnum);
+  copy (src, dst);
+}
 
-  gdb_assert (buf != NULL);
+/* See regcache.h.  */
+
+void
+reg_buffer::raw_collect (int regnum, void *dst) const
+{
   assert_regnum (regnum);
 
-  regbuf = register_buffer (regnum);
-  size = m_descr->sizeof_register[regnum];
-  memcpy (buf, regbuf, size);
+  int size = m_descr->sizeof_register[regnum];
+  return raw_collect (regnum, gdb::make_array_view ((gdb_byte *) dst, size));
 }
 
 /* See regcache.h.  */
@@ -1129,16 +1209,9 @@ void
 reg_buffer::raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
 				 bool is_signed) const
 {
-  enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
-  const gdb_byte *regbuf;
-  size_t regsize;
-
-  assert_regnum (regnum);
-
-  regbuf = register_buffer (regnum);
-  regsize = m_descr->sizeof_register[regnum];
-
-  copy_integer_to_size (addr, addr_len, regbuf, regsize, is_signed,
+  gdb::array_view<const gdb_byte> dst = register_buffer (regnum);
+  bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
+  copy_integer_to_size (addr, addr_len, dst.data (), dst.size (), is_signed,
 			byte_order);
 }
 
@@ -1157,7 +1230,8 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
 
   if (out_buf != nullptr)
     {
-      raw_collect_part (regnum, 0, reg_size, out_buf + offs);
+      raw_collect_part (regnum, 0,
+			gdb::make_array_view (out_buf + offs, reg_size));
 
       /* Ensure any additional space is cleared.  */
       if (slot_size > reg_size)
@@ -1168,12 +1242,14 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
       /* Zero-extend the register value if the slot is smaller than the register.  */
       if (slot_size < register_size (gdbarch, regnum))
 	out_regcache->raw_supply_zeroed (regnum);
-      out_regcache->raw_supply_part (regnum, 0, reg_size, in_buf + offs);
+      out_regcache->raw_supply_part (regnum, 0,
+				     gdb::make_array_view (in_buf + offs,
+							   reg_size));
     }
   else
     {
       /* Invalidate the register.  */
-      out_regcache->raw_supply (regnum, nullptr);
+      out_regcache->raw_supply (regnum, {});
     }
 }
 
@@ -1304,13 +1380,12 @@ bool
 reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
 {
   gdb_assert (buf != NULL);
-  assert_regnum (regnum);
 
-  const char *regbuf = (const char *) register_buffer (regnum);
-  size_t size = m_descr->sizeof_register[regnum];
-  gdb_assert (size >= offset);
+  gdb::array_view<const gdb_byte> regbuf = register_buffer (regnum);
+  gdb_assert (offset <= regbuf.size ());
+  regbuf = regbuf.slice (offset);
 
-  return (memcmp (buf, regbuf + offset, size - offset) == 0);
+  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
 }
 
 /* Special handling for register PC.  */
@@ -1399,17 +1474,15 @@ regcache::debug_print_register (const char *func,  int regno)
   if (regno >= 0 && regno < gdbarch_num_regs (gdbarch))
     {
       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-      int size = register_size (gdbarch, regno);
-      gdb_byte *buf = register_buffer (regno);
+      gdb::array_view<gdb_byte> buf = register_buffer (regno);
 
       gdb_printf (gdb_stdlog, " = ");
-      for (int i = 0; i < size; i++)
-	{
-	  gdb_printf (gdb_stdlog, "%02x", buf[i]);
-	}
-      if (size <= sizeof (LONGEST))
+      for (gdb_byte byte : buf)
+	gdb_printf (gdb_stdlog, "%02x", byte);
+
+      if (buf.size () <= sizeof (LONGEST))
 	{
-	  ULONGEST val = extract_unsigned_integer (buf, size, byte_order);
+	  ULONGEST val = extract_unsigned_integer (buf, byte_order);
 
 	  gdb_printf (gdb_stdlog, " %s %s",
 		      core_addr_to_string_nz (val), plongest (val));
@@ -1758,6 +1831,23 @@ registers_changed_ptid_target_ptid_test ()
 			      ptid_t (2, 2)) == 1);
 }
 
+/* Test using reg_buffer::raw_compare with offset equal to the register size
+   (thus comparing 0 bytes).  */
+
+static void
+reg_buffer_raw_compare_zero_len_test ()
+{
+  regcache_test_data_up data = populate_regcaches_for_test ();
+  inferior &inf = data->test_ctx_1.mock_inferior;
+  const regcache *regcache
+    = get_thread_arch_regcache (&inf, ptid_t (1, 1), inf.arch ());
+
+  /* The buffer address is irrelevant since we end up comparing 0 bytes, we just
+     need to pass something.  */
+  gdb_byte buf;
+  SELF_CHECK (regcache->raw_compare (0, &buf, register_size (inf.arch (), 0)));
+}
+
 class target_ops_no_register : public test_target_ops
 {
 public:
@@ -1869,7 +1959,7 @@ cooked_read_test (struct gdbarch *gdbarch)
   readwrite.set_ptid (mockctx.mock_ptid);
   gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum));
 
-  readwrite.raw_read (nonzero_regnum, buf.data ());
+  readwrite.raw_read (nonzero_regnum, buf);
 
   /* raw_read calls target_fetch_registers.  */
   SELF_CHECK (mockctx.mock_target.fetch_registers_called > 0);
@@ -1890,9 +1980,7 @@ cooked_read_test (struct gdbarch *gdbarch)
 
       gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
 
-      SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum,
-						      inner_buf.data ()));
-
+      SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum, inner_buf));
       SELF_CHECK (mockctx.mock_target.fetch_registers_called == 0);
       SELF_CHECK (mockctx.mock_target.store_registers_called == 0);
       SELF_CHECK (mockctx.mock_target.xfer_partial_called == 0);
@@ -1912,8 +2000,7 @@ cooked_read_test (struct gdbarch *gdbarch)
 	continue;
 
       gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
-      enum register_status status = readonly.cooked_read (regnum,
-							  inner_buf.data ());
+      register_status status = readonly.cooked_read (regnum, inner_buf);
 
       if (regnum < gdbarch_num_regs (gdbarch))
 	{
@@ -2003,8 +2090,8 @@ cooked_write_test (struct gdbarch *gdbarch)
 	      && regnum <= gdbarch_num_regs (gdbarch) + 4))
 	continue;
 
-      std::vector<gdb_byte> expected (register_size (gdbarch, regnum), 0);
-      std::vector<gdb_byte> buf (register_size (gdbarch, regnum), 0);
+      gdb::byte_vector expected (register_size (gdbarch, regnum), 0);
+      gdb::byte_vector buf (register_size (gdbarch, regnum), 0);
       const auto type = register_type (gdbarch, regnum);
 
       if (type->code () == TYPE_CODE_FLT
@@ -2059,9 +2146,9 @@ cooked_write_test (struct gdbarch *gdbarch)
 	  SELF_CHECK (0);
 	}
 
-      readwrite.cooked_write (regnum, expected.data ());
+      readwrite.cooked_write (regnum, expected);
 
-      SELF_CHECK (readwrite.cooked_read (regnum, buf.data ()) == REG_VALID);
+      SELF_CHECK (readwrite.cooked_read (regnum, buf) == REG_VALID);
       SELF_CHECK (expected == buf);
     }
 }
@@ -2154,6 +2241,8 @@ _initialize_regcache ()
 			    selftests::registers_changed_ptid_target_pid_test);
   selftests::register_test ("registers_changed_ptid_target_ptid",
 			    selftests::registers_changed_ptid_target_ptid_test);
+  selftests::register_test ("reg_buffer_raw_compare_zero_len",
+			    selftests::reg_buffer_raw_compare_zero_len_test);
 
   selftests::register_test_foreach_arch ("regcache::cooked_read_test",
 					 selftests::cooked_read_test);
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 7922a5c5ae21..d90f74bfbb06 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -20,6 +20,7 @@
 #ifndef REGCACHE_H
 #define REGCACHE_H
 
+#include "gdbsupport/array-view.h"
 #include "gdbsupport/common-regcache.h"
 #include "gdbsupport/function-view.h"
 
@@ -167,8 +168,8 @@ extern struct type *register_type (struct gdbarch *gdbarch, int regnum);
    
 extern int register_size (struct gdbarch *gdbarch, int regnum);
 
-typedef gdb::function_view<register_status (int regnum, gdb_byte *buf)>
-  register_read_ftype;
+using register_read_ftype
+  = gdb::function_view<register_status (int, gdb::array_view<gdb_byte>)>;
 
 /* A (register_number, register_value) pair.  */
 
@@ -194,7 +195,10 @@ class reg_buffer : public reg_buffer_common
   enum register_status get_register_status (int regnum) const override;
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_collect (int regnum, void *buf) const override;
+  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst) const override;
+
+  /* Deprecated overload of the above.  */
+  void raw_collect (int regnum, void *dst) const;
 
   /* Collect register REGNUM from REGCACHE.  Store collected value as an integer
      at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
@@ -204,17 +208,23 @@ class reg_buffer : public reg_buffer_common
   void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
 			    bool is_signed) const;
 
-  /* Collect register REGNUM from REGCACHE, starting at OFFSET in register,
-     reading only LEN.  */
-  void raw_collect_part (int regnum, int offset, int len, gdb_byte *out) const;
+  /* Collect part of register REGNUM from this register buffer.  Start at OFFSET
+     in register.  The size is given by the size of DST.  */
+  void raw_collect_part (int regnum, int offset,
+			 gdb::array_view<gdb_byte> dst) const;
+
+  /* Deprecated overload of the above.  */
+  void raw_collect_part (int regnum, int offset, int len, gdb_byte *dst) const
+  { raw_collect_part (regnum, offset, gdb::make_array_view (dst, len)); }
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_supply (int regnum, const void *buf) override;
+  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src) override;
+
+  /* Deprecated overload of the above.  */
+  void raw_supply (int regnum, const void *src);
 
   void raw_supply (int regnum, const reg_buffer &src)
-  {
-    raw_supply (regnum, src.register_buffer (regnum));
-  }
+  { raw_supply (regnum, src.register_buffer (regnum)); }
 
   /* Supply register REGNUM to REGCACHE.  Value to supply is an integer stored
      at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
@@ -229,9 +239,11 @@ class reg_buffer : public reg_buffer_common
      unavailable).  */
   void raw_supply_zeroed (int regnum);
 
-  /* Supply register REGNUM to REGCACHE, starting at OFFSET in register, writing
-     only LEN, without editing the rest of the register.  */
-  void raw_supply_part (int regnum, int offset, int len, const gdb_byte *in);
+  /* Supply part of register REGNUM to this register buffer.  Start at OFFSET in
+     the register.  The size is given by the size of SRC.  The rest of the
+     register left untouched.  */
+  void raw_supply_part (int regnum, int offset,
+			gdb::array_view<const gdb_byte> src);
 
   void invalidate (int regnum);
 
@@ -246,7 +258,11 @@ class reg_buffer : public reg_buffer_common
 
   int num_raw_registers () const;
 
-  gdb_byte *register_buffer (int regnum) const;
+  /* Return a view on register REGNUM's buffer cache.  */
+  template <typename ElemType>
+  gdb::array_view<ElemType> register_buffer (int regnum) const;
+  gdb::array_view<const gdb_byte> register_buffer (int regnum) const;
+  gdb::array_view<gdb_byte> register_buffer (int regnum);
 
   /* Save a register cache.  The set of registers saved into the
      regcache determined by the save_reggroup.  COOKED_READ returns
@@ -276,27 +292,41 @@ class readable_regcache : public reg_buffer
 
   /* Transfer a raw register [0..NUM_REGS) from core-gdb to this regcache,
      return its value in *BUF and return its availability status.  */
+  register_status raw_read (int regnum, gdb::array_view<gdb_byte> dst);
+
+  /* Deprecated overload of the above.  */
+  register_status raw_read (int regnum, gdb_byte *dst);
 
-  enum register_status raw_read (int regnum, gdb_byte *buf);
   template<typename T, typename = RequireLongest<T>>
-  enum register_status raw_read (int regnum, T *val);
+  register_status raw_read (int regnum, T *val);
 
   /* Partial transfer of raw registers.  Return the status of the register.  */
-  enum register_status raw_read_part (int regnum, int offset, int len,
-				      gdb_byte *buf);
+  register_status raw_read_part (int regnum, int offset,
+				 gdb::array_view<gdb_byte> dst);
+
+  /* Deprecated overload of the above.  */
+  register_status raw_read_part (int regnum, int offset, int len,
+				 gdb_byte *dst)
+  { return raw_read_part (regnum, offset, gdb::make_array_view (dst, len)); }
 
   /* Make certain that the register REGNUM is up-to-date.  */
   virtual void raw_update (int regnum) = 0;
 
   /* Transfer a raw register [0..NUM_REGS+NUM_PSEUDO_REGS) from core-gdb to
-     this regcache, return its value in *BUF and return its availability status.  */
-  enum register_status cooked_read (int regnum, gdb_byte *buf);
+     this regcache, return its value in DST and return its availability status.  */
+  register_status cooked_read (int regnum, gdb::array_view<gdb_byte> dst);
+  register_status cooked_read (int regnum, gdb_byte *dst);
+
   template<typename T, typename = RequireLongest<T>>
-  enum register_status cooked_read (int regnum, T *val);
+  register_status cooked_read (int regnum, T *val);
 
   /* Partial transfer of a cooked register.  */
-  enum register_status cooked_read_part (int regnum, int offset, int len,
-					 gdb_byte *buf);
+  register_status cooked_read_part (int regnum, int offset,
+				    gdb::array_view<gdb_byte> dst);
+
+  /* Deprecated overload of the above.  */
+  register_status cooked_read_part (int regnum, int offset, int len, gdb_byte *src)
+  { return cooked_read_part (regnum, offset, gdb::make_array_view (src, len)); }
 
   /* Read register REGNUM from the regcache and return a new value.  This
      will call mark_value_bytes_unavailable as appropriate.  */
@@ -306,8 +336,8 @@ class readable_regcache : public reg_buffer
 
   /* Perform a partial register transfer using a read, modify, write
      operation.  Will fail if register is currently invalid.  */
-  enum register_status read_part (int regnum, int offset, int len,
-				  gdb_byte *out, bool is_raw);
+  register_status read_part (int regnum, int offset,
+			     gdb::array_view<gdb_byte> dst, bool is_raw);
 };
 
 /* Buffer of registers, can be read and written.  */
@@ -343,13 +373,19 @@ class regcache : public detached_regcache
   /* Update the value of raw register REGNUM (in the range [0..NUM_REGS)) and
      transfer its value to core-gdb.  */
 
-  void raw_write (int regnum, const gdb_byte *buf);
+  void raw_write (int regnum, gdb::array_view<const gdb_byte> src);
+
+  /* Deprecated overload of the above.  */
+  void raw_write (int regnum, const gdb_byte *src);
 
   template<typename T, typename = RequireLongest<T>>
   void raw_write (int regnum, T val);
 
   /* Transfer of pseudo-registers.  */
-  void cooked_write (int regnum, const gdb_byte *buf);
+  void cooked_write (int regnum, gdb::array_view<const gdb_byte> src);
+
+  /* Deprecated overload of the above.  */
+  void cooked_write (int regnum, const gdb_byte *src);
 
   template<typename T, typename = RequireLongest<T>>
   void cooked_write (int regnum, T val);
@@ -358,12 +394,21 @@ class regcache : public detached_regcache
 
   /* Partial transfer of raw registers.  Perform read, modify, write style
      operations.  */
-  void raw_write_part (int regnum, int offset, int len, const gdb_byte *buf);
+  void raw_write_part (int regnum, int offset,
+		       gdb::array_view<const gdb_byte> src);
+
+  /* Deprecated overload of the above.  */
+  void raw_write_part (int regnum, int offset, int len, const gdb_byte *src)
+  { raw_write_part (regnum, offset, gdb::make_array_view (src, len)); }
 
   /* Partial transfer of a cooked register.  Perform read, modify, write style
      operations.  */
-  void cooked_write_part (int regnum, int offset, int len,
-			  const gdb_byte *buf);
+  void cooked_write_part (int regnum, int offset,
+			  gdb::array_view<const gdb_byte> src);
+
+  /* Deprecated overload of the above.  */
+  void cooked_write_part (int regnum, int offset, int len, const gdb_byte *src)
+  { cooked_write_part (regnum, offset, gdb::make_array_view (src, len)); }
 
   /* Transfer a set of registers (as described by REGSET) between
      REGCACHE and BUF.  If REGNUM == -1, transfer all registers
@@ -430,8 +475,9 @@ class regcache : public detached_regcache
 
   /* Perform a partial register transfer using a read, modify, write
      operation.  */
-  enum register_status write_part (int regnum, int offset, int len,
-				   const gdb_byte *in, bool is_raw);
+  register_status write_part (int regnum, int offset,
+			      gdb::array_view<const gdb_byte> src,
+			      bool is_raw);
 
   /* The inferior to switch to, to make target calls.
 
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 823ea1f015d3..c5d3670b4b56 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -315,27 +315,32 @@ regcache_register_size (const reg_buffer_common *regcache, int n)
     (gdb::checked_static_cast<const struct regcache *> (regcache)->tdesc, n);
 }
 
-static unsigned char *
+static gdb::array_view<gdb_byte>
 register_data (const struct regcache *regcache, int n)
 {
-  return (regcache->registers
-	  + find_register_by_number (regcache->tdesc, n).offset / 8);
+  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
+  return gdb::make_array_view (regcache->registers + reg.offset / 8,
+			       reg.size / 8);
 }
 
 void
-supply_register (struct regcache *regcache, int n, const void *buf)
+supply_register (struct regcache *regcache, int n, const void *vbuf)
 {
-  return regcache->raw_supply (n, buf);
+  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
+  const gdb_byte *buf = static_cast<const gdb_byte *> (vbuf);
+  return regcache->raw_supply (n, gdb::make_array_view (buf, reg.size / 8));
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-regcache::raw_supply (int n, const void *buf)
+regcache::raw_supply (int n, gdb::array_view<const gdb_byte> src)
 {
-  if (buf)
+  auto dst = register_data (this, n);
+
+  if (src.data () != nullptr)
     {
-      memcpy (register_data (this, n), buf, register_size (tdesc, n));
+      copy (src, dst);
 #ifndef IN_PROCESS_AGENT
       if (register_status != NULL)
 	register_status[n] = REG_VALID;
@@ -343,7 +348,7 @@ regcache::raw_supply (int n, const void *buf)
     }
   else
     {
-      memset (register_data (this, n), 0, register_size (tdesc, n));
+      memset (dst.data (), 0, dst.size ());
 #ifndef IN_PROCESS_AGENT
       if (register_status != NULL)
 	register_status[n] = REG_UNAVAILABLE;
@@ -356,8 +361,8 @@ regcache::raw_supply (int n, const void *buf)
 void
 supply_register_zeroed (struct regcache *regcache, int n)
 {
-  memset (register_data (regcache, n), 0,
-	  register_size (regcache->tdesc, n));
+  auto dst = register_data (regcache, n);
+  memset (dst.data (), 0, dst.size ());
 #ifndef IN_PROCESS_AGENT
   if (regcache->register_status != NULL)
     regcache->register_status[n] = REG_VALID;
@@ -426,17 +431,20 @@ supply_register_by_name (struct regcache *regcache,
 #endif
 
 void
-collect_register (struct regcache *regcache, int n, void *buf)
+collect_register (struct regcache *regcache, int n, void *vbuf)
 {
-  regcache->raw_collect (n, buf);
+  const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
+  gdb_byte *buf = static_cast<gdb_byte *> (vbuf);
+  regcache->raw_collect (n, gdb::make_array_view (buf, reg.size / 8));
 }
 
 /* See gdbsupport/common-regcache.h.  */
 
 void
-regcache::raw_collect (int n, void *buf) const
+regcache::raw_collect (int n, gdb::array_view<gdb_byte> dst) const
 {
-  memcpy (buf, register_data (this, n), register_size (tdesc, n));
+  auto src = register_data (this, n);
+  copy (src, dst);
 }
 
 enum register_status
@@ -476,8 +484,7 @@ regcache_raw_get_unsigned_by_name (struct regcache *regcache,
 void
 collect_register_as_string (struct regcache *regcache, int n, char *buf)
 {
-  bin2hex (register_data (regcache, n), buf,
-	   register_size (regcache->tdesc, n));
+  bin2hex (register_data (regcache, n), buf);
 }
 
 void
@@ -524,9 +531,9 @@ regcache::raw_compare (int regnum, const void *buf, int offset) const
 {
   gdb_assert (buf != NULL);
 
-  const unsigned char *regbuf = register_data (this, regnum);
-  int size = register_size (tdesc, regnum);
-  gdb_assert (size >= offset);
+  gdb::array_view<const gdb_byte> regbuf = register_data (this, regnum);
+  gdb_assert (offset < regbuf.size ());
+  regbuf = regbuf.slice (offset);
 
-  return (memcmp (buf, regbuf + offset, size - offset) == 0);
+  return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
 }
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 4700c03f104e..02791a048fb0 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -50,10 +50,10 @@ struct regcache : public reg_buffer_common
   enum register_status get_register_status (int regnum) const override;
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_supply (int regnum, const void *buf) override;
+  void raw_supply (int regnum, gdb::array_view<const gdb_byte> src) override;
 
   /* See gdbsupport/common-regcache.h.  */
-  void raw_collect (int regnum, void *buf) const override;
+  void raw_collect (int regnum, gdb::array_view<gdb_byte> dst) const override;
 
   /* See gdbsupport/common-regcache.h.  */
   bool raw_compare (int regnum, const void *buf, int offset) const override;
diff --git a/gdbsupport/common-regcache.h b/gdbsupport/common-regcache.h
index 6d98ca8c92ed..c40b92a83ee1 100644
--- a/gdbsupport/common-regcache.h
+++ b/gdbsupport/common-regcache.h
@@ -78,11 +78,41 @@ struct reg_buffer_common
      buffer.  */
   virtual register_status get_register_status (int regnum) const = 0;
 
-  /* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE.  */
-  virtual void raw_supply (int regnum, const void *buf) = 0;
+  /* Supply register REGNUM, whose contents are stored in SRC, to this register
+     buffer.  */
+  virtual void raw_supply (int regnum, gdb::array_view<const gdb_byte> src)
+    = 0;
+
+  void raw_supply (int regnum, const uint64_t *src)
+  {
+    raw_supply (regnum,
+		gdb::make_array_view ((const gdb_byte *) src, sizeof (*src)));
+  }
+
+  void raw_supply (int regnum, const gdb_byte *src)
+  {
+    raw_supply (regnum,
+		gdb::make_array_view (src,
+				      regcache_register_size (this, regnum)));
+  }
 
-  /* Collect register REGNUM from REGCACHE and store its contents in BUF.  */
-  virtual void raw_collect (int regnum, void *buf) const = 0;
+  /* Collect register REGNUM from this register buffer and store its contents in
+     DST.  */
+  virtual void raw_collect (int regnum, gdb::array_view<gdb_byte> dst) const
+    = 0;
+
+  void raw_collect (int regnum, uint64_t *dst) const
+  {
+    raw_collect (regnum,
+		 gdb::make_array_view ((gdb_byte *) dst, sizeof (*dst)));
+  };
+
+  void raw_collect (int regnum, gdb_byte *dst)
+  {
+    raw_collect (regnum,
+		 gdb::make_array_view (dst,
+				       regcache_register_size (this, regnum)));
+  }
 
   /* Compare the contents of the register stored in the regcache (ignoring the
      first OFFSET bytes) to the contents of BUF (without any offset).  Returns
diff --git a/gdbsupport/rsp-low.cc b/gdbsupport/rsp-low.cc
index 3d8c2002956e..632be265c00c 100644
--- a/gdbsupport/rsp-low.cc
+++ b/gdbsupport/rsp-low.cc
@@ -143,6 +143,14 @@ bin2hex (const gdb_byte *bin, char *hex, int count)
 
 /* See rsp-low.h.  */
 
+int
+bin2hex (gdb::array_view<gdb_byte> bin, char *hex)
+{
+  return bin2hex (bin.data (), hex, bin.size ());
+}
+
+/* See rsp-low.h.  */
+
 std::string
 bin2hex (const gdb_byte *bin, int count)
 {
diff --git a/gdbsupport/rsp-low.h b/gdbsupport/rsp-low.h
index 327d5f3a0947..1fc2572a7f5c 100644
--- a/gdbsupport/rsp-low.h
+++ b/gdbsupport/rsp-low.h
@@ -54,6 +54,8 @@ extern std::string hex2str (const char *hex, int count);
 
 extern int bin2hex (const gdb_byte *bin, char *hex, int count);
 
+extern int bin2hex (gdb::array_view<gdb_byte> bin, char *hex);
+
 /* Overloaded version of bin2hex that returns a std::string.  */
 
 extern std::string bin2hex (const gdb_byte *bin, int count);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2023-12-01 16:27 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-08  5:00 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
2023-11-08  5:00 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
2023-11-11 19:29   ` John Baldwin
2023-11-08  5:00 ` [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h Simon Marchi
2023-11-11 19:42   ` John Baldwin
2023-11-08  5:00 ` [PATCH 03/24] gdb: make store_integer take an array_view Simon Marchi
2023-11-08  5:00 ` [PATCH 04/24] gdb: simplify conditions in regcache::{read,write,raw_collect,raw_supply}_part Simon Marchi
2023-11-08  5:00 ` [PATCH 05/24] gdb: change regcache interface to use array_view Simon Marchi
2023-11-13 13:43   ` Andrew Burgess
2023-11-13 14:00     ` Andrew Burgess
2023-11-13 16:47       ` Simon Marchi
2023-11-08  5:00 ` [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes Simon Marchi
2023-11-13 15:00   ` Andrew Burgess
2023-11-13 19:51     ` Simon Marchi
2023-11-08  5:00 ` [PATCH 07/24] gdb: make put_frame_register take an array_view Simon Marchi
2023-11-08  5:00 ` [PATCH 08/24] gdb: change value_of_register and value_of_register_lazy to take the next frame Simon Marchi
2023-11-08  5:00 ` [PATCH 09/24] gdb: remove frame_register Simon Marchi
2023-11-08  5:00 ` [PATCH 10/24] gdb: make put_frame_register take the next frame Simon Marchi
2023-11-08  5:00 ` [PATCH 11/24] gdb: make put_frame_register_bytes " Simon Marchi
2023-11-08  5:00 ` [PATCH 12/24] gdb: make get_frame_register_bytes " Simon Marchi
2023-11-08  5:00 ` [PATCH 13/24] gdb: add value::allocate_register Simon Marchi
2023-11-08  5:00 ` [PATCH 14/24] gdb: read pseudo register through frame Simon Marchi
2023-11-11 20:11   ` John Baldwin
2023-11-08  5:00 ` [PATCH 15/24] gdb: change parameter name in frame_unwind_register_unsigned declaration Simon Marchi
2023-11-08  5:01 ` [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write Simon Marchi
2023-11-14 12:12   ` Andrew Burgess
2023-11-14 15:16     ` Simon Marchi
2023-11-08  5:01 ` [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame Simon Marchi
2023-11-14 12:20   ` Andrew Burgess
2023-11-14 15:20     ` Simon Marchi
2023-11-08  5:01 ` [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write Simon Marchi
2023-11-11 20:16   ` John Baldwin
2023-11-13  2:59     ` Simon Marchi
2023-11-08  5:01 ` [PATCH 19/24] gdb: make aarch64_za_offsets_from_regnum return za_offsets Simon Marchi
2023-11-08  5:01 ` [PATCH 20/24] gdb: add missing raw register read in aarch64_sme_pseudo_register_write Simon Marchi
2023-11-08  5:01 ` [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write Simon Marchi
2023-11-08  5:01 ` [PATCH 22/24] gdb: migrate arm to gdbarch_pseudo_register_read_value Simon Marchi
2023-11-08  5:01 ` [PATCH 23/24] gdb: migrate arm to new gdbarch_pseudo_register_write Simon Marchi
2023-11-08  5:01 ` [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers Simon Marchi
2023-11-08  5:16 ` [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
2023-11-09  3:05   ` Simon Marchi
2023-11-08 11:57 ` Luis Machado
2023-11-08 15:47   ` Simon Marchi
2023-11-08 17:08     ` Luis Machado
2023-11-08 19:34       ` Simon Marchi
2023-11-09 19:04         ` Simon Marchi
2023-11-13 13:10           ` Luis Machado
2023-11-13 15:08             ` Luis Machado
2023-11-11 20:26 ` John Baldwin
2023-11-13  3:03   ` Simon Marchi
2023-12-01 16:27 Simon Marchi
2023-12-01 16:27 ` [PATCH 05/24] gdb: change regcache interface to use array_view Simon Marchi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).