Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH] Support large registers in regcache transfer_regset
@ 2018-06-12  8:04 Alan Hayward
  2018-06-17  2:52 ` Simon Marchi
  0 siblings, 1 reply; 6+ messages in thread
From: Alan Hayward @ 2018-06-12  8:04 UTC (permalink / raw)
  To: gdb-patches; +Cc: nd, Alan Hayward

Regcache transfer_regset is used to copy registers to/from a regset.
If the size of a regcache register is greater than it's slot in the regset
then the writes will overflow into the next slot(s), causing general
overflow corruption with the latter slots.

Add raw_collect_part and raw_supply_part, which are simplified versions of
raw_read_part and raw_write_part. Use these in accordingly in transfer_regset.

This is required to support the .reg2 core section on Aarch64 SVE, which
only has enough space to store the first 128bits of each vector register.

This patch replaces [PATCH v2 10/10] Remove reg2 section from Aarch64 SVE cores

Tested by generating cores on aarch64 and aarch64 sve, both via gdb and the
kernel, then ensuring the cores load back on the both systems.
Checked cores on x86 still look ok.
Ran make check on x86 and aarch64.

2018-06-12  Alan Hayward  <alan.hayward@arm.com>

	* regcache.c (reg_buffer::raw_collect_part): New function.
	(reg_buffer::raw_supply_part): Likewise.
	(regcache::transfer_regset): Call new functions.
	* regcache.h (reg_buffer::raw_collect_part): New declaration.
	(reg_buffer::raw_supply_part): Likewise.
---
 gdb/regcache.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 gdb/regcache.h |  8 +++++
 2 files changed, 94 insertions(+), 10 deletions(-)

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;
-- 
2.15.1 (Apple Git-101)


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

* Re: [PATCH] Support large registers in regcache transfer_regset
  2018-06-12  8:04 [PATCH] Support large registers in regcache transfer_regset Alan Hayward
@ 2018-06-17  2:52 ` Simon Marchi
  2018-06-19 11:28   ` Alan Hayward
  0 siblings, 1 reply; 6+ messages in thread
From: Simon Marchi @ 2018-06-17  2:52 UTC (permalink / raw)
  To: Alan Hayward, gdb-patches; +Cc: nd

On 2018-06-12 04:03 AM, Alan Hayward wrote:
> Regcache transfer_regset is used to copy registers to/from a regset.
> If the size of a regcache register is greater than it's slot in the regset
> then the writes will overflow into the next slot(s), causing general
> overflow corruption with the latter slots.

Hi Alan,

Can you please remind me when this happens?  When reading cores, we don't
write to regsets, if I understand correctly, so it would not be a problem
tben.

> Add raw_collect_part and raw_supply_part, which are simplified versions of
> raw_read_part and raw_write_part. Use these in accordingly in transfer_regset.
> 
> This is required to support the .reg2 core section on Aarch64 SVE, which
> only has enough space to store the first 128bits of each vector register.
> 
> This patch replaces [PATCH v2 10/10] Remove reg2 section from Aarch64 SVE cores
> 
> Tested by generating cores on aarch64 and aarch64 sve, both via gdb and the
> kernel, then ensuring the cores load back on the both systems.
> Checked cores on x86 still look ok.
> Ran make check on x86 and aarch64.
> 
> 2018-06-12  Alan Hayward  <alan.hayward@arm.com>
> 
> 	* regcache.c (reg_buffer::raw_collect_part): New function.
> 	(reg_buffer::raw_supply_part): Likewise.
> 	(regcache::transfer_regset): Call new functions.
> 	* regcache.h (reg_buffer::raw_collect_part): New declaration.
> 	(reg_buffer::raw_supply_part): Likewise.
> ---
>  gdb/regcache.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
>  gdb/regcache.h |  8 +++++
>  2 files changed, 94 insertions(+), 10 deletions(-)
> 
> 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

I know this comes from raw_read_part/raw_write_part, but I find the naming of the
in/out parameters confusing.  I think it would make more sense if this one was
called "out" and the one in raw_supply_part "in".

Also, can they be "gdb_byte *" instead of "void *"?

> +{
> +  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]);

The "&& offset <= m_descr->sizeof_register[regnum]" is redundant, given the
following line.  Other than mimicking raw_read_part, is there a reason why
these are signed integers?  Having them unsigned would avoid having to assert
they are >= 0.

> +  /* 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]);

"&& offset <= m_descr->sizeof_register[regnum]" is redundant here too.

> +  /* 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);
> +	      }

I think the code here could stay relatively simple if we always went
through raw_collect_part/raw_supply_part.  To avoid the unnecessary copy
that would happen in raw_collect_part/raw_supply_part in the typical case
where the full register is transferred, there could be a shortcut path
that calls raw_collect/raw_supply when collecting/supplying the full
reguster.

Simon


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

* Re: [PATCH] Support large registers in regcache transfer_regset
  2018-06-17  2:52 ` Simon Marchi
