[05/24] gdb: change regcache interface to use array_view

Message ID 20231201162751.741751-6-simon.marchi@efficios.com
State New
Headers
Series Fix reading and writing pseudo registers in non-current frames |

Commit Message

Simon Marchi Dec. 1, 2023, 4:27 p.m. UTC
  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(-)
  

Patch

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);