[5/7] : Regcache: Allow writable regcache

Message ID FB34034C-2DD1-4400-9B4A-CB4497B1E924@arm.com
State New, archived
Headers

Commit Message

Alan Hayward Aug. 17, 2017, 8:48 a.m. UTC
  This patch allows a regcache to be writable.

With the previous patches in place, we are now secure that only a
target_regcache can write through to a target, and only a regcache
will use the cooked registers.

This patch adds a readonly flag to the public regcache constructor and dup(),
with a default value of true.
A target_regcache can never be readonly.

The cooked registers are only allocated on a regcache, a target_regcache
will allocate only the raw registers.
However, the register status array is always fully allocated, this ensures that
cooked_read/cooked_write can always check the status of the register without
having to add a target_regcache check.


Tested on a --enable-targets=all build with board files unix,
native-gdbserver and unittest.exp.

2017-08-16  Alan Hayward  <alan.hayward@arm.com>

	* regcache.c (regcache::regcache): Add bool flags.
	(target_regcache::target_regcache): Likewise.
	(regcache::dup): Likewise.
	(target_regcache::dup): Likewise.
	(regcache::get_register_status): Split into two functions...
	(target_regcache::get_register_status): ...here.
	(regcache::cooked_read): Remove check.
	(regcache::cooked_read_value): Likewise.
  

Patch

diff --git a/gdb/regcache.h b/gdb/regcache.h
index 1437dac220e0364557e3c568f1c5223cec598dfd..f4408a562f84bb2d0dcd2792c646c0f98deee716 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -235,9 +235,8 @@  typedef struct cached_reg
class regcache
{
public:
-  regcache (gdbarch *gdbarch, address_space *aspace_)
-    : regcache (gdbarch, aspace_, true)
-  {}
+  regcache (gdbarch *gdbarch, address_space *aspace_, bool readonly_p_ = true,
+	    bool allocate_registers = true);

  regcache (const regcache &) = delete;
  void operator= (const regcache &) = delete;
@@ -256,7 +255,7 @@  public:
  }

  /* Duplicate self into a new regcache.  */
-  virtual regcache* dup ();
+  virtual regcache* dup (bool readonly_p = true);

  /* Copy the register contents from a target_regcache to self.
     All cooked registers are read and cached.  */
@@ -300,7 +299,7 @@  public:

  void raw_supply_zeroed (int regnum);

-  enum register_status get_register_status (int regnum) const;
+  virtual enum register_status get_register_status (int regnum) const;

  void raw_set_cached_value (int regnum, const gdb_byte *buf);

@@ -337,7 +336,6 @@  public:
  void debug_print_register (const char *func, int regno);

protected:
-  regcache (gdbarch *gdbarch, address_space *aspace_, bool readonly_p_);

  gdb_byte *register_buffer (int regnum) const;

@@ -392,13 +390,14 @@  public:
  void restore_to (target_regcache *dst) = delete;

  /* Duplicate self into a new regcache.  Result is not a target_regcache.  */
-  regcache* dup ();
+  regcache* dup (bool readonly_p = true);

  /* Overridden regcache methods.  These versions will pass the read/write
     through to the target.  */
  enum register_status raw_read (int regnum, gdb_byte *buf);
  virtual void raw_write (int regnum, const gdb_byte *buf);
  void raw_update (int regnum);
+  enum register_status get_register_status (int regnum) const;

  ptid_t ptid () const
  {
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 5405dba7a706910f0b6d20c77eef657e38695b34..9d04c7be904cfb17ac8fd871e9d7678df1694dfd 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -189,29 +189,32 @@  regcache_register_size (const struct regcache *regcache, int n)
}

