gdb: change regcache interface to use array_view

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>
This commit is contained in:
Simon Marchi
2023-12-01 11:27:18 -05:00
parent 08d8e7ff94
commit 51e6b8cfd6
9 changed files with 461 additions and 275 deletions

View File

@@ -1108,9 +1108,9 @@ get_frame_func (frame_info_ptr this_frame)
std::unique_ptr<readonly_detached_regcache> std::unique_ptr<readonly_detached_regcache>
frame_save_as_regcache (frame_info_ptr this_frame) 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; return REG_UNAVAILABLE;
else else
return REG_VALID; return REG_VALID;

View File

@@ -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); 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 *header
= (struct user_sve_header *) sve_state.data (); = (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_SVE_Z0_REGNUM + i, reg);
} }
reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); reg_buf->raw_supply (AARCH64_FPSR_REGNUM,
reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); (const gdb_byte *) &fpsimd->fpsr);
reg_buf->raw_supply (AARCH64_FPCR_REGNUM,
(const gdb_byte *) &fpsimd->fpcr);
/* Clear the SVE only registers. */ /* Clear the SVE only registers. */
memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); 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); gdb::byte_vector new_state (SVE_PT_SIZE (32, SVE_PT_REGS_SVE), 0);
memcpy (new_state.data (), sve_state.data (), sve_state.size ()); memcpy (new_state.data (), sve_state.data (), sve_state.size ());
header = (struct user_sve_header *) new_state.data (); 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. */ /* Sanity check the data in the header. */
if (!sve_vl_valid (header->vl) 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)) 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)) 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 /* At this point we have collected all the data from the register
cache and we are ready to update the FPSIMD register content 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. */ /* Sanity check. */
gdb_assert (!za_state.empty ()); 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; struct user_za_header *header = (struct user_za_header *) base;
/* If we have ZA state, read it. Otherwise, make the contents of ZA /* 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. */ /* Fetch the current ZA state from the thread. */
gdb::byte_vector za_state = aarch64_fetch_za_regset (tid); 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; struct user_za_header *za_header = (struct user_za_header *) base;
uint64_t svq = sve_vq_from_vl (za_header->vl); uint64_t svq = sve_vq_from_vl (za_header->vl);

View File

