diff --git a/gdb/regcache.c b/gdb/regcache.c
index 750ea2ad30..babc0e1d43 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -812,6 +812,27 @@ readable_regcache::read_part (int regnum, int offset, int len, void *in,
   return REG_VALID;
 }
 
+/* See regcache.h.  */
+
+void
+reg_buffer::raw_collect_part (int regnum, int offset, int len, void *in) const
+{
+  struct gdbarch *gdbarch = arch ();
+  gdb_byte *reg = (gdb_byte *) alloca (register_size (gdbarch, regnum));
+
+  gdb_assert (in != NULL);
+  gdb_assert (offset >= 0 && offset <= m_descr->sizeof_register[regnum]);
+  gdb_assert (len >= 0 && offset + len <= m_descr->sizeof_register[regnum]);
+  /* Something to do?  */
+  if (offset + len == 0)
+    return;
+  /* Read (when needed) ...  */
+  raw_collect (regnum, reg);
+
+  /* ... modify ...  */
+  memcpy (in, reg + offset, len);
+}
+
 enum register_status
 regcache::write_part (int regnum, int offset, int len,
 		     const void *out, bool is_raw)
@@ -849,6 +870,33 @@ regcache::write_part (int regnum, int offset, int len,
   return REG_VALID;
 }
 
+/* See regcache.h.  */
+
+void
+reg_buffer::raw_supply_part (int regnum, int offset, int len,
+			     const gdb_byte *out)
+{
+  struct gdbarch *gdbarch = arch ();
+  gdb_byte *reg = (gdb_byte *) alloca (register_size (gdbarch, regnum));
+
+  gdb_assert (offset >= 0 && offset <= m_descr->sizeof_register[regnum]);
+  gdb_assert (len >= 0 && offset + len <= m_descr->sizeof_register[regnum]);
+  /* Something to do?  */
+  if (offset + len == 0)
+    return;
+  /* Read (when needed) ...  */
+  if (offset > 0
+      || offset + len < m_descr->sizeof_register[regnum])
+    raw_collect (regnum, reg);
+
+  if (out == nullptr)
+    memset (reg + offset, 0, len);
+  else
+    memcpy (reg + offset, out, len);
+  /* ... write (when needed).  */
+  raw_supply (regnum, reg);
+}
+
 enum register_status
 readable_regcache::raw_read_part (int regnum, int offset, int len, gdb_byte *buf)
 {
@@ -1016,12 +1064,26 @@ regcache::transfer_regset (const struct regset *regset,
 	    if (offs + slot_size > size)
 	      break;
 
-	    if (out_buf)
-	      raw_collect (regno, (gdb_byte *) out_buf + offs);
+	    gdb_byte *out_loc = (gdb_byte *) out_buf + offs;
+	    const gdb_byte *in_loc = in_buf ? (const gdb_byte *) in_buf + offs
+					    : NULL;
+
+	    if (slot_size < m_descr->sizeof_register[regno])
+	      {
+		/* Register is bigger than the size of the slot.  Prevent
+		   possible overflow.  */
+		if (out_buf)
+		  raw_collect_part (regno, 0, slot_size, out_loc);
+		else
+		  out_regcache->raw_supply_part (regno, 0, slot_size, in_loc);
+	      }
 	    else
-	      out_regcache->raw_supply (regno, in_buf
-					? (const gdb_byte *) in_buf + offs
-					: NULL);
+	      {
+		if (out_buf)
+		  raw_collect (regno, out_loc);
+		else
+		  out_regcache->raw_supply (regno, in_loc);
+	      }
 	  }
       else
 	{
@@ -1030,12 +1092,26 @@ regcache::transfer_regset (const struct regset *regset,
 	  if (offs + slot_size > size)
 	    return;
 
-	  if (out_buf)
-	    raw_collect (regnum, (gdb_byte *) out_buf + offs);
+	  gdb_byte *out_loc = (gdb_byte *) out_buf + offs;
+	  const gdb_byte *in_loc = in_buf ? (const gdb_byte *) in_buf + offs
+					  : NULL;
+
+	  if (slot_size < m_descr->sizeof_register[regno])
+	    {
+	      /* Register is bigger than the size of the slot.  Prevent
+		 possible overflow.  */
+	      if (out_buf)
+		raw_collect_part (regnum, 0, slot_size, out_loc);
+	      else
+		out_regcache->raw_supply_part (regnum, 0, slot_size, in_loc);
+	    }
 	  else
-	    out_regcache->raw_supply (regnum, in_buf
-				      ? (const gdb_byte *) in_buf + offs
-				      : NULL);
+	    {
+	      if (out_buf)
+		raw_collect (regnum, out_loc);
+	      else
+		out_regcache->raw_supply (regnum, in_loc);
+	    }
 	  return;
 	}
     }
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 41465fb20d..3e2c5a9067 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -163,6 +163,10 @@ public:
   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 REGCACHE,
+     reading only LEN.  */
+  void raw_collect_part (int regnum, int offset, int len, void *in) const;
+
   /* See common/common-regcache.h.  */
   void raw_supply (int regnum, const void *buf) override;
 
@@ -184,6 +188,10 @@ public:
      unavailable).  */
   void raw_supply_zeroed (int regnum);
 
+  /* Supply register REGNUM to REGCACHE, starting at offset in REGCACHE, writing
+     only LEN.  */
+  void raw_supply_part (int regnum, int offset, int len, const gdb_byte *out);
+
   void invalidate (int regnum);
 
   virtual ~reg_buffer () = default;