@ 2018-06-19 11:28   ` Alan Hayward
  2018-06-19 14:52     ` Simon Marchi
  0 siblings, 1 reply; 6+ messages in thread
From: Alan Hayward @ 2018-06-19 11:28 UTC (permalink / raw)
  To: Simon Marchi; +Cc: GDB Patches, nd



> On 17 Jun 2018, at 03:52, Simon Marchi <simark@simark.ca> wrote:
> 
> On 2018-06-12 04:03 AM, Alan Hayward wrote:
>> Regcache transfer_regset is used to copy registers to/from a regset.
>> If the size of a regcache register is greater than it's slot in the regset
>> then the writes will overflow into the next slot(s), causing general
>> overflow corruption with the latter slots.
> 
> Hi Alan,
> 
> Can you please remind me when this happens?  When reading cores, we don't
> write to regsets, if I understand correctly, so it would not be a problem
> tben.

Type “generate-core-file” on the command line.
Not sure if there are other ways of triggering it.

> 
>> Add raw_collect_part and raw_supply_part, which are simplified versions of
>> raw_read_part and raw_write_part. Use these in accordingly in transfer_regset.
>> 
>> This is required to support the .reg2 core section on Aarch64 SVE, which
>> only has enough space to store the first 128bits of each vector register.
>> 
>> This patch replaces [PATCH v2 10/10] Remove reg2 section from Aarch64 SVE cores
>> 
>> Tested by generating cores on aarch64 and aarch64 sve, both via gdb and the
>> kernel, then ensuring the cores load back on the both systems.
>> Checked cores on x86 still look ok.
>> Ran make check on x86 and aarch64.
>> 
>> 2018-06-12  Alan Hayward  <alan.hayward@arm.com>
>> 
>> 	* regcache.c (reg_buffer::raw_collect_part): New function.
>> 	(reg_buffer::raw_supply_part): Likewise.
>> 	(regcache::transfer_regset): Call new functions.
>> 	* regcache.h (reg_buffer::raw_collect_part): New declaration.
>> 	(reg_buffer::raw_supply_part): Likewise.
>> ---
>> gdb/regcache.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
>> gdb/regcache.h |  8 +++++
>> 2 files changed, 94 insertions(+), 10 deletions(-)
>> 
>> 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
> 
> I know this comes from raw_read_part/raw_write_part, but I find the naming of the
> in/out parameters confusing.  I think it would make more sense if this one was
> called "out" and the one in raw_supply_part "in”.
> 

Agreed. I’ll also update the original code too.

> Also, can they be "gdb_byte *" instead of "void *”?

Done.

> 
>> +{
>> +  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]);
> 
> The "&& offset <= m_descr->sizeof_register[regnum]" is redundant, given the
> following line.  Other than mimicking raw_read_part, is there a reason why
> these are signed integers?  Having them unsigned would avoid having to assert
> they are >= 0.

