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-12-01 16:27 Simon Marchi
  2023-12-01 16:27 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
                   ` (25 more replies)
  0 siblings, 26 replies; 34+ 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

Hi,

This is version 3 of:

  https://inbox.sourceware.org/gdb-patches/20231124212656.96801-1-simon.marchi@efficios.com/

There are two functional changes in this version:

 - fixed the raw_compare behavior in patch 5 ("gdb: change regcache
   interface to use array_view"), added a selftest that would have
   caught my mistake
 - changed a LONGEST for ULONGEST in patch 3 ("gdb: make store_integer
   take an array_view")

The rest of the changes should all be cosmetic.

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                            | 293 +++++----
 gdb/alpha-tdep.c                              |  11 +-
 gdb/amd64-tdep.c                              |  82 +--
 gdb/arch/arm-get-next-pcs.c                   |   8 +-
 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                                | 563 +++++++++++-------
 gdb/regcache.h                                | 110 +++-
 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        |  88 +++
 .../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          |  79 +++
 .../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                                   | 153 +++++
 gdb/value.h                                   |  64 +-
 gdb/xtensa-tdep.c                             |   3 +-
 gdbserver/linux-arm-low.cc                    |   4 +-
 gdbserver/regcache.cc                         |  66 +-
 gdbserver/regcache.h                          |   4 +-
 gdbsupport/common-regcache.cc                 |   2 +-
 gdbsupport/common-regcache.h                  |  55 +-
 gdbsupport/rsp-low.cc                         |   8 +
 gdbsupport/rsp-low.h                          |   2 +
 79 files changed, 2329 insertions(+), 1141 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: 9d8fc40eb0e611162844eb7b89f1c76875153fbe
-- 
2.43.0


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

* [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h Simon Marchi
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-by: John Baldwin <jhb@FreeBSD.org>
---
 gdb/i386-tdep.c | 23 -----------------------
 1 file changed, 23 deletions(-)

diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index e00c3bd9d568..f356995520fe 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.43.0


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

* [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
  2023-12-01 16:27 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 03/24] gdb: make store_integer take an array_view Simon Marchi
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 34+ 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:

 - remove unrelated whitespace change
 - formatting fixes
 - use regcache local var instead of self->regcache

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
Reviewed-by: John Baldwin <jhb@FreeBSD.org>
---
 gdb/arch/arm-get-next-pcs.c   |  8 ++++----
 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         | 17 +++++++++++------
 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..528753aff2f7 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;
@@ -268,12 +268,12 @@ thumb_get_next_pcs_raw (struct arm_get_next_pcs *self)
 {
   int byte_order = self->byte_order;
   int byte_order_for_code = self->byte_order_for_code;
+  reg_buffer_common *regcache = self->regcache;
   CORE_ADDR pc = regcache_read_pc (self->regcache);
   unsigned long pc_val = ((unsigned long) pc) + 4;	/* PC after prefetch */
   unsigned short inst1;
   CORE_ADDR nextpc = pc + 2;		/* Default is next instruction.  */
   ULONGEST status, itstate;
-  struct regcache *regcache = self->regcache;
   std::vector<CORE_ADDR> next_pcs;
 
   nextpc = MAKE_THUMB_ADDR (nextpc);
@@ -653,8 +653,8 @@ 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;
-  CORE_ADDR pc = regcache_read_pc (self->regcache);
+  reg_buffer_common *regcache = self->regcache;
+  CORE_ADDR pc = regcache_read_pc (regcache);
   std::vector<CORE_ADDR> next_pcs;
 
   pc_val = (unsigned long) pc;
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 6fc4c0e11cd6..5c41b26483bb 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 8117d35a4d37..05538251612b 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -903,8 +903,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)
@@ -914,7 +916,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 = 
@@ -937,8 +939,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 23fecf7cf666..83fce2abd508 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 89bf28b5133f..c4f4cbd77115 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 e46a0b58f505..6c986b740dfd 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)
@@ -417,7 +418,7 @@ get_thread_regcache (thread_info *thread)
 
 /* 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
@@ -630,11 +631,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
@@ -1314,8 +1316,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;
@@ -1342,7 +1345,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 2e75a948a198..823ea1f015d3 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,10 +440,11 @@ 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);
 
@@ -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.43.0


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

* [PATCH 03/24] gdb: make store_integer take an array_view
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
  2023-12-01 16:27 ` [PATCH 01/24] gdb: don't handle i386 k registers as pseudo registers Simon Marchi
  2023-12-01 16:27 ` [PATCH 02/24] gdb: use reg_buffer_common throughout gdbsupport/common-regcache.h Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 04/24] gdb: simplify conditions in regcache::{read,write,raw_collect,raw_supply}_part Simon Marchi
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 34+ 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 type of store_unsigned_integer parameter, LONGEST -> ULONGEST

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 bcce4f4c3e43..e20143b8146a 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -495,21 +495,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,
+			ULONGEST 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 952ec20c0b73..c7a681f8a890 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -159,12 +159,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.  */
@@ -187,13 +187,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.43.0


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

* [PATCH 04/24] gdb: simplify conditions in regcache::{read,write,raw_collect,raw_supply}_part
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (2 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 03/24] gdb: make store_integer take an array_view 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
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/regcache.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/gdb/regcache.c b/gdb/regcache.c
index 6c986b740dfd..7eb54d27b135 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -873,16 +873,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);
@@ -910,16 +910,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);
@@ -940,16 +940,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);
@@ -979,16 +979,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.43.0


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

* [PATCH 05/24] gdb: change regcache interface to use array_view
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (3 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 04/24] gdb: simplify conditions in regcache::{read,write,raw_collect,raw_supply}_part Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes Simon Marchi
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 34+ 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] 34+ messages in thread

* [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (4 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 05/24] gdb: change regcache interface to use array_view Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 07/24] gdb: make put_frame_register take an array_view Simon Marchi
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 34+ 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, Andrew Burgess

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
Approved-By: Andrew Burgess <aburgess@redhat.com>
---
 gdb/frame.c | 63 +++++++++++++++++++----------------------------------
 1 file changed, 23 insertions(+), 40 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index 529453efa158..08ce21705432 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1483,9 +1483,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))
@@ -1496,9 +1493,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);
 
@@ -1507,20 +1504,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))
 	{
@@ -1528,8 +1520,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;
 	}
@@ -1548,13 +1540,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++;
     }
@@ -1579,36 +1570,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.43.0


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

* [PATCH 07/24] gdb: make put_frame_register take an array_view
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (5 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 06/24] gdb: fix bugs in {get,put}_frame_register_bytes Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 08/24] gdb: change value_of_register and value_of_register_lazy to take the next frame Simon Marchi
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/alpha-tdep.c  | 12 ++++++------
 gdb/frame.c       | 11 +++++++----
 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, 35 insertions(+), 24 deletions(-)

diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c
index 0d3a49550530..eaf2a7e97949 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 08ce21705432..9312d1b40168 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1424,7 +1424,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;
@@ -1432,6 +1432,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);
@@ -1441,7 +1444,7 @@ 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:
@@ -1577,7 +1580,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
@@ -1587,7 +1590,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 19bf81766822..4117e169379a 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -731,7 +731,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 f356995520fe..3afa902767e5 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 b9c9e476e936..b39ca2adef55 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 6d537c4c8d36..f778c092537b 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 0b761d019f2a..2e8043af0e06 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 c7157c19e7be..16edfdb0f1b8 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 16f3da9c0117..cbbaf6d83a19 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -2741,9 +2741,10 @@ rs6000_value_to_register (frame_info_ptr frame,
      fpr to vsr.  */
   regnum = ieee_128_float_regnum_adjust (gdbarch, type, regnum);
 
-  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);
 }
 
 static struct value *
-- 
2.43.0


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

* [PATCH 08/24] gdb: change value_of_register and value_of_register_lazy to take the next frame
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (6 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 07/24] gdb: make put_frame_register take an array_view Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 09/24] gdb: remove frame_register Simon Marchi
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 4a7ce68e85a9..b0aee191d71f 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 83fce2abd508..1ee8bdf46159 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 8192aeba3640..e075cc3138d6 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 c7a681f8a890..e5513d00dea4 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -243,42 +243,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
@@ -291,7 +281,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 ec818bc6936f..20e5fde55ee1 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2319,9 +2319,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 f7d08c60a582..12865b99e13f 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 487b0a920254..21f2c72cfb90 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 16edfdb0f1b8..adaa091bda14 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 0a7e10f09ff2..f38bf5d8237f 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 2b4b9e2a02ac..8110a8deedbf 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 6d91e8eee7a9..3f9b35b589bd 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.43.0


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

* [PATCH 09/24] gdb: remove frame_register
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (7 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 08/24] gdb: change value_of_register and value_of_register_lazy to take the next frame Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 10/24] gdb: make put_frame_register take the next frame Simon Marchi
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/frame.c | 39 ++++++++-------------------------------
 1 file changed, 8 insertions(+), 31 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index 9312d1b40168..61d6822ecc1a 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1214,31 +1214,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)
 {
@@ -1436,8 +1411,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)
@@ -1473,8 +1448,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;
 }
@@ -1523,8 +1499,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.43.0


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

* [PATCH 10/24] gdb: make put_frame_register take the next frame
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (8 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 09/24] gdb: remove frame_register Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 11/24] gdb: make put_frame_register_bytes " Simon Marchi
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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

Change-Id: I062fd4663b8f54f0fc7bbf39c860b7341363821b
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 eaf2a7e97949..799441758103 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 61d6822ecc1a..44c4906e9cc1 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1398,10 +1398,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;
@@ -1411,8 +1411,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)
@@ -1557,7 +1557,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
@@ -1567,7 +1568,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 4117e169379a..1d870d619ca5 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -727,10 +727,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 3afa902767e5..f188f83de6f3 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 b39ca2adef55..77dc5a008d5f 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 f778c092537b..3bede2644c03 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 2e8043af0e06..8bbfa4bd0b10 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 adaa091bda14..dbdf8a668b8f 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 cbbaf6d83a19..c43039302bc3 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -2744,7 +2744,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);
 }
 
 static struct value *
-- 
2.43.0


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

* [PATCH 11/24] gdb: make put_frame_register_bytes take the next frame
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (9 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 10/24] gdb: make put_frame_register take the next frame Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 12/24] gdb: make get_frame_register_bytes " Simon Marchi
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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

Change-Id: I27bcb26573686d99b231230823cff8db6405a788
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 44c4906e9cc1..982aaa6d6753 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1537,11 +1537,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))
@@ -1557,19 +1557,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 1d870d619ca5..5ce3a1b285aa 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -743,8 +743,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 dbdf8a668b8f..9c0cfede492a 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 6521580a1e83..8e1849837cf1 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.43.0


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

* [PATCH 12/24] gdb: make get_frame_register_bytes take the next frame
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (10 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 11/24] gdb: make put_frame_register_bytes " Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-22 13:33   ` Kévin Le Gouguec
  2023-12-01 16:27 ` [PATCH 13/24] gdb: add value::allocate_register Simon Marchi
                   ` (13 subsequent siblings)
  25 siblings, 1 reply; 34+ 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

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