@@ -220,10 +220,9 @@ regcache::regcache (inferior *inf_for_target_calls, gdbarch *gdbarch)
readonly_detached_regcache::readonly_detached_regcache (regcache &src) readonly_detached_regcache::readonly_detached_regcache (regcache &src)
: readonly_detached_regcache (src.arch (), : readonly_detached_regcache (src.arch (),
[&src] (int regnum, gdb_byte *buf) [&src] (int regnum,
{ gdb::array_view<gdb_byte> buf)
return src.cooked_read (regnum, buf); { return src.cooked_read (regnum, buf); })
})
{ {
} }
@@ -233,19 +232,38 @@ reg_buffer::arch () const
return m_descr->gdbarch; 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 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 void
reg_buffer::save (register_read_ftype cooked_read) reg_buffer::save (register_read_ftype cooked_read)
{ {
struct gdbarch *gdbarch = m_descr->gdbarch; struct gdbarch *gdbarch = m_descr->gdbarch;
int regnum;
/* It should have pseudo registers. */ /* It should have pseudo registers. */
gdb_assert (m_has_pseudo); 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 + save_reggroup) and mark them as valid. The full [0 .. gdbarch_num_regs +
gdbarch_num_pseudo_regs) range is checked since some architectures need gdbarch_num_pseudo_regs) range is checked since some architectures need
to save/restore `cooked' registers that live in memory. */ 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)) if (gdbarch_register_reggroup_p (gdbarch, regnum, save_reggroup))
{ {
gdb_byte *dst_buf = register_buffer (regnum); gdb::array_view<gdb_byte> dst_buf = register_buffer (regnum);
enum register_status status = cooked_read (regnum, dst_buf); register_status status = cooked_read (regnum, dst_buf);
gdb_assert (status != REG_UNKNOWN); gdb_assert (status != REG_UNKNOWN);
if (status != REG_VALID) 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; m_register_status[regnum] = status;
} }
@@ -592,21 +610,30 @@ regcache::raw_update (int regnum)
} }
} }
enum register_status register_status
readable_regcache::raw_read (int regnum, gdb_byte *buf) 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); raw_update (regnum);
if (m_register_status[regnum] != REG_VALID) if (m_register_status[regnum] != REG_VALID)
memset (buf, 0, m_descr->sizeof_register[regnum]); memset (dst.data (), 0, dst.size ());
else else
memcpy (buf, register_buffer (regnum), copy (register_buffer (regnum), dst);
m_descr->sizeof_register[regnum]);
return m_register_status[regnum]; 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 enum register_status
regcache_raw_read_signed (struct regcache *regcache, int regnum, LONGEST *val) 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) readable_regcache::raw_read (int regnum, T *val)
{ {
assert_regnum (regnum); assert_regnum (regnum);
size_t len = m_descr->sizeof_register[regnum]; size_t size = m_descr->sizeof_register[regnum];
gdb_byte *buf = (gdb_byte *) alloca (len); gdb_byte *buf = (gdb_byte *) alloca (size);
register_status status = raw_read (regnum, buf); auto view = gdb::make_array_view (buf, size);
register_status status = raw_read (regnum, view);
if (status == REG_VALID) if (status == REG_VALID)
*val = extract_integer<T> ({buf, len}, *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
gdbarch_byte_order (m_descr->gdbarch));
else else
*val = 0; *val = 0;
return status; return status;
} }
@@ -650,13 +679,13 @@ template<typename T, typename>
void void
regcache::raw_write (int regnum, T val) regcache::raw_write (int regnum, T val)
{ {
gdb_byte *buf;
assert_regnum (regnum); assert_regnum (regnum);
buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]);
store_integer (buf, m_descr->sizeof_register[regnum], int size = m_descr->sizeof_register[regnum];
gdbarch_byte_order (m_descr->gdbarch), val); gdb_byte *buf = (gdb_byte *) alloca (size);
raw_write (regnum, buf); auto view = gdb::make_array_view (buf, size);
store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
raw_write (regnum, view);
} }
void void
@@ -680,47 +709,60 @@ regcache_raw_get_signed (struct regcache *regcache, int regnum)
return value; return value;
} }
enum register_status /* See regcache.h. */
readable_regcache::cooked_read (int regnum, gdb_byte *buf)
register_status
readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst)
{ {
gdb_assert (regnum >= 0); gdb_assert (regnum >= 0);
gdb_assert (regnum < m_descr->nr_cooked_registers); gdb_assert (regnum < m_descr->nr_cooked_registers);
if (regnum < num_raw_registers ()) if (regnum < num_raw_registers ())
return raw_read (regnum, buf); return raw_read (regnum, dst);
else if (m_has_pseudo
&& m_register_status[regnum] != REG_UNKNOWN) 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) if (m_register_status[regnum] == REG_VALID)
memcpy (buf, register_buffer (regnum), copy (register_buffer (regnum), dst);
m_descr->sizeof_register[regnum]);
else else
memset (buf, 0, m_descr->sizeof_register[regnum]); memset (dst.data (), 0, dst.size ());
return m_register_status[regnum]; return m_register_status[regnum];
} }
else if (gdbarch_pseudo_register_read_value_p (m_descr->gdbarch)) else if (gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
{ {
struct value *computed; register_status result = REG_VALID;
enum register_status result = REG_VALID;
scoped_value_mark mark; 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 ()) if (computed->entirely_available ())
memcpy (buf, computed->contents_raw ().data (), copy (computed->contents_raw (), dst);
m_descr->sizeof_register[regnum]);
else else
{ {
memset (buf, 0, m_descr->sizeof_register[regnum]); memset (dst.data (), 0, dst.size ());
result = REG_UNAVAILABLE; result = REG_UNAVAILABLE;
} }
return result; return result;
} }
else else
return gdbarch_pseudo_register_read (m_descr->gdbarch, this, return gdbarch_pseudo_register_read (m_descr->gdbarch, this, regnum,
regnum, buf); 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 * 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 /* It is more efficient in general to do this delegation in this
direction than in the other one, even though the value-based direction than in the other one, even though the value-based
API is preferred. */ API is preferred. */
if (cooked_read (regnum, if (cooked_read (regnum, result->contents_raw ()) == REG_UNAVAILABLE)
result->contents_raw ().data ()) == REG_UNAVAILABLE)
result->mark_bytes_unavailable (0, result->mark_bytes_unavailable (0,
result->type ()->length ()); result->type ()->length ());
@@ -767,12 +808,12 @@ enum register_status
readable_regcache::cooked_read (int regnum, T *val) readable_regcache::cooked_read (int regnum, T *val)
{ {
gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers); gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
size_t len = m_descr->sizeof_register[regnum]; size_t size = m_descr->sizeof_register[regnum];
gdb_byte *buf = (gdb_byte *) alloca (len); gdb_byte *buf = (gdb_byte *) alloca (size);
register_status status = cooked_read (regnum, buf); auto view = gdb::make_array_view (buf, size);
register_status status = cooked_read (regnum, view);
if (status == REG_VALID) if (status == REG_VALID)
*val = extract_integer<T> ({buf, len}, *val = extract_integer<T> (view, gdbarch_byte_order (m_descr->gdbarch));
gdbarch_byte_order (m_descr->gdbarch));
else else
*val = 0; *val = 0;
return status; return status;
@@ -798,13 +839,14 @@ template<typename T, typename>
void void
regcache::cooked_write (int regnum, T val) 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); int size = m_descr->sizeof_register[regnum];
buf = (gdb_byte *) alloca (m_descr->sizeof_register[regnum]); gdb_byte *buf = (gdb_byte *) alloca (size);
store_integer (buf, m_descr->sizeof_register[regnum], auto view = gdb::make_array_view (buf, size);
gdbarch_byte_order (m_descr->gdbarch), val); store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
cooked_write (regnum, buf); cooked_write (regnum, view);
} }
void void
@@ -816,11 +858,10 @@ regcache_cooked_write_unsigned (struct regcache *regcache, int regnum,
} }
void 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); 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 /* 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. */ 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 /* If we have a valid copy of the register, and new value == old
value, then don't bother doing the actual store. */ value, then don't bother doing the actual store. */
if (get_register_status (regnum) == REG_VALID if (get_register_status (regnum) == REG_VALID
&& (memcmp (register_buffer (regnum), buf, && (memcmp (register_buffer (regnum).data (), src.data (), src.size ())
m_descr->sizeof_register[regnum]) == 0)) == 0))
return; return;
std::optional<scoped_restore_current_thread> maybe_restore_thread std::optional<scoped_restore_current_thread> maybe_restore_thread
= maybe_switch_inferior (m_inf_for_target_calls); = maybe_switch_inferior (m_inf_for_target_calls);
target_prepare_to_store (this); target_prepare_to_store (this);
raw_supply (regnum, buf); raw_supply (regnum, src);
/* Invalidate the register after it is written, in case of a /* Invalidate the register after it is written, in case of a
failure. */ failure. */
@@ -853,211 +894,248 @@ regcache::raw_write (int regnum, const gdb_byte *buf)
} }
void void
regcache::cooked_write (int regnum, const gdb_byte *buf) regcache::raw_write (int regnum, const gdb_byte *src)
{ {
gdb_assert (regnum >= 0); assert_regnum (regnum);
gdb_assert (regnum < m_descr->nr_cooked_registers);
if (regnum < num_raw_registers ()) int size = m_descr->sizeof_register[regnum];
raw_write (regnum, buf); raw_write (regnum, gdb::make_array_view (src, size));
else
gdbarch_pseudo_register_write (m_descr->gdbarch, this,
regnum, buf);
} }
/* See regcache.h. */ /* See regcache.h. */
enum register_status void
readable_regcache::read_part (int regnum, int offset, int len, regcache::cooked_write (int regnum, gdb::array_view<const gdb_byte> src)
gdb_byte *out, bool is_raw) {
gdb_assert (regnum >= 0);
gdb_assert (regnum < m_descr->nr_cooked_registers);
if (regnum < num_raw_registers ())
raw_write (regnum, src);
else
gdbarch_pseudo_register_write (m_descr->gdbarch, this, regnum,
src.data ());
}
/* See regcache.h. */
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); int reg_size = register_size (arch (), regnum);
gdb_assert (out != NULL);
gdb_assert (offset >= 0); 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. */ /* Nothing to do. */
return REG_VALID; return REG_VALID;
} }
if (len == reg_size) if (dst.size () == reg_size)
{ {
/* Read the full register. */ /* 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. */ /* 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) if (status != REG_VALID)
return status; return status;
/* Copy out. */ /* Copy out. */
memcpy (out, reg + offset, len); copy (reg.slice (offset, dst.size ()), dst);
return REG_VALID; return REG_VALID;
} }
/* See regcache.h. */ /* See regcache.h. */
void void
reg_buffer::raw_collect_part (int regnum, int offset, int len, reg_buffer::raw_collect_part (int regnum, int offset,
gdb_byte *out) const gdb::array_view<gdb_byte> dst) const
{ {
int reg_size = register_size (arch (), regnum); int reg_size = register_size (arch (), regnum);
gdb_assert (out != nullptr);
gdb_assert (offset >= 0); 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. */ /* Nothing to do. */
return; return;
} }
if (len == reg_size) if (dst.size () == reg_size)
{ {
/* Collect the full register. */ /* Collect the full register. */
return raw_collect (regnum, out); return raw_collect (regnum, dst);
} }
/* Read to buffer, then write out. */ /* 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); raw_collect (regnum, reg);
memcpy (out, reg + offset, len); copy (reg.slice (offset, dst.size ()), dst);
} }
/* See regcache.h. */ /* See regcache.h. */
enum register_status register_status
regcache::write_part (int regnum, int offset, int len, regcache::write_part (int regnum, int offset,
const gdb_byte *in, bool is_raw) gdb::array_view<const gdb_byte> src, bool is_raw)
{ {
int reg_size = register_size (arch (), regnum); int reg_size = register_size (arch (), regnum);
gdb_assert (in != NULL);
gdb_assert (offset >= 0); 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. */ /* Nothing to do. */
return REG_VALID; return REG_VALID;
} }
if (len == reg_size) if (src.size () == reg_size)
{ {
/* Write the full register. */ /* 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; return REG_VALID;
} }
enum register_status status;
gdb_byte *reg = (gdb_byte *) alloca (reg_size);
/* Read existing register to buffer. */ /* 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) if (status != REG_VALID)
return status; return status;
/* Update buffer, then write back to regcache. */ /* Update buffer, then write back to regcache. */
memcpy (reg + offset, in, len); copy (src, reg.slice (offset, src.size ()));
is_raw ? raw_write (regnum, reg) : cooked_write (regnum, reg);
if (is_raw)
raw_write (regnum, reg);
else
cooked_write (regnum, reg);
return REG_VALID; return REG_VALID;
} }
/* See regcache.h. */ /* See regcache.h. */
void void
reg_buffer::raw_supply_part (int regnum, int offset, int len, reg_buffer::raw_supply_part (int regnum, int offset,
const gdb_byte *in) gdb::array_view<const gdb_byte> src)
{ {
int reg_size = register_size (arch (), regnum); int reg_size = register_size (arch (), regnum);
gdb_assert (in != nullptr);
gdb_assert (offset >= 0); 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. */ /* Nothing to do. */
return; return;
} }
if (len == reg_size) if (src.size () == reg_size)
{ {
/* Supply the full register. */ /* 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. */ /* 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); raw_collect (regnum, reg);
/* Write to buffer, then write out. */ /* Write to buffer, then write out. */
memcpy (reg + offset, in, len); copy (src, reg.slice (offset, src.size ()));
raw_supply (regnum, reg); raw_supply (regnum, reg);
} }
enum register_status register_status
readable_regcache::raw_read_part (int regnum, int offset, int len, readable_regcache::raw_read_part (int regnum, int offset,
gdb_byte *buf) gdb::array_view<gdb_byte> dst)
{ {
assert_regnum (regnum); assert_regnum (regnum);
return read_part (regnum, offset, len, buf, true); return read_part (regnum, offset, dst, true);
} }
/* See regcache.h. */ /* See regcache.h. */
void void
regcache::raw_write_part (int regnum, int offset, int len, regcache::raw_write_part (int regnum, int offset,
const gdb_byte *buf) gdb::array_view<const gdb_byte> src)
{ {
assert_regnum (regnum); assert_regnum (regnum);
write_part (regnum, offset, len, buf, true); write_part (regnum, offset, src, true);
} }
/* See regcache.h. */ /* See regcache.h. */
enum register_status register_status
readable_regcache::cooked_read_part (int regnum, int offset, int len, readable_regcache::cooked_read_part (int regnum, int offset,
gdb_byte *buf) gdb::array_view<gdb_byte> dst)
{ {
gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers); 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. */ /* See regcache.h. */
void void
regcache::cooked_write_part (int regnum, int offset, int len, regcache::cooked_write_part (int regnum, int offset,
const gdb_byte *buf) gdb::array_view<const gdb_byte> src)
{ {
gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers); 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. */ /* See gdbsupport/common-regcache.h. */
void 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; gdb::array_view<gdb_byte> dst = register_buffer (regnum);
size_t size;
assert_regnum (regnum); if (src.data () != nullptr)
regbuf = register_buffer (regnum);
size = m_descr->sizeof_register[regnum];
if (buf)
{ {
memcpy (regbuf, buf, size); copy (src, dst);
m_register_status[regnum] = REG_VALID; m_register_status[regnum] = REG_VALID;
} }
else else
@@ -1065,7 +1143,7 @@ reg_buffer::raw_supply (int regnum, const void *buf)
/* This memset not strictly necessary, but better than garbage /* This memset not strictly necessary, but better than garbage
in case the register value manages to escape somewhere (due in case the register value manages to escape somewhere (due
to a bug, no less). */ to a bug, no less). */
memset (regbuf, 0, size); memset (dst.data (), 0, dst.size ());
m_register_status[regnum] = REG_UNAVAILABLE; m_register_status[regnum] = REG_UNAVAILABLE;
} }
} }
@@ -1073,19 +1151,24 @@ reg_buffer::raw_supply (int regnum, const void *buf)
/* See regcache.h. */ /* See regcache.h. */
void void
reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr, reg_buffer::raw_supply (int regnum, const void *src)
int addr_len, bool is_signed)
{ {
enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
gdb_byte *regbuf;
size_t regsize;
assert_regnum (regnum); assert_regnum (regnum);
regbuf = register_buffer (regnum); int size = m_descr->sizeof_register[regnum];
regsize = m_descr->sizeof_register[regnum]; raw_supply (regnum, gdb::make_array_view ((const gdb_byte *) src, size));
}
copy_integer_to_size (regbuf, regsize, addr, addr_len, is_signed, /* See regcache.h. */
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); byte_order);
m_register_status[regnum] = REG_VALID; m_register_status[regnum] = REG_VALID;
} }
@@ -1095,32 +1178,29 @@ reg_buffer::raw_supply_integer (int regnum, const gdb_byte *addr,
void void
reg_buffer::raw_supply_zeroed (int regnum) reg_buffer::raw_supply_zeroed (int regnum)
{ {
void *regbuf; gdb::array_view<gdb_byte> dst = register_buffer (regnum);
size_t size; memset (dst.data (), 0, dst.size ());
assert_regnum (regnum);
regbuf = register_buffer (regnum);
size = m_descr->sizeof_register[regnum];
memset (regbuf, 0, size);
m_register_status[regnum] = REG_VALID; m_register_status[regnum] = REG_VALID;
} }
/* See gdbsupport/common-regcache.h. */ /* See gdbsupport/common-regcache.h. */
void 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; gdb::array_view<const gdb_byte> src = register_buffer (regnum);
size_t size; copy (src, dst);
}
gdb_assert (buf != NULL); /* See regcache.h. */
void
reg_buffer::raw_collect (int regnum, void *dst) const
{
assert_regnum (regnum); assert_regnum (regnum);
regbuf = register_buffer (regnum); int size = m_descr->sizeof_register[regnum];
size = m_descr->sizeof_register[regnum]; return raw_collect (regnum, gdb::make_array_view ((gdb_byte *) dst, size));
memcpy (buf, regbuf, size);
} }
/* See regcache.h. */ /* See regcache.h. */
@@ -1129,16 +1209,9 @@ void
reg_buffer::raw_collect_integer (int regnum, gdb_byte *addr, int addr_len, reg_buffer::raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
bool is_signed) const bool is_signed) const
{ {
enum bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch); gdb::array_view<const gdb_byte> dst = register_buffer (regnum);
const gdb_byte *regbuf; bfd_endian byte_order = gdbarch_byte_order (m_descr->gdbarch);
size_t regsize; copy_integer_to_size (addr, addr_len, dst.data (), dst.size (), is_signed,
assert_regnum (regnum);
regbuf = register_buffer (regnum);
regsize = m_descr->sizeof_register[regnum];
copy_integer_to_size (addr, addr_len, regbuf, regsize, is_signed,
byte_order); byte_order);
} }
@@ -1157,7 +1230,8 @@ regcache::transfer_regset_register (struct regcache *out_regcache, int regnum,
if (out_buf != nullptr) 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. */ /* Ensure any additional space is cleared. */
if (slot_size > reg_size) 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. */ /* Zero-extend the register value if the slot is smaller than the register. */
if (slot_size < register_size (gdbarch, regnum)) if (slot_size < register_size (gdbarch, regnum))
out_regcache->raw_supply_zeroed (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 else
{ {
/* Invalidate the register. */ /* 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 reg_buffer::raw_compare (int regnum, const void *buf, int offset) const
{ {
gdb_assert (buf != NULL); gdb_assert (buf != NULL);
assert_regnum (regnum);
const char *regbuf = (const char *) register_buffer (regnum); gdb::array_view<const gdb_byte> regbuf = register_buffer (regnum);
size_t size = m_descr->sizeof_register[regnum]; gdb_assert (offset <= regbuf.size ());
gdb_assert (size >= offset); 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. */ /* 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)) if (regno >= 0 && regno < gdbarch_num_regs (gdbarch))
{ {
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
int size = register_size (gdbarch, regno); gdb::array_view<gdb_byte> buf = register_buffer (regno);
gdb_byte *buf = register_buffer (regno);
gdb_printf (gdb_stdlog, " = "); gdb_printf (gdb_stdlog, " = ");
for (int i = 0; i < size; i++) for (gdb_byte byte : buf)
gdb_printf (gdb_stdlog, "%02x", byte);
if (buf.size () <= sizeof (LONGEST))
{ {
gdb_printf (gdb_stdlog, "%02x", buf[i]); ULONGEST val = extract_unsigned_integer (buf, byte_order);
}
if (size <= sizeof (LONGEST))
{
ULONGEST val = extract_unsigned_integer (buf, size, byte_order);
gdb_printf (gdb_stdlog, " %s %s", gdb_printf (gdb_stdlog, " %s %s",
core_addr_to_string_nz (val), plongest (val)); core_addr_to_string_nz (val), plongest (val));
@@ -1758,6 +1831,23 @@ registers_changed_ptid_target_ptid_test ()
ptid_t (2, 2)) == 1); 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 class target_ops_no_register : public test_target_ops
{ {
public: public:
@@ -1869,7 +1959,7 @@ cooked_read_test (struct gdbarch *gdbarch)
readwrite.set_ptid (mockctx.mock_ptid); readwrite.set_ptid (mockctx.mock_ptid);
gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum)); gdb::byte_vector buf (register_size (gdbarch, nonzero_regnum));
readwrite.raw_read (nonzero_regnum, buf.data ()); readwrite.raw_read (nonzero_regnum, buf);
/* raw_read calls target_fetch_registers. */ /* raw_read calls target_fetch_registers. */
SELF_CHECK (mockctx.mock_target.fetch_registers_called > 0); 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)); gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum, SELF_CHECK (REG_VALID == readwrite.cooked_read (regnum, inner_buf));
inner_buf.data ()));
SELF_CHECK (mockctx.mock_target.fetch_registers_called == 0); SELF_CHECK (mockctx.mock_target.fetch_registers_called == 0);
SELF_CHECK (mockctx.mock_target.store_registers_called == 0); SELF_CHECK (mockctx.mock_target.store_registers_called == 0);
SELF_CHECK (mockctx.mock_target.xfer_partial_called == 0); SELF_CHECK (mockctx.mock_target.xfer_partial_called == 0);
@@ -1912,8 +2000,7 @@ cooked_read_test (struct gdbarch *gdbarch)
continue; continue;
gdb::byte_vector inner_buf (register_size (gdbarch, regnum)); gdb::byte_vector inner_buf (register_size (gdbarch, regnum));
enum register_status status = readonly.cooked_read (regnum, register_status status = readonly.cooked_read (regnum, inner_buf);
inner_buf.data ());
if (regnum < gdbarch_num_regs (gdbarch)) if (regnum < gdbarch_num_regs (gdbarch))
{ {
@@ -2003,8 +2090,8 @@ cooked_write_test (struct gdbarch *gdbarch)
&& regnum <= gdbarch_num_regs (gdbarch) + 4)) && regnum <= gdbarch_num_regs (gdbarch) + 4))
continue; continue;
std::vector<gdb_byte> expected (register_size (gdbarch, regnum), 0); gdb::byte_vector expected (register_size (gdbarch, regnum), 0);
std::vector<gdb_byte> buf (register_size (gdbarch, regnum), 0); gdb::byte_vector buf (register_size (gdbarch, regnum), 0);
const auto type = register_type (gdbarch, regnum); const auto type = register_type (gdbarch, regnum);
if (type->code () == TYPE_CODE_FLT if (type->code () == TYPE_CODE_FLT
@@ -2059,9 +2146,9 @@ cooked_write_test (struct gdbarch *gdbarch)
SELF_CHECK (0); 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); SELF_CHECK (expected == buf);
} }
} }
@@ -2154,6 +2241,8 @@ _initialize_regcache ()
selftests::registers_changed_ptid_target_pid_test); selftests::registers_changed_ptid_target_pid_test);
selftests::register_test ("registers_changed_ptid_target_ptid", selftests::register_test ("registers_changed_ptid_target_ptid",
selftests::registers_changed_ptid_target_ptid_test); 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::register_test_foreach_arch ("regcache::cooked_read_test",
selftests::cooked_read_test); selftests::cooked_read_test);

View File

@@ -20,6 +20,7 @@
#ifndef REGCACHE_H #ifndef REGCACHE_H
#define REGCACHE_H #define REGCACHE_H
#include "gdbsupport/array-view.h"
#include "gdbsupport/common-regcache.h" #include "gdbsupport/common-regcache.h"
#include "gdbsupport/function-view.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); extern int register_size (struct gdbarch *gdbarch, int regnum);
typedef gdb::function_view<register_status (int regnum, gdb_byte *buf)> using register_read_ftype
register_read_ftype; = gdb::function_view<register_status (int, gdb::array_view<gdb_byte>)>;
/* A (register_number, register_value) pair. */ /* A (register_number, register_value) pair. */
@@ -194,7 +195,10 @@ public:
enum register_status get_register_status (int regnum) const override; enum register_status get_register_status (int regnum) const override;
/* See gdbsupport/common-regcache.h. */ /* 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 /* 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. at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
@@ -204,17 +208,23 @@ public:
void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len, void raw_collect_integer (int regnum, gdb_byte *addr, int addr_len,
bool is_signed) const; bool is_signed) const;
/* Collect register REGNUM from REGCACHE, starting at OFFSET in register, /* Collect part of register REGNUM from this register buffer. Start at OFFSET
reading only LEN. */ in register. The size is given by the size of DST. */
void raw_collect_part (int regnum, int offset, int len, gdb_byte *out) const; 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. */ /* 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) 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 /* 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. at address ADDR, in target endian, with length ADDR_LEN and sign IS_SIGNED.
@@ -229,9 +239,11 @@ public:
unavailable). */ unavailable). */
void raw_supply_zeroed (int regnum); void raw_supply_zeroed (int regnum);
/* Supply register REGNUM to REGCACHE, starting at OFFSET in register, writing /* Supply part of register REGNUM to this register buffer. Start at OFFSET in
only LEN, without editing the rest of the register. */ the register. The size is given by the size of SRC. The rest of the
void raw_supply_part (int regnum, int offset, int len, const gdb_byte *in); register left untouched. */
void raw_supply_part (int regnum, int offset,
gdb::array_view<const gdb_byte> src);
void invalidate (int regnum); void invalidate (int regnum);
@@ -246,7 +258,11 @@ protected:
int num_raw_registers () const; 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 /* Save a register cache. The set of registers saved into the
regcache determined by the save_reggroup. COOKED_READ returns regcache determined by the save_reggroup. COOKED_READ returns
@@ -276,27 +292,41 @@ public:
/* Transfer a raw register [0..NUM_REGS) from core-gdb to this regcache, /* Transfer a raw register [0..NUM_REGS) from core-gdb to this regcache,
return its value in *BUF and return its availability status. */ 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>> 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. */ /* Partial transfer of raw registers. Return the status of the register. */
enum register_status raw_read_part (int regnum, int offset, int len, register_status raw_read_part (int regnum, int offset,
gdb_byte *buf); 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. */ /* Make certain that the register REGNUM is up-to-date. */
virtual void raw_update (int regnum) = 0; virtual void raw_update (int regnum) = 0;
/* Transfer a raw register [0..NUM_REGS+NUM_PSEUDO_REGS) from core-gdb to /* 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. */ this regcache, return its value in DST and return its availability status. */
enum register_status cooked_read (int regnum, gdb_byte *buf); 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>> 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. */ /* Partial transfer of a cooked register. */
enum register_status cooked_read_part (int regnum, int offset, int len, register_status cooked_read_part (int regnum, int offset,
gdb_byte *buf); 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 /* Read register REGNUM from the regcache and return a new value. This
will call mark_value_bytes_unavailable as appropriate. */ will call mark_value_bytes_unavailable as appropriate. */
@@ -306,8 +336,8 @@ protected:
/* Perform a partial register transfer using a read, modify, write /* Perform a partial register transfer using a read, modify, write
operation. Will fail if register is currently invalid. */ operation. Will fail if register is currently invalid. */
enum register_status read_part (int regnum, int offset, int len, register_status read_part (int regnum, int offset,
gdb_byte *out, bool is_raw); gdb::array_view<gdb_byte> dst, bool is_raw);
}; };
/* Buffer of registers, can be read and written. */ /* Buffer of registers, can be read and written. */
@@ -343,13 +373,19 @@ public:
/* Update the value of raw register REGNUM (in the range [0..NUM_REGS)) and /* Update the value of raw register REGNUM (in the range [0..NUM_REGS)) and
transfer its value to core-gdb. */ 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>> template<typename T, typename = RequireLongest<T>>
void raw_write (int regnum, T val); void raw_write (int regnum, T val);
/* Transfer of pseudo-registers. */ /* 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>> template<typename T, typename = RequireLongest<T>>
void cooked_write (int regnum, T val); void cooked_write (int regnum, T val);
@@ -358,12 +394,21 @@ public:
/* Partial transfer of raw registers. Perform read, modify, write style /* Partial transfer of raw registers. Perform read, modify, write style
operations. */ 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 /* Partial transfer of a cooked register. Perform read, modify, write style
operations. */ operations. */
void cooked_write_part (int regnum, int offset, int len, void cooked_write_part (int regnum, int offset,
const gdb_byte *buf); 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 /* Transfer a set of registers (as described by REGSET) between
REGCACHE and BUF. If REGNUM == -1, transfer all registers REGCACHE and BUF. If REGNUM == -1, transfer all registers
@@ -430,8 +475,9 @@ private:
/* Perform a partial register transfer using a read, modify, write /* Perform a partial register transfer using a read, modify, write
operation. */ operation. */
enum register_status write_part (int regnum, int offset, int len, register_status write_part (int regnum, int offset,
const gdb_byte *in, bool is_raw); gdb::array_view<const gdb_byte> src,
bool is_raw);
/* The inferior to switch to, to make target calls. /* The inferior to switch to, to make target calls.

View File

@@ -315,27 +315,32 @@ regcache_register_size (const reg_buffer_common *regcache, int n)
(gdb::checked_static_cast<const struct regcache *> (regcache)->tdesc, 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) register_data (const struct regcache *regcache, int n)
{ {
return (regcache->registers const gdb::reg &reg = find_register_by_number (regcache->tdesc, n);
+ find_register_by_number (regcache->tdesc, n).offset / 8); return gdb::make_array_view (regcache->registers + reg.offset / 8,
reg.size / 8);
} }
void 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. */ /* See gdbsupport/common-regcache.h. */
void 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 #ifndef IN_PROCESS_AGENT
if (register_status != NULL) if (register_status != NULL)
register_status[n] = REG_VALID; register_status[n] = REG_VALID;
@@ -343,7 +348,7 @@ regcache::raw_supply (int n, const void *buf)
} }
else else
{ {
memset (register_data (this, n), 0, register_size (tdesc, n)); memset (dst.data (), 0, dst.size ());
#ifndef IN_PROCESS_AGENT #ifndef IN_PROCESS_AGENT
if (register_status != NULL) if (register_status != NULL)
register_status[n] = REG_UNAVAILABLE; register_status[n] = REG_UNAVAILABLE;
@@ -356,8 +361,8 @@ regcache::raw_supply (int n, const void *buf)
void void
supply_register_zeroed (struct regcache *regcache, int n) supply_register_zeroed (struct regcache *regcache, int n)
{ {
memset (register_data (regcache, n), 0, auto dst = register_data (regcache, n);
register_size (regcache->tdesc, n)); memset (dst.data (), 0, dst.size ());
#ifndef IN_PROCESS_AGENT #ifndef IN_PROCESS_AGENT
if (regcache->register_status != NULL) if (regcache->register_status != NULL)
regcache->register_status[n] = REG_VALID; regcache->register_status[n] = REG_VALID;
@@ -426,17 +431,20 @@ supply_register_by_name (struct regcache *regcache,
#endif #endif
void 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. */ /* See gdbsupport/common-regcache.h. */
void 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 enum register_status
@@ -476,8 +484,7 @@ regcache_raw_get_unsigned_by_name (struct regcache *regcache,
void void
collect_register_as_string (struct regcache *regcache, int n, char *buf) collect_register_as_string (struct regcache *regcache, int n, char *buf)
{ {
bin2hex (register_data (regcache, n), buf, bin2hex (register_data (regcache, n), buf);
register_size (regcache->tdesc, n));
} }
void void
@@ -524,9 +531,9 @@ regcache::raw_compare (int regnum, const void *buf, int offset) const
{ {
gdb_assert (buf != NULL); gdb_assert (buf != NULL);
const unsigned char *regbuf = register_data (this, regnum); gdb::array_view<const gdb_byte> regbuf = register_data (this, regnum);
int size = register_size (tdesc, regnum); gdb_assert (offset < regbuf.size ());
gdb_assert (size >= offset); regbuf = regbuf.slice (offset);
return (memcmp (buf, regbuf + offset, size - offset) == 0); return memcmp (buf, regbuf.data (), regbuf.size ()) == 0;
} }

View File

@@ -50,10 +50,10 @@ struct regcache : public reg_buffer_common
enum register_status get_register_status (int regnum) const override; enum register_status get_register_status (int regnum) const override;
/* See gdbsupport/common-regcache.h. */ /* 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. */ /* 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. */ /* See gdbsupport/common-regcache.h. */
bool raw_compare (int regnum, const void *buf, int offset) const override; bool raw_compare (int regnum, const void *buf, int offset) const override;

View File

@@ -78,11 +78,41 @@ struct reg_buffer_common
buffer. */ buffer. */
virtual register_status get_register_status (int regnum) const = 0; virtual register_status get_register_status (int regnum) const = 0;
/* Supply register REGNUM, whose contents are stored in BUF, to REGCACHE. */ /* Supply register REGNUM, whose contents are stored in SRC, to this register
virtual void raw_supply (int regnum, const void *buf) = 0; buffer. */
virtual void raw_supply (int regnum, gdb::array_view<const gdb_byte> src)
= 0;
/* Collect register REGNUM from REGCACHE and store its contents in BUF. */ void raw_supply (int regnum, const uint64_t *src)
virtual void raw_collect (int regnum, void *buf) const = 0; {
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 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 /* Compare the contents of the register stored in the regcache (ignoring the
first OFFSET bytes) to the contents of BUF (without any offset). Returns first OFFSET bytes) to the contents of BUF (without any offset). Returns

View File

@@ -143,6 +143,14 @@ bin2hex (const gdb_byte *bin, char *hex, int count)
/* See rsp-low.h. */ /* 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 std::string
bin2hex (const gdb_byte *bin, int count) bin2hex (const gdb_byte *bin, int count)
{ {

View File

@@ -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 (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. */ /* Overloaded version of bin2hex that returns a std::string. */
extern std::string bin2hex (const gdb_byte *bin, int count); extern std::string bin2hex (const gdb_byte *bin, int count);