namespace gdb {
+/* Copied from gdbsupport/tdesc.h because I couldn't find an appropriate
+ header that would work for gdbserver/regcache.cc. */
+
+#define TDESC_REG_VARIABLE_SIZE -1
+
struct reg
{
reg (int _offset)
reg (const char *_name, int _offset, int _size)
: name (_name),
offset (_offset),
- size (_size)
+ size (_size),
+ bitsize_parameter (UINT_MAX)
+ {}
+
+ reg (const char *_name, int _offset, unsigned int _bitsize_parameter)
+ : name (_name),
+ offset (_offset),
+ size (TDESC_REG_VARIABLE_SIZE),
+ bitsize_parameter (_bitsize_parameter)
{}
/* The name of this register - NULL for pad entries. */
/* The offset (in bits) of the value of this register in the buffer. */
int offset;
- /* The size (in bits) of the value of this register, as transmitted. */
+ /* The size (in bits) of the value of this register, as transmitted. If
+ it's TDESC_REG_VARIABLE_SIZE then this register contains a vector with
+ total vector size given by BITSIZE_PARAMETER, and each element has a
+ size of ELEMENT_BITSIZE. */
int size;
+ /* Size of each element in the vector. Only valid if
+ size == TDESC_REG_VARIABLE_SIZE. */
+ unsigned int element_bitsize;
+
+ /* Target description parameter giving the bitsize of the vector. Only
+ valid if size == TDESC_REG_VARIABLE_SIZE. */
+ unsigned int bitsize_parameter;
+
bool operator== (const reg &other) const
{
return (strcmp (name, other.name) == 0
&& offset == other.offset
- && size == other.size);
+ && size == other.size
+ && bitsize_parameter == other.bitsize_parameter);
}
bool operator!= (const reg &other) const
#endif
regcache::regcache (const target_desc *tdesc, unsigned char *regbuf)
- : tdesc (tdesc), registers (regbuf)
+ : tdesc (tdesc), fixed_size_registers (regbuf)
{
gdb_assert (regbuf != nullptr);
}
regcache::regcache (const target_desc *tdesc)
: tdesc (tdesc), registers_owned (true)
{
- gdb_assert (tdesc->registers_size != 0);
+ gdb_assert (tdesc->fixed_registers_size != 0);
/* Make sure to zero-initialize the register cache when it is
created, in case there are registers the target never
fetches. This way they'll read as zero instead of
garbage. */
- this->registers
- = (unsigned char *) xmalloc (tdesc->registers_size);
+ this->fixed_size_registers
+ = (unsigned char *) xmalloc (tdesc->fixed_registers_size);
size_t num_regs = tdesc->reg_defs.size ();
m_register_status.reset (new enum register_status[num_regs]);
reset (REG_UNKNOWN);
regcache::~regcache ()
{
if (registers_owned)
- free (registers);
+ free (fixed_size_registers);
}
#endif
/* Zero-initialize the register cache, in case there are registers
the target never fetches. This way they'll read as zero instead
of garbage. */
- memset (this->registers, 0, this->tdesc->registers_size);
+ memset (this->fixed_size_registers, 0, this->tdesc->fixed_registers_size);
#ifndef IN_PROCESS_AGENT
+ this->variable_size_registers.reset ();
+
for (int i = 0; i < this->tdesc->reg_defs.size (); i++)
set_register_status (i, status);
+
+ invalidate_variable_size_registers ();
#endif
}
gdb_assert (src->tdesc == this->tdesc);
gdb_assert (src != this);
- memcpy (this->registers, src->registers, src->tdesc->registers_size);
+ memcpy (this->fixed_size_registers, src->fixed_size_registers, src->tdesc->fixed_registers_size);
#ifndef IN_PROCESS_AGENT
if (m_register_status != nullptr && src->m_register_status != nullptr)
memcpy (m_register_status.get (), src->m_register_status.get (),
for (int i = 0; i < tdesc->reg_defs.size (); ++i)
{
collect_register_as_string (regcache, i, buf);
- buf += register_size (tdesc, i) * 2;
+ buf += regcache->register_size (i) * 2;
}
}
registers_from_string (struct regcache *regcache, char *buf)
{
int len = strlen (buf);
- unsigned char *registers = regcache->registers;
+ unsigned char *registers = regcache->fixed_size_registers;
+ unsigned char *var_size_registers = regcache->variable_size_registers.get ();
const struct target_desc *tdesc = regcache->tdesc;
+ int expected_len = (tdesc->fixed_registers_size + regcache->variable_registers_size) * 2;
- if (len != tdesc->registers_size * 2)
+ if (len != expected_len)
{
warning ("Wrong sized register packet (expected %d bytes, got %d)",
- 2 * tdesc->registers_size, len);
- if (len > tdesc->registers_size * 2)
- len = tdesc->registers_size * 2;
+ expected_len, len);
+ if (len > expected_len)
+ len = expected_len;
+ }
+
+ for (int i = 0; i < tdesc->reg_defs.size (); ++i)
+ {
+ int reg_size = regcache->register_size (i);
+ unsigned char *regs;
+
+ if (tdesc->reg_defs[i].size == TDESC_REG_VARIABLE_SIZE)
+ regs = var_size_registers;
+ else
+ regs = registers;
+
+ hex2bin (buf, regs, reg_size);
+ buf += reg_size * 2;
}
- hex2bin (buf, registers, len / 2);
}
/* See regcache.h */
}
#endif
+#ifndef IN_PROCESS_AGENT
+/* See regcache.h */
+
+void
+regcache::initialize_variable_size_registers ()
+{
+ const unsigned int num_regs = tdesc->reg_defs.size ();
+ int total_size = 0;
+
+ variable_size_sizes.resize (num_regs);
+ variable_size_offsets.resize (num_regs);
+
+ for (int i = 0; i < num_regs; i++)
+ {
+ const gdb::reg ® = tdesc->reg_defs[i];
+ if (reg.size != TDESC_REG_VARIABLE_SIZE)
+ {
+ variable_size_sizes[i] = -1;
+ variable_size_offsets[i] = -1;
+ continue;
+ }
+
+ long size = target_desc_parameter_value (this, reg.bitsize_parameter);
+ variable_size_sizes[i] = size;
+ variable_size_offsets[i] = total_size;
+ total_size += size;
+ }
+
+ variable_size_registers.reset (new unsigned char[total_size]);
+ variable_registers_size = total_size;
+}
+
+/* See regcache.h */
+
+void
+regcache::invalidate_variable_size_registers ()
+{
+ /* There's nothing to do if there are already no variable-size register
+ contents. */
+ if (variable_size_registers == nullptr)
+ return;
+
+ for (unsigned int i = 0; i < tdesc->reg_defs.size (); i++)
+ {
+ const gdb::reg ® = tdesc->reg_defs[i];
+ if (reg.size == TDESC_REG_VARIABLE_SIZE)
+ set_register_status (i, REG_UNKNOWN);
+ }
+
+ variable_size_registers = nullptr;
+ variable_size_sizes.clear ();
+ variable_size_offsets.clear ();
+}
+#endif
+
int
register_cache_size (const struct target_desc *tdesc)
{
- return tdesc->registers_size;
+ /* Variable-size registers aren't considered here because this function is
+ only used for tracepoints. */
+ return tdesc->fixed_registers_size;
}
int
register_size (const struct target_desc *tdesc, int n)
{
- return find_register_by_number (tdesc, n).size / 8;
+ const gdb::reg ® = find_register_by_number (tdesc, n);
+
+ gdb_assert (reg.size != TDESC_REG_VARIABLE_SIZE);
+
+ return reg.size / 8;
}
/* See gdbsupport/common-regcache.h. */
int
regcache::register_size (int regnum) const
{
- return ::register_size (tdesc, regnum);
+ const gdb::reg ® = find_register_by_number (tdesc, regnum);
+
+ if (reg.size == TDESC_REG_VARIABLE_SIZE)
+ {
+#ifndef IN_PROCESS_AGENT
+ if (!variable_size_registers)
+ // FIXME: Remove cast.
+ const_cast<struct regcache *> (this)->initialize_variable_size_registers ();
+
+ return variable_size_sizes[regnum];
+#else
+ gdb_assert_not_reached ("Variable-size registers not supported.");
+#endif
+ }
+ else
+ return reg.size / 8;
}
static gdb::array_view<gdb_byte>
register_data (const struct regcache *regcache, int n)
{
const gdb::reg ® = find_register_by_number (regcache->tdesc, n);
- return gdb::make_array_view (regcache->registers + reg.offset / 8,
- reg.size / 8);
+
+ if (reg.size == TDESC_REG_VARIABLE_SIZE)
+ {
+#ifndef IN_PROCESS_AGENT
+ if (!regcache->variable_size_registers)
+ // FIXME: Remove cast.
+ const_cast<struct regcache *> (regcache)->initialize_variable_size_registers ();
+
+ return gdb::make_array_view (regcache->variable_size_registers.get ()
+ + regcache->variable_size_offsets[n],
+ regcache->variable_size_sizes[n]);
+#else
+ gdb_assert_not_reached ("Variable-size registers not supported.");
+#endif
+ }
+ else
+ return gdb::make_array_view (regcache->fixed_size_registers + reg.offset / 8,
+ reg.size / 8);
}
void
set_register_status (n, REG_UNAVAILABLE);
#endif
}
+
+#ifndef IN_PROCESS_AGENT
+ if (target_is_register_relevant_for_tdesc_parameter (this->tdesc, n))
+ /* Invalidate variable-size registers. */
+ this->invalidate_variable_size_registers ();
+#endif
}
/* Supply register N with value zero to REGCACHE. */
gdb_assert (buf != nullptr);
const struct target_desc *tdesc = regcache->tdesc;
- memcpy (regcache->registers, buf, tdesc->registers_size);
+ memcpy (regcache->fixed_size_registers, buf, tdesc->fixed_registers_size);
#ifndef IN_PROCESS_AGENT
for (int i = 0; i < tdesc->reg_defs.size (); i++)
regcache->set_register_status (i, REG_VALID);
gdb_assert (regcache != NULL);
- size = register_size (regcache->tdesc, regnum);
+ size = regcache->register_size (regnum);
if (size > (int) sizeof (ULONGEST))
error (_("That operation is not available on integers of more than"
void
collect_register_as_string (struct regcache *regcache, int n, char *buf)
{
- int reg_size = register_size (regcache->tdesc, n);
+ int reg_size = regcache->register_size (n);
if (regcache->get_register_status (n) == REG_VALID)
bin2hex (register_data (regcache, n), buf);
return tdesc->parameters[param_id].size;
}
+/* Sets up REG's size information using TYPE. */
+
+static void
+setup_variable_size_reg (const target_desc *tdesc, tdesc_type *type,
+ gdb::reg ®)
+{
+ /* We only support vector types or unions containing vector types as
+ variable-size. */
+ gdb_assert (type->kind == TDESC_TYPE_UNION
+ || type->kind == TDESC_TYPE_VECTOR);
+
+ if (type->kind == TDESC_TYPE_VECTOR)
+ {
+ tdesc_type_vector *vec_type = static_cast<tdesc_type_vector *> (type);
+
+ reg.element_bitsize = tdesc_type_bitsize (vec_type->element_type);
+ std::optional<unsigned int> maybe_param_id
+ = tdesc_parameter_id (tdesc, vec_type->bitsize_parameter->feature.c_str (),
+ vec_type->bitsize_parameter->name.c_str ());
+ gdb_assert (maybe_param_id.has_value ());
+
+ reg.bitsize_parameter = *maybe_param_id;
+ }
+ else
+ {
+ tdesc_type_with_fields *union_type
+ = static_cast<tdesc_type_with_fields *> (type);
+
+ /* We assume that all fields in the union have the same size, so
+ just get the first one. */
+ setup_variable_size_reg (tdesc, union_type->fields.front ().type, reg);
+ }
+}
+
void
init_target_desc (struct target_desc *tdesc,
const char **expedite_regs,
tdesc->reg_defs.emplace_back (treg->name.c_str (), offset,
treg->bitsize);
- offset += treg->bitsize;
+
+ if (treg->bitsize == TDESC_REG_VARIABLE_SIZE)
+ {
+ tdesc_type *type
+ = tdesc_named_type (feature.get (), treg->type.c_str ());
+
+ setup_variable_size_reg (tdesc, type, tdesc->reg_defs.back ());
+ }
+ else
+ offset += treg->bitsize;
}
}
- tdesc->registers_size = offset / 8;
+ tdesc->fixed_registers_size = offset / 8;
/* Make sure PBUFSIZ is large enough to hold a full register
packet. */
- gdb_assert (2 * tdesc->registers_size + 32 <= PBUFSIZ);
+ gdb_assert (2 * tdesc->fixed_registers_size + 32 <= PBUFSIZ);
#ifndef IN_PROCESS_AGENT
/* Drop the contents of the previous vector, if any. */
{
dest->reg_defs = src->reg_defs;
dest->expedite_regs = src->expedite_regs;
- dest->registers_size = src->registers_size;
+ dest->fixed_registers_size = src->fixed_registers_size;
dest->xmltarget = src->xmltarget;
}