Change-Id: Ie8f35042bfa6e93565fcefaee71b6b3903f0fe9f
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 982aaa6d6753..d260e8c28f3f 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1456,12 +1456,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))
@@ -1499,17 +1498,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 5ce3a1b285aa..62f53ece9eaa 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -733,11 +733,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 f188f83de6f3..59d77d1d574d 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 77dc5a008d5f..ffd8303746e5 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 3bede2644c03..b009b46ef6df 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 8bbfa4bd0b10..f9c734cf3808 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 9c0cfede492a..fc3193ea27a5 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 c43039302bc3..643997e5bd7d 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -2713,11 +2713,11 @@ rs6000_register_to_value (frame_info_ptr frame,
      fpr to vsr.  */
   regnum = ieee_128_float_regnum_adjust (gdbarch, type, regnum);
 
-  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 (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 8e1849837cf1..dbf7a7552b10 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.43.0


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

* [PATCH 13/24] gdb: add value::allocate_register
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (11 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 12/24] gdb: make get_frame_register_bytes " Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 14/24] gdb: read pseudo register through frame Simon Marchi
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 19ba353a335c..9b3fd4f060c7 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -775,11 +775,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 1a3985582ba5..99af27b98700 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 3f9b35b589bd..2f3b41e26ea4 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.43.0


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

* [PATCH 14/24] gdb: read pseudo register through frame
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (12 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 13/24] gdb: add value::allocate_register Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 15/24] gdb: change parameter name in frame_unwind_register_unsigned declaration Simon Marchi
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-by: John Baldwin <jhb@FreeBSD.org>
---
 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 b0aee191d71f..d901b69ce11d 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)];