regcache::regcache (gdbarch *gdbarch, address_space *aspace_,
-		    bool readonly_p_)
+		    bool readonly_p_, bool allocate_registers)
  : m_aspace (aspace_), m_readonly_p (readonly_p_)
{
  gdb_assert (gdbarch != NULL);
  m_descr = regcache_descr (gdbarch);

-  if (m_readonly_p)
+  if (allocate_registers)
    {
+      /* Need extra space to store the additional cooked registers for when
+	 the detached regcache is used to save a regcache.  */
      m_registers = XCNEWVEC (gdb_byte, m_descr->sizeof_cooked_registers);
-      m_register_status = XCNEWVEC (signed char,
-				    m_descr->sizeof_cooked_register_status);
-    }
-  else
-    {
-      m_registers = XCNEWVEC (gdb_byte, m_descr->sizeof_raw_registers);
-      m_register_status = XCNEWVEC (signed char,
-				    m_descr->sizeof_raw_register_status);
    }
+
+  /* All status' are initialised to REG_UNKNOWN.  */
+  m_register_status = XCNEWVEC (signed char,
+				m_descr->sizeof_cooked_register_status);
}

target_regcache::target_regcache (gdbarch *gdbarch, address_space *aspace_)
-  : regcache (gdbarch, aspace_, false)
+  : regcache (gdbarch, aspace_, false, false)
{
+  /* Only allocate the raw registers - cooked registers are not cached.
+     Note that the register status is still fully allocated, to allow the
+     checking of the state of any register.  */
+  m_registers = XCNEWVEC (gdb_byte, m_descr->sizeof_raw_registers);
+
  m_ptid = minus_one_ptid;

  /* A target_regcache should never be readonly.  */
@@ -359,9 +362,9 @@  regcache::restore_to (target_regcache *dst)

/* Duplicate detached regcache to a detached regcache.  */
regcache*
-regcache::dup ()
+regcache::dup (bool readonly_p)
{
-  regcache *new_regcache = new regcache (arch (), aspace ());
+  regcache *new_regcache = new regcache (arch (), aspace (), readonly_p);

  memcpy (new_regcache->m_registers, m_registers,
	  m_descr->sizeof_cooked_registers);
@@ -373,9 +376,9 @@  regcache::dup ()

/* Duplicate a target_regcache to a detached regcache.  */
regcache*
-target_regcache::dup ()
+target_regcache::dup (bool readonly_p)
{
-  regcache *new_regcache = new regcache (arch (), aspace ());
+  regcache *new_regcache = new regcache (arch (), aspace (), readonly_p);
  new_regcache->save (do_cooked_read, (void *) this);
  return new_regcache;
}
@@ -391,14 +394,17 @@  enum register_status
regcache::get_register_status (int regnum) const
{
  gdb_assert (regnum >= 0);
-  if (m_readonly_p)
-    gdb_assert (regnum < m_descr->nr_cooked_registers);
-  else
-    gdb_assert (regnum < m_descr->nr_raw_registers);
-
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
  return (enum register_status) m_register_status[regnum];
}

+enum register_status
+target_regcache::get_register_status (int regnum) const
+{
+  gdb_assert (regnum < m_descr->nr_raw_registers);
+  return regcache::get_register_status (regnum);
+}
+
void
regcache_invalidate (struct regcache *regcache, int regnum)
{
@@ -707,8 +713,7 @@  regcache::cooked_read (int regnum, gdb_byte *buf)
  gdb_assert (regnum < m_descr->nr_cooked_registers);
  if (regnum < m_descr->nr_raw_registers)
    return raw_read (regnum, buf);
-  else if (m_readonly_p
-	   && m_register_status[regnum] != REG_UNKNOWN)
+  else if (m_register_status[regnum] != REG_UNKNOWN)
    {
      /* Read-only register cache, perhaps the cooked value was
	 cached?  */
@@ -760,7 +765,7 @@  regcache::cooked_read_value (int regnum)
  gdb_assert (regnum < m_descr->nr_cooked_registers);

  if (regnum < m_descr->nr_raw_registers
-      || (m_readonly_p && m_register_status[regnum] != REG_UNKNOWN)
+      || m_register_status[regnum] != REG_UNKNOWN
      || !gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
    {
      struct value *result;