Looking at regcache, int is used for regnum throughout. I’d rather not have a
mismatch, and wouldn’t want to update everything else either (at least not
in this patch). In addition, if this code is going to now call down to
raw_collect/raw_supply, they should match.

I’ve updated the asserts in both here and the original code too.

> 
>> +  /* 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]);
> 
> "&& offset <= m_descr->sizeof_register[regnum]" is redundant here too.

Done.

> 
>> +  /* 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);
>> +	      }
> 
> I think the code here could stay relatively simple if we always went
> through raw_collect_part/raw_supply_part.  To avoid the unnecessary copy
> that would happen in raw_collect_part/raw_supply_part in the typical case
> where the full register is transferred, there could be a shortcut path
> that calls raw_collect/raw_supply when collecting/supplying the full
> reguster.
> 

Ok, added this too.

However, this uncovered an issue. If the slot size is larger than the size of
the register then this causes raw_collect_part/raw_supply_part to assert.
This is triggered on aarch64 with CPSR, because the register is 32bits but
the core file reserves 64bits.
In the current code there is a bug here as the existing code doesn’t ensure
this extra space in the slot size is cleared when writing out cores.
(In reality, probably not much of a problem as we ignore that space when
reading the core back in. But it could be an issue if another program was
Reading gdb’s core files).

To fix this I allowed raw_collect_part/raw_supply_part to handle cases where
the length overflows the register, either filling with zeros or truncating
accordingly.
Also, because the existing transfer_register code would result in register
being set to invalid when writing null to the regcache, I also ensured
that was kept.


I’ve also made a few more changes to write_part/read_part to make them
consistent with the changes to the new functions. There is no functionality
change to them.


Is this ok?


2018-06-19  Alan Hayward  <alan.hayward@arm.com>

	* regcache.c (readable_regcache::read_part): Add full read
	shortcut.
	(reg_buffer::raw_collect_part): New function.
	(regcache::write_part): Add full write shortcut.
	(reg_buffer::raw_supply_part): New function.
	(regcache::transfer_regset): Use new functions.
	* regcache.h (reg_buffer::raw_collect_part): New declaration.
	(reg_buffer::raw_supply_part): Likewise.


diff --git a/gdb/regcache.h b/gdb/regcache.h
index 41465fb20d0dcdddaf39e47c1fc79f1e8eadc260..297f04b5b44690562602a2b5ce15b9dd1a92b18b 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -163,6 +163,11 @@ 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.  If this runs off the end of the register, then fill the
+     additional space with zeros.  */
+  void raw_collect_part (int regnum, int offset, int len, gdb_byte *out) const;
+
   /* See common/common-regcache.h.  */
   void raw_supply (int regnum, const void *buf) override;

@@ -184,6 +189,11 @@ public:
      unavailable).  */
   void raw_supply_zeroed (int regnum);

+  /* Supply register REGNUM to REGCACHE, starting at offset in REGCACHE, writing
+     only LEN, without editing the rest of the register.  If the length of the
+     supplied value would overflow the register, then truncate.  */
+  void raw_supply_part (int regnum, int offset, int len, const gdb_byte *in);
+
   void invalidate (int regnum);

   virtual ~reg_buffer () = default;
@@ -254,8 +264,11 @@ public:
   struct value *cooked_read_value (int regnum);

 protected:
-  enum register_status read_part (int regnum, int offset, int len, void *in,
-				  bool is_raw);
+
+  /* Perform a partial register transfer using a read, modify, write
+     operation.  */
+  enum register_status read_part (int regnum, int offset, int len,
+				  gdb_byte *out, bool is_raw);
 };

 /* Buffer of registers, can be read and written.  */
@@ -356,8 +369,10 @@ private:
 			int regnum, const void *in_buf,
 			void *out_buf, size_t size) const;