-  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 d72dd0ad971d..d3d1ecdf1f5b 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 d260e8c28f3f..a9cad1dca8fb 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1257,9 +1257,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_thread_regcache (inferior_thread ()), 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 9f468bd1f618..c70bfc97634a 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 694ac3660235..c597b38ad5a9 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 59d77d1d574d..be1aa83bbbdb 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 9b3fd4f060c7..9575988760ba 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -735,8 +735,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);
@@ -788,8 +789,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
@@ -1952,10 +1954,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,
+				  std::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);
 
   /* raw_read calls target_fetch_registers.  */
@@ -2053,6 +2063,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
@@ -2067,7 +2079,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,
+				  std::make_unique<readwrite_regcache> (
+				    &ctx.mock_inferior, gdbarch)))
+	 ->second;
+
   readwrite.set_ptid (ctx.mock_ptid);
   const int num_regs = gdbarch_num_cooked_regs (gdbarch);
 
@@ -2148,6 +2170,8 @@ cooked_write_test (struct gdbarch *gdbarch)
       SELF_CHECK (readwrite.cooked_read (regnum, buf) == 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 99af27b98700..9d826d4fdbed 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 2f3b41e26ea4..935d9ebfd8c9 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1650,4 +1650,30 @@ struct scoped_array_length_limiting
   std::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.43.0


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

* [PATCH 15/24] gdb: change parameter name in frame_unwind_register_unsigned declaration
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (13 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 14/24] gdb: read pseudo register through frame Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write Simon Marchi
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

For consistency with the declarations around.

Change-Id: I398266a61eae6e93fb7e306923009da9dd7f8fc4
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/frame.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/frame.h b/gdb/frame.h
index 62f53ece9eaa..ec9b42115406 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -714,7 +714,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.43.0


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

* [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (14 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 15/24] gdb: change parameter name in frame_unwind_register_unsigned declaration Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame Simon Marchi
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 d901b69ce11d..5b2d87be6bfc 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 1ee8bdf46159..85fd37ba73a0 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 be0601056a80..c0b954c4acc9 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 6528cf3e405e..7944228a6021 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 c70bfc97634a..7b34dce3cfc8 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 c597b38ad5a9..ee3fd2b69454 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 be1aa83bbbdb..228b390245f6 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8678,7 +8678,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 b009b46ef6df..699959a8bf0b 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 a0f1779c41b0..3efeb0032e76 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 6210b929f9d0..ad630d60bf1e 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 fc3193ea27a5..8749c7c9eeaf 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 c03e12c9486f..cc2ac4d2621f 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 9575988760ba..0e3316aaa096 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -912,8 +912,8 @@ regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src)
   if (regnum < num_raw_registers ())
     raw_write (regnum, src);
   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 8110a8deedbf..4c129a66652f 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 643997e5bd7d..a7e0bf5305b5 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -8353,8 +8353,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 5b58cb382e30..fcba7a1a5608 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 9e7bfde193fc..6e426c457894 100644
--- a/gdb/xtensa-tdep.c
+++ b/gdb/xtensa-tdep.c
@@ -3176,7 +3176,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.43.0


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

* [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (15 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 16/24] gdb: rename gdbarch_pseudo_register_write to gdbarch_deprecated_pseudo_register_write Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write Simon Marchi
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 34+ 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 v2:

 - Add missing comments, fix typos

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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               | 68 +++++++++++++++++++++++++++++++++++++++
 gdb/value.h               | 22 +++++++++++++
 7 files changed, 167 insertions(+), 2 deletions(-)

diff --git a/gdb/frame.c b/gdb/frame.c
index a9cad1dca8fb..5f4c8c621a04 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1453,7 +1453,14 @@ put_frame_register (frame_info_ptr next_frame, int regnum,
 	break;
       }
     case lval_register:
-      get_thread_regcache (inferior_thread ())->cooked_write (realnum, buf);
+      /* 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_thread_regcache (inferior_thread ())->cooked_write (realnum, buf);
+      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 7b34dce3cfc8..80d40136c379 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 ee3fd2b69454..4352f7066512 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 0e3316aaa096..6140a05f02b2 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -911,6 +911,10 @@ regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src)
 
   if (regnum < num_raw_registers ())
     raw_write (regnum, src);
+  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 9d826d4fdbed..f61949bf9422 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -4057,6 +4057,23 @@ 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 (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
+     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 +4097,29 @@ pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
   return pseudo_reg_val;
 }
 
+/* See value.h. */
+
+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 +4151,34 @@ pseudo_from_concat_raw (frame_info_ptr next_frame, int pseudo_reg_num,
   return pseudo_reg_val;
 }
 
+/* See value.h. */
+
+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 935d9ebfd8c9..d7bda1e8d2c9 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 next_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 next_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 three raw
+   registers RAW_REG_1_NUM, RAW_REG_2_NUM and RAW_REG_3_NUM.  */
+
+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);
+
 #endif /* !defined (VALUE_H) */
-- 
2.43.0


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

* [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (16 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 17/24] gdb: add gdbarch_pseudo_register_write that takes a frame Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 19/24] gdb: make aarch64_za_offsets_from_regnum return za_offsets Simon Marchi
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 34+ 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 v2:

 - Move some changes from here to the previous patch (where they belong)

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/amd64-tdep.c |  39 +++----------
 gdb/i386-tdep.c  | 146 +++++++++++++++++------------------------------
 gdb/i386-tdep.h  |   6 +-
 3 files changed, 61 insertions(+), 130 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 228b390245f6..d5602917aa8c 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"));
@@ -8678,8 +8635,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,
-- 
2.43.0


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

* [PATCH 19/24] gdb: make aarch64_za_offsets_from_regnum return za_offsets
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (17 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 18/24] gdb: migrate i386 and amd64 to the new gdbarch_pseudo_register_write Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 20/24] gdb: add missing raw register read in aarch64_sme_pseudo_register_write Simon Marchi
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Approved-by: Luis Machado <luis.machado@arm.com>
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 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 5b2d87be6bfc..5c3b824ed0ad 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.43.0


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

* [PATCH 20/24] gdb: add missing raw register read in aarch64_sme_pseudo_register_write
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (18 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 19/24] gdb: make aarch64_za_offsets_from_regnum return za_offsets Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write Simon Marchi
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
Approved-by: Luis Machado <luis.machado@arm.com>
---
 gdb/aarch64-tdep.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 5c3b824ed0ad..e3658c2c8e10 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);
 
   /* Copy the requested data.  */
   for (int chunks = 0; chunks < offsets.chunks; chunks++)
-- 
2.43.0


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

* [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (19 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 20/24] gdb: add missing raw register read in aarch64_sme_pseudo_register_write Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-14 14:53   ` Luis Machado
  2023-12-01 16:27 ` [PATCH 22/24] gdb: migrate arm to gdbarch_pseudo_register_read_value Simon Marchi
                   ` (4 subsequent siblings)
  25 siblings, 1 reply; 34+ 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 v2:

 - Allocate enough space for an SVE register in aarch64_pseudo_write_1
   (i.e. keep the logic that's there before the patch for the local
   buffer size)

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

Change-Id: Ic012a0b95ae728d45a7121f77a79d604c23a849e
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
---
 gdb/aarch64-tdep.c | 128 +++++++++++++++++++++++++--------------------
 1 file changed, 70 insertions(+), 58 deletions(-)

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index e3658c2c8e10..a1eab01abb8c 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -3296,32 +3296,35 @@ 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;
+  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
 
   /* Enough space for a full vector register.  */
-  gdb_byte reg_buf[register_size (gdbarch, AARCH64_V0_REGNUM)];
+  int raw_reg_size = register_size (gdbarch, raw_regnum);
+  gdb_byte raw_buf[raw_reg_size];
   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 (reg_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));
+  memset (raw_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));
 
-  memcpy (reg_buf, buf, regsize);
-  regcache->raw_write (v_regnum, reg_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, raw_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 +3338,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);
+  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 +3380,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 +4495,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.43.0


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

* [PATCH 22/24] gdb: migrate arm to gdbarch_pseudo_register_read_value
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (20 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 23/24] gdb: migrate arm to new gdbarch_pseudo_register_write Simon Marchi
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
Approved-by: Luis Machado <luis.machado@arm.com>
---
 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 85fd37ba73a0..36ca867da5c2 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.43.0


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

* [PATCH 23/24] gdb: migrate arm to new gdbarch_pseudo_register_write
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (21 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 22/24] gdb: migrate arm to gdbarch_pseudo_register_read_value Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-01 16:27 ` [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers Simon Marchi
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 34+ 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

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

Change-Id: Icb2a649ab6394817844230e9e94c3d0564d2f765
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
Approved-by: Luis Machado <luis.machado@arm.com>
---
 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 36ca867da5c2..0e333290e1e5 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.43.0


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

* [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (22 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 23/24] gdb: migrate arm to new gdbarch_pseudo_register_write Simon Marchi
@ 2023-12-01 16:27 ` Simon Marchi
  2023-12-14 14:54   ` Luis Machado
  2023-12-01 16:56 ` [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
  2023-12-14 14:51 ` Luis Machado
  25 siblings, 1 reply; 34+ 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

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

New in v2:

 - Switched to using x19/w19 in the aarch64 test

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
Reviewed-By: John Baldwin <jhb@FreeBSD.org>
Reviewed-by: Luis Machado <luis.machado@arm.com>
---
 .../gdb.arch/aarch64-pseudo-unwind-asm.S      | 82 +++++++++++++++++
 .../gdb.arch/aarch64-pseudo-unwind.c          | 33 +++++++
 .../gdb.arch/aarch64-pseudo-unwind.exp        | 88 ++++++++++++++++++
 .../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          | 79 ++++++++++++++++
 .../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, 796 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..575ed75ae9f5
--- /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 x19 value on the stack.  */
+.cfi_offset 19, -32
+	str x19, [sp, -16]!
+
+	/* Put our own x19 value.  */
+	adr x0, value_callee
+	ldr x19, [x0]
+
+break_here_asm:
+
+	/* Restore caller's x19 value.  */
+	ldr x19, [sp], 16
+
+	/* Standard epilogue:
+	    - pop fp (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 x19 value.  */
+	adr x0, value_caller
+	ldr x19, [x0]
+
+	/* Call callee.  */
+	bl callee
+
+	/* Store our x19 value in x0 to return it.  */
+	mov x0, x19
+
+	/* 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..62b3f8c4337c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-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 AArch64.  We
+# use the raw register x19 which is 64 bits long and pseudo register w19, which
+# is the bottom half of x19.
+
+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 x19/w19 in the inner frame (callee).
+with_test_prefix "callee, before change" {
+    gdb_test "p/x \$x19" " = 0x2021222324252627"
+    gdb_test "p/x \$w19" " = 0x24252627"
+}
+
+# Verify that we can change the value of the pseudo register (w19) in the inner
+# frame (callee).
+gdb_test_no_output "set \$w19 = 0x34353637"
+
+# Verify the value of x19/w19 in the inner frame (callee) after the change.
+with_test_prefix "callee, after change" {
+    gdb_test "p/x \$x19" " = 0x34353637"
+    gdb_test "p/x \$w19" " = 0x34353637"
+}
+
+# Go up one frame (to caller) and do the same.
+gdb_test "up"
+
+# Verify the value of x19/w19 in the outer frame (caller).
+with_test_prefix "caller, before change" {
+    gdb_test "p/x \$x19" " = 0x1011121314151617"
+    gdb_test "p/x \$w19" " = 0x14151617"
+}
+
+# Verify that we can change the value of the pseudo register (w19) in the outer
+# frame (caller).
+gdb_test_no_output "set \$w19 = 0x44454647"
+
+# Verify the value of x19/w19 in the outer frame (caller) after the change.
+with_test_prefix "caller, after change" {
+    gdb_test "p/x \$x19" " = 0x44454647"
+    gdb_test "p/x \$w19" " = 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 \$x19" " = 0x34353637"
+    gdb_test "p/x \$w19" " = 0x34353637"
+}
+
+# Verify that the value of the saved x19 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..0e5ad1b9bee1
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
@@ -0,0 +1,79 @@
+/* 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.43.0


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

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (23 preceding siblings ...)
  2023-12-01 16:27 ` [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers Simon Marchi
@ 2023-12-01 16:56 ` Simon Marchi
  2023-12-14 14:51 ` Luis Machado
  25 siblings, 0 replies; 34+ messages in thread
From: Simon Marchi @ 2023-12-01 16:56 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, John Baldwin, Aktemur, Tankut Baris

On 12/1/23 11:27, Simon Marchi wrote:
> Hi,
> 
> This is version 3 of:
> 
>   https://inbox.sourceware.org/gdb-patches/20231124212656.96801-1-simon.marchi@efficios.com/
> 
> There are two functional changes in this version:
> 
>  - fixed the raw_compare behavior in patch 5 ("gdb: change regcache
>    interface to use array_view"), added a selftest that would have
>    caught my mistake
>  - changed a LONGEST for ULONGEST in patch 3 ("gdb: make store_integer
>    take an array_view")
> 
> The rest of the changes should all be cosmetic.
> 

Sorry, I messed up putting "v3" in the subjects.  I sent this by doing a
"git format-patch" followed by a "git send-email", but I only passed
"-v3" to "git send-email".  I should have passed it to "git
format-patch".

Simon

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

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-12-01 16:27 [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
                   ` (24 preceding siblings ...)
  2023-12-01 16:56 ` [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames Simon Marchi
@ 2023-12-14 14:51 ` Luis Machado
  2023-12-14 16:20   ` Simon Marchi
  25 siblings, 1 reply; 34+ messages in thread
From: Luis Machado @ 2023-12-14 14:51 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: John Baldwin, Aktemur, Tankut Baris

Hi Simon,

I re-tested this on aarch64 and arm, and the result with the changes are still clean.

On 12/1/23 16:27, Simon Marchi wrote:
> Hi,
> 
> This is version 3 of:
> 
>   https://inbox.sourceware.org/gdb-patches/20231124212656.96801-1-simon.marchi@efficios.com/
> 
> There are two functional changes in this version:
> 
>  - fixed the raw_compare behavior in patch 5 ("gdb: change regcache
>    interface to use array_view"), added a selftest that would have
>    caught my mistake
>  - changed a LONGEST for ULONGEST in patch 3 ("gdb: make store_integer
>    take an array_view")
> 
> The rest of the changes should all be cosmetic.
> 
> 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                            | 293 +++++----
>  gdb/alpha-tdep.c                              |  11 +-
>  gdb/amd64-tdep.c                              |  82 +--
>  gdb/arch/arm-get-next-pcs.c                   |   8 +-
>  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                                | 563 +++++++++++-------
>  gdb/regcache.h                                | 110 +++-
>  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        |  88 +++
>  .../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          |  79 +++
>  .../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                                   | 153 +++++
>  gdb/value.h                                   |  64 +-
>  gdb/xtensa-tdep.c                             |   3 +-
>  gdbserver/linux-arm-low.cc                    |   4 +-
>  gdbserver/regcache.cc                         |  66 +-
>  gdbserver/regcache.h                          |   4 +-
>  gdbsupport/common-regcache.cc                 |   2 +-
>  gdbsupport/common-regcache.h                  |  55 +-
>  gdbsupport/rsp-low.cc                         |   8 +
>  gdbsupport/rsp-low.h                          |   2 +
>  79 files changed, 2329 insertions(+), 1141 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: 9d8fc40eb0e611162844eb7b89f1c76875153fbe

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

* Re: [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write
  2023-12-01 16:27 ` [PATCH 21/24] gdb: migrate aarch64 to new gdbarch_pseudo_register_write Simon Marchi
@ 2023-12-14 14:53   ` Luis Machado
  0 siblings, 0 replies; 34+ messages in thread
From: Luis Machado @ 2023-12-14 14:53 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: John Baldwin, Aktemur, Tankut Baris

On 12/1/23 16:27, Simon Marchi wrote:
> New in v2:
> 
>  - Allocate enough space for an SVE register in aarch64_pseudo_write_1
>    (i.e. keep the logic that's there before the patch for the local
>    buffer size)
> 
> Make aarch64 use the new gdbarch_pseudo_register_write.  This fixes
> writing pseudo registers to non-current frames on this architecture.
> 
> Change-Id: Ic012a0b95ae728d45a7121f77a79d604c23a849e
> Reviewed-By: John Baldwin <jhb@FreeBSD.org>
> ---
>  gdb/aarch64-tdep.c | 128 +++++++++++++++++++++++++--------------------
>  1 file changed, 70 insertions(+), 58 deletions(-)
> 
> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
> index e3658c2c8e10..a1eab01abb8c 100644
> --- a/gdb/aarch64-tdep.c
> +++ b/gdb/aarch64-tdep.c
> @@ -3296,32 +3296,35 @@ 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;
> +  unsigned raw_regnum = AARCH64_V0_REGNUM + regnum_offset;
>  
>    /* Enough space for a full vector register.  */
> -  gdb_byte reg_buf[register_size (gdbarch, AARCH64_V0_REGNUM)];
> +  int raw_reg_size = register_size (gdbarch, raw_regnum);
> +  gdb_byte raw_buf[raw_reg_size];
>    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 (reg_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));
> +  memset (raw_buf, 0, register_size (gdbarch, AARCH64_V0_REGNUM));
>  
> -  memcpy (reg_buf, buf, regsize);
> -  regcache->raw_write (v_regnum, reg_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, raw_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 +3338,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);
> +  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 +3380,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 +4495,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,

Approved-By: Luis Machado <luis.machado@arm.com>
Tested-By: Luis Machado <luis.machado@arm.com>

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

* Re: [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers
  2023-12-01 16:27 ` [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers Simon Marchi
@ 2023-12-14 14:54   ` Luis Machado
  0 siblings, 0 replies; 34+ messages in thread
From: Luis Machado @ 2023-12-14 14:54 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches
  Cc: John Baldwin, Aktemur, Tankut Baris, Simon Marchi

On 12/1/23 16:27, Simon Marchi wrote:
> From: Simon Marchi <simon.marchi@polymtl.ca>
> 
> New in v2:
> 
>  - Switched to using x19/w19 in the aarch64 test
> 
> 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
> Reviewed-By: John Baldwin <jhb@FreeBSD.org>
> Reviewed-by: Luis Machado <luis.machado@arm.com>
> ---
>  .../gdb.arch/aarch64-pseudo-unwind-asm.S      | 82 +++++++++++++++++
>  .../gdb.arch/aarch64-pseudo-unwind.c          | 33 +++++++
>  .../gdb.arch/aarch64-pseudo-unwind.exp        | 88 ++++++++++++++++++
>  .../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          | 79 ++++++++++++++++
>  .../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, 796 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..575ed75ae9f5
> --- /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 x19 value on the stack.  */
> +.cfi_offset 19, -32
> +	str x19, [sp, -16]!
> +
> +	/* Put our own x19 value.  */
> +	adr x0, value_callee
> +	ldr x19, [x0]
> +
> +break_here_asm:
> +
> +	/* Restore caller's x19 value.  */
> +	ldr x19, [sp], 16
> +
> +	/* Standard epilogue:
> +	    - pop fp (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 x19 value.  */
> +	adr x0, value_caller
> +	ldr x19, [x0]
> +
> +	/* Call callee.  */
> +	bl callee
> +
> +	/* Store our x19 value in x0 to return it.  */
> +	mov x0, x19
> +
> +	/* 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..62b3f8c4337c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/aarch64-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 AArch64.  We
> +# use the raw register x19 which is 64 bits long and pseudo register w19, which
> +# is the bottom half of x19.
> +
> +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 x19/w19 in the inner frame (callee).
> +with_test_prefix "callee, before change" {
> +    gdb_test "p/x \$x19" " = 0x2021222324252627"
> +    gdb_test "p/x \$w19" " = 0x24252627"
> +}
> +
> +# Verify that we can change the value of the pseudo register (w19) in the inner
> +# frame (callee).
> +gdb_test_no_output "set \$w19 = 0x34353637"
> +
> +# Verify the value of x19/w19 in the inner frame (callee) after the change.
> +with_test_prefix "callee, after change" {
> +    gdb_test "p/x \$x19" " = 0x34353637"
> +    gdb_test "p/x \$w19" " = 0x34353637"
> +}
> +
> +# Go up one frame (to caller) and do the same.
> +gdb_test "up"
> +
> +# Verify the value of x19/w19 in the outer frame (caller).
> +with_test_prefix "caller, before change" {
> +    gdb_test "p/x \$x19" " = 0x1011121314151617"
> +    gdb_test "p/x \$w19" " = 0x14151617"
> +}
> +
> +# Verify that we can change the value of the pseudo register (w19) in the outer
> +# frame (caller).
> +gdb_test_no_output "set \$w19 = 0x44454647"
> +
> +# Verify the value of x19/w19 in the outer frame (caller) after the change.
> +with_test_prefix "caller, after change" {
> +    gdb_test "p/x \$x19" " = 0x44454647"
> +    gdb_test "p/x \$w19" " = 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 \$x19" " = 0x34353637"
> +    gdb_test "p/x \$w19" " = 0x34353637"
> +}
> +
> +# Verify that the value of the saved x19 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..0e5ad1b9bee1
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/arm-pseudo-unwind-asm.S
> @@ -0,0 +1,79 @@
> +/* 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"

Approved-By: Luis Machado <luis.machado@arm.com> (aarch64/arm)
Tested-By: Luis Machado <luis.machado@arm.com> (aarch64/arm)

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

* Re: [PATCH 00/24] Fix reading and writing pseudo registers in non-current frames
  2023-12-14 14:51 ` Luis Machado
@ 2023-12-14 16:20   ` Simon Marchi
  0 siblings, 0 replies; 34+ messages in thread
From: Simon Marchi @ 2023-12-14 16:20 UTC (permalink / raw)
  To: Luis Machado, Simon Marchi, gdb-patches
  Cc: John Baldwin, Aktemur, Tankut Baris

On 12/14/23 09:51, Luis Machado wrote:
> Hi Simon,
> 
> I re-tested this on aarch64 and arm, and the result with the changes are still clean.

Thanks a lot for testing, I pushed the series.

Simon

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

* Re: [PATCH 12/24] gdb: make get_frame_register_bytes take the next frame
  2023-12-01 16:27 ` [PATCH 12/24] gdb: make get_frame_register_bytes " Simon Marchi
@ 2023-12-22 13:33   ` Kévin Le Gouguec
  2023-12-22 15:31     ` Simon Marchi
  0 siblings, 1 reply; 34+ messages in thread
From: Kévin Le Gouguec @ 2023-12-22 13:33 UTC (permalink / raw)
  To: Simon Marchi
  Cc: gdb-patches, Luis Machado, John Baldwin, Aktemur, Tankut Baris

[-- Attachment #1: Type: text/plain, Size: 2990 bytes --]

Hey Simon!

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

> Similar to the previous patches, change get_frame_register_bytes to take
> the "next frame" instead of "this frame".
>
> Change-Id: Ie8f35042bfa6e93565fcefaee71b6b3903f0fe9f
> Reviewed-By: John Baldwin <jhb@FreeBSD.org>
> ---
>  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/rs6000-tdep.c b/gdb/rs6000-tdep.c
> index c43039302bc3..643997e5bd7d 100644
> --- a/gdb/rs6000-tdep.c
> +++ b/gdb/rs6000-tdep.c
> @@ -2713,11 +2713,11 @@ rs6000_register_to_value (frame_info_ptr frame,
>       fpr to vsr.  */
>    regnum = ieee_128_float_regnum_adjust (gdbarch, type, regnum);
>  
> -  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 (frame, regnum, 0, from_view, optimizedp,
                                    ^^^^^

Should this have been changed to 'next_frame', as the commit message
suggests, and as was done in the other *-tdep.c?

Asking because I'm observing a regression on ppc-linux in our internal
testsuite.  I've reproduced that regression with the O2_float_param
testcase:

  cd gdb/testsuite/gdb.ada/O2_float_param
  powerpc-linux-gnu-gnatmake foo.adb \
      -cargs -g -O2 -bargs -static -largs -static
  scp foo $TARGET:
  […gdb…] foo                                   \
      -batch                                    \
      -ex 'tbreak callee.increment'             \
      -ex 'target remote | ssh gdbserver - foo' \
      -ex continue

Expected:

  > Temporary breakpoint 1, callee.increment (val=val@entry=99.0, msg=...) at callee.adb:19

Currently, since 2023-12-14 "gdb: make get_frame_register_bytes take the
next frame" (9fc79b42369):

  > Temporary breakpoint 1, callee.increment (val=<optimized out>, val@entry=0.0, msg=...) at callee.adb:19

Applying the attached patch gets us the expected output back, and does
not introduce further regressions that I could find.  I've tried it on
top of both master and your recently submitted "register value cleanups"
series, <20231221191716.257256-1-simon.marchi@efficios.com>.

Let me know if I've missed something!

> +				 unavailablep))
>      return 0;
>  
>    target_float_convert (from, builtin_type (gdbarch)->builtin_double,


[-- Attachment #2: 0001-gdb-fix-refactoring-hiccup-in-rs6000_register_to_val.patch --]
[-- Type: text/x-diff, Size: 1584 bytes --]

From 0fae84368f22250cb672c467e56652103fbb8c49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?K=C3=A9vin=20Le=20Gouguec?= <legouguec@adacore.com>
Date: Fri, 22 Dec 2023 14:06:15 +0100
Subject: [PATCH] gdb: fix refactoring hiccup in rs6000_register_to_value
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In 2023-12-14 "gdb: make get_frame_register_bytes take the next frame"
(9fc79b42369), *_register_to_value functions were made to (a) call
get_next_frame_sentinel_okay (frame) (b) pass that next frame to
get_frame_register_bytes.

Step (b) was omitted for rs6000-tdep.c; this manifests as a regression on
PPC platforms for e.g. O2_float_param: instead of seeing…

  Temporary breakpoint 1, callee.increment (val=val@entry=99.0, msg=...) at callee.adb:19

… we get "optimized_out" for val.  Passing next_frame to
get_frame_register_bytes fixes the issue.
---
 gdb/rs6000-tdep.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 1c9bb3e5f04..f043be08355 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -2716,7 +2716,7 @@ rs6000_register_to_value (frame_info_ptr frame,
   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 (frame, regnum, 0, from_view, optimizedp,
+  if (!get_frame_register_bytes (next_frame, regnum, 0, from_view, optimizedp,
 				 unavailablep))
     return 0;
 
-- 
2.34.1


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

* Re: [PATCH 12/24] gdb: make get_frame_register_bytes take the next frame
  2023-12-22 13:33   ` Kévin Le Gouguec
@ 2023-12-22 15:31     ` Simon Marchi
  2023-12-22 16:22       ` Kévin Le Gouguec
  0 siblings, 1 reply; 34+ messages in thread
From: Simon Marchi @ 2023-12-22 15:31 UTC (permalink / raw)
  To: Kévin Le Gouguec
  Cc: gdb-patches, Luis Machado, John Baldwin, Aktemur, Tankut Baris


Hi Kevin,

> Should this have been changed to 'next_frame', as the commit message
> suggests, and as was done in the other *-tdep.c?

Yes, you're right.

> 
> Asking because I'm observing a regression on ppc-linux in our internal
> testsuite.  I've reproduced that regression with the O2_float_param
> testcase:
> 
>   cd gdb/testsuite/gdb.ada/O2_float_param
>   powerpc-linux-gnu-gnatmake foo.adb \
>       -cargs -g -O2 -bargs -static -largs -static
>   scp foo $TARGET:
>   […gdb…] foo                                   \
>       -batch                                    \
>       -ex 'tbreak callee.increment'             \
>       -ex 'target remote | ssh gdbserver - foo' \
>       -ex continue
> 
> Expected:
> 
>   > Temporary breakpoint 1, callee.increment (val=val@entry=99.0, msg=...) at callee.adb:19
> 
> Currently, since 2023-12-14 "gdb: make get_frame_register_bytes take the
> next frame" (9fc79b42369):
> 
>   > Temporary breakpoint 1, callee.increment (val=<optimized out>, val@entry=0.0, msg=...) at callee.adb:19
> 
> Applying the attached patch gets us the expected output back, and does
> not introduce further regressions that I could find.  I've tried it on
> top of both master and your recently submitted "register value cleanups"
> series, <20231221191716.257256-1-simon.marchi@efficios.com>.
> 
> Let me know if I've missed something!

Your patch LGTM, thanks for finding and fixing it.

Approved-By: Simon Marchi <simon.marchi@efficios.com>

Simon

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

* Re: [PATCH 12/24] gdb: make get_frame_register_bytes take the next frame
  2023-12-22 15:31     ` Simon Marchi
@ 2023-12-22 16:22       ` Kévin Le Gouguec
  0 siblings, 0 replies; 34+ messages in thread
From: Kévin Le Gouguec @ 2023-12-22 16:22 UTC (permalink / raw)
  To: Simon Marchi
  Cc: gdb-patches, Luis Machado, John Baldwin, Aktemur, Tankut Baris

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

>> Applying the attached patch gets us the expected output back, and does
>> not introduce further regressions that I could find.  I've tried it on
>> top of both master and your recently submitted "register value cleanups"
>> series, <20231221191716.257256-1-simon.marchi@efficios.com>.
>> 
>> Let me know if I've missed something!
>
> Your patch LGTM, thanks for finding and fixing it.

Thank you for confirming!  Pushed.

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

* [PATCH 24/24] gdb/testsuite: add tests for unwinding of pseudo registers
  2023-11-08  5:00 Simon Marchi
@ 2023-11-08  5:01 ` Simon Marchi
  0 siblings, 0 replies; 34+ 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] 34+ messages in thread

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

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