+  /* Perform a partial register transfer using a read, modify, write
+     operation.  */
   enum register_status write_part (int regnum, int offset, int len,
-				   const void *out, bool is_raw);
+				   const gdb_byte *out, bool is_raw);


   /* The address space of this register cache (for registers where it
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 750ea2ad30f60b03dd76fc30cb72f87d5a531406..5c672e6928101710904c2e70fc829036214f64b6 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -780,75 +780,138 @@ regcache::cooked_write (int regnum, const gdb_byte *buf)
 				   regnum, buf);
 }

-/* Perform a partial register transfer using a read, modify, write
-   operation.  */
+/* See regcache.h.  */

 enum register_status
-readable_regcache::read_part (int regnum, int offset, int len, void *in,
+readable_regcache::read_part (int regnum, int offset, int len, gdb_byte *out,
 			      bool is_raw)
 {
-  struct gdbarch *gdbarch = arch ();
-  gdb_byte *reg = (gdb_byte *) alloca (register_size (gdbarch, regnum));
+  int reg_size = register_size (arch (), 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)
+  gdb_assert (out != NULL);
+  gdb_assert (offset >= 0 && len >= 0 && offset + len <= reg_size);
+
+  if (offset == 0 && len == 0)
     return REG_VALID;
-  /* Read (when needed) ...  */
+
+  if (offset == 0 && len == reg_size)
+    return (is_raw) ? raw_read (regnum, out) : cooked_read (regnum, out);
+
+  /* Read to buffer, then write out.  */
   enum register_status status;
+  gdb_byte *reg = (gdb_byte *) alloca (reg_size);

-  if (is_raw)
-    status = raw_read (regnum, reg);
-  else
-    status = cooked_read (regnum, reg);
+  status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
   if (status != REG_VALID)
     return status;

-  /* ... modify ...  */
-  memcpy (in, reg + offset, len);
-
+  memcpy (out, reg + offset, len);
   return REG_VALID;
 }

+/* See regcache.h.  */
+
+void
+reg_buffer::raw_collect_part (int regnum, int offset, int len,
+			      gdb_byte *out) const
+{
+  int reg_size = register_size (arch (), regnum);
+
+  gdb_assert (out != nullptr);
+  gdb_assert (offset >= 0 && len >= 0 && offset <= reg_size);
+
+  if (offset == 0 && len == 0)
+    return;
+
+  if (offset == 0 && len == reg_size)
+    return raw_collect (regnum, out);
+
+  /* Read to buffer, then write out.  */
+  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
+  raw_collect (regnum, reg);
+
+  if (offset + len <= reg_size)
+    memcpy (out, reg + offset, len);
+  else
+    {
+      /* Requested region runs off the end of the register.  Clear the
+	 additional space.  */
+      memcpy (out, reg + offset, reg_size - offset);
+      memset (out + reg_size, 0, offset + len - reg_size);
+    }
+}
+
+/* See regcache.h.  */
+
 enum register_status
-regcache::write_part (int regnum, int offset, int len,
-		     const void *out, bool is_raw)
+regcache::write_part (int regnum, int offset, int len, const gdb_byte *in,
+		      bool is_raw)
 {
-  struct gdbarch *gdbarch = arch ();
-  gdb_byte *reg = (gdb_byte *) alloca (register_size (gdbarch, regnum));
+  int reg_size = register_size (arch (), regnum);

-  gdb_assert (out != 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)
+  gdb_assert (in != NULL);
+  gdb_assert (offset >= 0 && len >= 0 && offset + len <= reg_size);
+
+  if (offset == 0 && len == 0)
     return REG_VALID;
-  /* Read (when needed) ...  */
-  if (offset > 0
-      || offset + len < m_descr->sizeof_register[regnum])
+
+  if (offset == 0 && len == reg_size)
     {
-      enum register_status status;
+      (is_raw) ? raw_write (regnum, in) : cooked_write (regnum, in);
+      return REG_VALID;
+    }

-      if (is_raw)
-	status = raw_read (regnum, reg);
-      else
-	status = cooked_read (regnum, reg);
+  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
+
+  /* Read when needed.  */
+  if (offset > 0 || offset + len < reg_size)
+    {
+      enum register_status status;
+      status = (is_raw) ? raw_read (regnum, reg) : cooked_read (regnum, reg);
       if (status != REG_VALID)
 	return status;
     }

-  memcpy (reg + offset, out, len);
-  /* ... write (when needed).  */
-  if (is_raw)
-    raw_write (regnum, reg);
-  else
-    cooked_write (regnum, reg);
-
+  /* Write to buffer, then write out.  */
+  memcpy (reg + offset, in, len);
+  is_raw ? raw_write (regnum, reg) : 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)
+{
+  int reg_size = register_size (arch (), regnum);
+
+  gdb_assert (in != nullptr);
+  gdb_assert (offset >= 0 && len >= 0 && offset <= reg_size);
+
+  if (offset == 0 && len == 0)
+    return;
+
+  if (offset + len > reg_size)
+    {
+      /* Truncate length to fit the size of the regcache register.  */
+      len = reg_size - offset;
+    }
+
+  if (offset == 0 && len == reg_size)
+    return raw_supply (regnum, in);
+
+  gdb_byte *reg = (gdb_byte *) alloca (reg_size);
+
+  /* Read when needed.  */
+  if (offset > 0 || offset + len < reg_size)
+    raw_collect (regnum, reg);
+
+  /* Write to buffer, then write out.  */
+  memcpy (reg + offset, in, len);
+  raw_supply (regnum, reg);
+}
+
 enum register_status
 readable_regcache::raw_read_part (int regnum, int offset, int len, gdb_byte *buf)
 {
@@ -994,6 +1057,7 @@ regcache::transfer_regset (const struct regset *regset,
 {
   const struct regcache_map_entry *map;
   int offs = 0, count;
+  struct gdbarch *gdbarch = arch ();

   for (map = (const struct regcache_map_entry *) regset->regmap;
        (count = map->count) != 0;
@@ -1016,12 +1080,18 @@ regcache::transfer_regset (const struct regset *regset,
 	    if (offs + slot_size > size)
 	      break;

+	    /* Use part versions to prevent possible overflow.  */
 	    if (out_buf)
-	      raw_collect (regno, (gdb_byte *) out_buf + offs);
+	      raw_collect_part (regno, 0, slot_size,
+				(gdb_byte *) out_buf + offs);
+	    else if (in_buf)
+	      out_regcache->raw_supply_part (regno, 0, slot_size,
+					     (const gdb_byte *) in_buf + offs);
 	    else
-	      out_regcache->raw_supply (regno, in_buf
-					? (const gdb_byte *) in_buf + offs
-					: NULL);
+	      {
+	      	/* Invalidate the register.  */
+		out_regcache->raw_supply (regno, nullptr);
+	      }
 	  }
       else
 	{
@@ -1030,12 +1100,19 @@ regcache::transfer_regset (const struct regset *regset,
 	  if (offs + slot_size > size)
 	    return;

+	  /* Use part versions to prevent possible overflow.  */
 	  if (out_buf)
-	    raw_collect (regnum, (gdb_byte *) out_buf + offs);
+	    raw_collect_part (regnum, 0, slot_size,
+			      (gdb_byte *) out_buf + offs);
+	  else if (in_buf)
+	    out_regcache->raw_supply_part (regnum, 0, slot_size,
+					   (const gdb_byte *) in_buf + offs);
 	  else
-	    out_regcache->raw_supply (regnum, in_buf
-				      ? (const gdb_byte *) in_buf + offs
-				      : NULL);
+	    {
+	      /* Invalidate the register.  */
+	      out_regcache->raw_supply (regnum, nullptr);
+	    }
+
 	  return;
 	}
     }



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

* Re: [PATCH] Support large registers in regcache transfer_regset
  2018-06-19 11:28   ` Alan Hayward
@ 2018-06-19 14:52     ` Simon Marchi
  2018-06-19 15:46       ` Alan Hayward
  0 siblings, 1 reply; 6+ messages in thread
From: Simon Marchi @ 2018-06-19 14:52 UTC (permalink / raw)
  To: Alan Hayward; +Cc: GDB Patches, nd

On 2018-06-19 07:27, Alan Hayward wrote:
>>> +/* 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]);
>> 
>> The "&& offset <= m_descr->sizeof_register[regnum]" is redundant, 
>> given the
>> following line.  Other than mimicking raw_read_part, is there a reason 
>> why
>> these are signed integers?  Having them unsigned would avoid having to 
>> assert
>> they are >= 0.
> 
> Looking at regcache, int is used for regnum throughout. I’d rather not 
> have a
> mismatch, and wouldn’t want to update everything else either (at least 
> not
> in this patch). In addition, if this code is going to now call down to
> raw_collect/raw_supply, they should match.

Sorry, I was talking about len and offset, not regnum.

Simon


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

* Re: [PATCH] Support large registers in regcache transfer_regset
  2018-06-19 14:52     ` Simon Marchi
@ 2018-06-19 15:46       ` Alan Hayward
  2018-06-19 21:12         ` Simon Marchi
  0 siblings, 1 reply; 6+ messages in thread
From: Alan Hayward @ 2018-06-19 15:46 UTC (permalink / raw)
  To: Simon Marchi; +Cc: GDB Patches, nd



> On 19 Jun 2018, at 15:52, Simon Marchi <simark@simark.ca> wrote:
> 
> On 2018-06-19 07:27, Alan Hayward wrote:
>>>> +/* 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]);
>>> The "&& offset <= m_descr->sizeof_register[regnum]" is redundant, given the
>>> following line.  Other than mimicking raw_read_part, is there a reason why
>>> these are signed integers?  Having them unsigned would avoid having to assert
>>> they are >= 0.
>> Looking at regcache, int is used for regnum throughout. I’d rather not have a
>> mismatch, and wouldn’t want to update everything else either (at least not
>> in this patch). In addition, if this code is going to now call down to
>> raw_collect/raw_supply, they should match.
> 
> Sorry, I was talking about len and offset, not regnum.
> 
> Simon

Ah, ok. If doing that, then it’d make sense to update regcache_map_entry to use
unsigned ints for count and size.

struct regcache_map_entry
{
  int count;
  int regno;
  int size;
};

At that point it probably makes sense to repost the patch as v2 in smaller pieces?


Alan.



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

* Re: [PATCH] Support large registers in regcache transfer_regset
  2018-06-19 15:46       ` Alan Hayward
@ 2018-06-19 21:12         ` Simon Marchi
  0 siblings, 0 replies; 6+ messages in thread
From: Simon Marchi @ 2018-06-19 21:12 UTC (permalink / raw)
  To: Alan Hayward; +Cc: GDB Patches, nd

On 2018-06-19 11:46, Alan Hayward wrote:
> Ah, ok. If doing that, then it’d make sense to update 
> regcache_map_entry to use
> unsigned ints for count and size.
> 
> struct regcache_map_entry
> {
>   int count;
>   int regno;
>   int size;
> };
> 
> At that point it probably makes sense to repost the patch as v2 in
> smaller pieces?

If you end up doing this (I did not really dig much to see if it works), 
then yes it might make sense to have a preparatory patch that changes 
some types to be unsigned.  As always, one logical change per patch is 
appreciated and is much easier to review.

If you're going to send a proper v2, I'll wait for this one to look at 
the new changes you sent as part of the "updated" v1.

Simon


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

end of thread, other threads:[~2018-06-19 21:12 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-12  8:04 [PATCH] Support large registers in regcache transfer_regset Alan Hayward
2018-06-17  2:52 ` Simon Marchi
2018-06-19 11:28   ` Alan Hayward
2018-06-19 14:52     ` Simon Marchi
2018-06-19 15:46       ` Alan Hayward
2018-06-19 21:12         ` Simon Marchi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox