/* Cache and manage the values of registers for GDB, the GNU debugger.
- Copyright (C) 1986, 1987, 1989, 1991, 1994, 1995, 1996, 1998, 2000, 2001,
- 2002, 2004, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1986-2015 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbcmd.h"
#include "regcache.h"
#include "reggroups.h"
-#include "gdb_assert.h"
-#include "gdb_string.h"
-#include "gdbcmd.h" /* For maintenanceprintlist. */
#include "observer.h"
-#include "exceptions.h"
+#include "remote.h"
+#include "valprint.h"
+#include "regset.h"
/*
* DATA STRUCTURE
long sizeof_cooked_registers;
long sizeof_cooked_register_status;
- /* Offset and size (in 8 bit bytes), of reach register in the
+ /* Offset and size (in 8 bit bytes), of each register in the
register cache. All registers (including those in the range
[NR_RAW_REGISTERS .. NR_COOKED_REGISTERS) are given an
offset. */
gdb_assert (gdbarch != NULL);
descr = regcache_descr (gdbarch);
- regcache = XMALLOC (struct regcache);
+ regcache = XNEW (struct regcache);
regcache->descr = descr;
regcache->readonly_p = readonly_p;
if (readonly_p)
{
regcache->registers
- = XCALLOC (descr->sizeof_cooked_registers, gdb_byte);
+ = XCNEWVEC (gdb_byte, descr->sizeof_cooked_registers);
regcache->register_status
- = XCALLOC (descr->sizeof_cooked_register_status, gdb_byte);
+ = XCNEWVEC (signed char, descr->sizeof_cooked_register_status);
}
else
{
regcache->registers
- = XCALLOC (descr->sizeof_raw_registers, gdb_byte);
+ = XCNEWVEC (gdb_byte, descr->sizeof_raw_registers);
regcache->register_status
- = XCALLOC (descr->sizeof_raw_register_status, gdb_byte);
+ = XCNEWVEC (signed char, descr->sizeof_raw_register_status);
}
regcache->aspace = aspace;
regcache->ptid = minus_one_ptid;
return make_cleanup (do_regcache_xfree, regcache);
}
+/* Cleanup routines for invalidating a register. */
+
+struct register_to_invalidate
+{
+ struct regcache *regcache;
+ int regnum;
+};
+
+static void
+do_regcache_invalidate (void *data)
+{
+ struct register_to_invalidate *reg = data;
+
+ regcache_invalidate (reg->regcache, reg->regnum);
+}
+
+static struct cleanup *
+make_cleanup_regcache_invalidate (struct regcache *regcache, int regnum)
+{
+ struct register_to_invalidate* reg = XNEW (struct register_to_invalidate);
+
+ reg->regcache = regcache;
+ reg->regnum = regnum;
+ return make_cleanup_dtor (do_regcache_invalidate, (void *) reg, xfree);
+}
+
/* Return REGCACHE's architecture. */
struct gdbarch *
}
}
-void
+static void
regcache_restore (struct regcache *dst,
regcache_cooked_read_ftype *cooked_read,
void *cooked_read_context)
{
if (gdbarch_register_reggroup_p (gdbarch, regnum, restore_reggroup))
{
- int valid = cooked_read (cooked_read_context, regnum, buf);
+ enum register_status status;
- if (valid)
+ status = cooked_read (cooked_read_context, regnum, buf);
+ if (status == REG_VALID)
regcache_cooked_write (dst, regnum, buf);
}
}
return regcache_cooked_read (regcache, regnum, buf);
}
+static void regcache_cpy_no_passthrough (struct regcache *dst,
+ struct regcache *src);
+
void
regcache_cpy (struct regcache *dst, struct regcache *src)
{
regcache_cpy_no_passthrough (dst, src);
}
-void
+/* Copy/duplicate the contents of a register cache. Unlike regcache_cpy,
+ which is pass-through, this does not go through to the target.
+ Only values values already in the cache are transferred. The SRC and DST
+ buffers must not overlap. */
+
+static void
regcache_cpy_no_passthrough (struct regcache *dst, struct regcache *src)
{
gdb_assert (src != NULL && dst != NULL);
return newbuf;
}
-int
+enum register_status
regcache_register_status (const struct regcache *regcache, int regnum)
{
gdb_assert (regcache != NULL);
else
gdb_assert (regnum < regcache->descr->nr_raw_registers);
- return regcache->register_status[regnum];
+ return (enum register_status) regcache->register_status[regnum];
}
void
static struct regcache_list *current_regcache;
struct regcache *
-get_thread_arch_regcache (ptid_t ptid, struct gdbarch *gdbarch)
+get_thread_arch_aspace_regcache (ptid_t ptid, struct gdbarch *gdbarch,
+ struct address_space *aspace)
{
struct regcache_list *list;
struct regcache *new_regcache;
&& get_regcache_arch (list->regcache) == gdbarch)
return list->regcache;
- new_regcache = regcache_xmalloc_1 (gdbarch,
- target_thread_address_space (ptid), 0);
+ new_regcache = regcache_xmalloc_1 (gdbarch, aspace, 0);
new_regcache->ptid = ptid;
- gdb_assert (new_regcache->aspace != NULL);
- list = xmalloc (sizeof (struct regcache_list));
+ list = XNEW (struct regcache_list);
list->regcache = new_regcache;
list->next = current_regcache;
current_regcache = list;
return new_regcache;
}
+struct regcache *
+get_thread_arch_regcache (ptid_t ptid, struct gdbarch *gdbarch)
+{
+ struct address_space *aspace;
+
+ /* For the benefit of "maint print registers" & co when debugging an
+ executable, allow dumping the regcache even when there is no
+ thread selected (target_thread_address_space internal-errors if
+ no address space is found). Note that normal user commands will
+ fail higher up on the call stack due to no
+ target_has_registers. */
+ aspace = (ptid_equal (null_ptid, ptid)
+ ? NULL
+ : target_thread_address_space (ptid));
+
+ return get_thread_arch_aspace_regcache (ptid, gdbarch, aspace);
+}
+
static ptid_t current_thread_ptid;
static struct gdbarch *current_thread_arch;
return get_thread_regcache (inferior_ptid);
}
+/* See common/common-regcache.h. */
+
+struct regcache *
+get_thread_regcache_for_ptid (ptid_t ptid)
+{
+ return get_thread_regcache (ptid);
+}
/* Observer for the target_changed event. */
registers_changed_ptid (ptid_t ptid)
{
struct regcache_list *list, **list_link;
- int wildcard = ptid_equal (ptid, minus_one_ptid);
list = current_regcache;
list_link = ¤t_regcache;
list = *list_link;
}
- if (wildcard || ptid_equal (ptid, current_thread_ptid))
+ if (ptid_match (current_thread_ptid, ptid))
{
current_thread_ptid = null_ptid;
current_thread_arch = NULL;
}
- if (wildcard || ptid_equal (ptid, inferior_ptid))
+ if (ptid_match (inferior_ptid, ptid))
{
/* We just deleted the regcache of the current thread. Need to
forget about any frames we have cached, too. */
memcpy (buf, register_buffer (regcache, regnum),
regcache->descr->sizeof_register[regnum]);
- return regcache->register_status[regnum];
+ return (enum register_status) regcache->register_status[regnum];
}
enum register_status
{
/* Read-only register cache, perhaps the cooked value was
cached? */
- struct gdbarch *gdbarch = regcache->descr->gdbarch;
-
if (regcache->register_status[regnum] == REG_VALID)
memcpy (buf, register_buffer (regcache, regnum),
regcache->descr->sizeof_register[regnum]);
else
memset (buf, 0, regcache->descr->sizeof_register[regnum]);
- return regcache->register_status[regnum];
+ return (enum register_status) regcache->register_status[regnum];
+ }
+ else if (gdbarch_pseudo_register_read_value_p (regcache->descr->gdbarch))
+ {
+ struct value *mark, *computed;
+ enum register_status result = REG_VALID;
+
+ mark = value_mark ();
+
+ computed = gdbarch_pseudo_register_read_value (regcache->descr->gdbarch,
+ regcache, regnum);
+ if (value_entirely_available (computed))
+ memcpy (buf, value_contents_raw (computed),
+ regcache->descr->sizeof_register[regnum]);
+ else
+ {
+ memset (buf, 0, regcache->descr->sizeof_register[regnum]);
+ result = REG_UNAVAILABLE;
+ }
+
+ value_free_to_mark (mark);
+
+ return result;
}
else
return gdbarch_pseudo_register_read (regcache->descr->gdbarch, regcache,
regnum, buf);
}
+struct value *
+regcache_cooked_read_value (struct regcache *regcache, int regnum)
+{
+ gdb_assert (regnum >= 0);
+ gdb_assert (regnum < regcache->descr->nr_cooked_registers);
+
+ if (regnum < regcache->descr->nr_raw_registers
+ || (regcache->readonly_p
+ && regcache->register_status[regnum] != REG_UNKNOWN)
+ || !gdbarch_pseudo_register_read_value_p (regcache->descr->gdbarch))
+ {
+ struct value *result;
+
+ result = allocate_value (register_type (regcache->descr->gdbarch,
+ regnum));
+ VALUE_LVAL (result) = lval_register;
+ VALUE_REGNUM (result) = regnum;
+
+ /* It is more efficient in general to do this delegation in this
+ direction than in the other one, even though the value-based
+ API is preferred. */
+ if (regcache_cooked_read (regcache, regnum,
+ value_contents_raw (result)) == REG_UNAVAILABLE)
+ mark_value_bytes_unavailable (result, 0,
+ TYPE_LENGTH (value_type (result)));
+
+ return result;
+ }
+ else
+ return gdbarch_pseudo_register_read_value (regcache->descr->gdbarch,
+ regcache, regnum);
+}
+
enum register_status
regcache_cooked_read_signed (struct regcache *regcache, int regnum,
LONGEST *val)
regcache_raw_write (struct regcache *regcache, int regnum,
const gdb_byte *buf)
{
- struct cleanup *old_chain;
+ struct cleanup *chain_before_save_inferior;
+ struct cleanup *chain_before_invalidate_register;
gdb_assert (regcache != NULL && buf != NULL);
gdb_assert (regnum >= 0 && regnum < regcache->descr->nr_raw_registers);
regcache->descr->sizeof_register[regnum]) == 0))
return;
- old_chain = save_inferior_ptid ();
+ chain_before_save_inferior = save_inferior_ptid ();
inferior_ptid = regcache->ptid;
target_prepare_to_store (regcache);
memcpy (register_buffer (regcache, regnum), buf,
regcache->descr->sizeof_register[regnum]);
regcache->register_status[regnum] = REG_VALID;
+
+ /* Register a cleanup function for invalidating the register after it is
+ written, in case of a failure. */
+ chain_before_invalidate_register
+ = make_cleanup_regcache_invalidate (regcache, regnum);
+
target_store_registers (regcache, regnum);
- do_cleanups (old_chain);
+ /* The target did not throw an error so we can discard invalidating the
+ register and restore the cleanup chain to what it was. */
+ discard_cleanups (chain_before_invalidate_register);
+
+ do_cleanups (chain_before_save_inferior);
}
void
memcpy (buf, regbuf, size);
}
+/* Transfer a single or all registers belonging to a certain register
+ set to or from a buffer. This is the main worker function for
+ regcache_supply_regset and regcache_collect_regset. */
+
+static void
+regcache_transfer_regset (const struct regset *regset,
+ const struct regcache *regcache,
+ struct regcache *out_regcache,
+ int regnum, const void *in_buf,
+ void *out_buf, size_t size)
+{
+ const struct regcache_map_entry *map;
+ int offs = 0, count;
+
+ for (map = regset->regmap; (count = map->count) != 0; map++)
+ {
+ int regno = map->regno;
+ int slot_size = map->size;
+
+ if (slot_size == 0 && regno != REGCACHE_MAP_SKIP)
+ slot_size = regcache->descr->sizeof_register[regno];
+
+ if (regno == REGCACHE_MAP_SKIP
+ || (regnum != -1
+ && (regnum < regno || regnum >= regno + count)))
+ offs += count * slot_size;
+
+ else if (regnum == -1)
+ for (; count--; regno++, offs += slot_size)
+ {
+ if (offs + slot_size > size)
+ break;
+
+ if (out_buf)
+ regcache_raw_collect (regcache, regno,
+ (gdb_byte *) out_buf + offs);
+ else
+ regcache_raw_supply (out_regcache, regno, in_buf
+ ? (const gdb_byte *) in_buf + offs
+ : NULL);
+ }
+ else
+ {
+ /* Transfer a single register and return. */
+ offs += (regnum - regno) * slot_size;
+ if (offs + slot_size > size)
+ return;
+
+ if (out_buf)
+ regcache_raw_collect (regcache, regnum,
+ (gdb_byte *) out_buf + offs);
+ else
+ regcache_raw_supply (out_regcache, regnum, in_buf
+ ? (const gdb_byte *) in_buf + offs
+ : NULL);
+ return;
+ }
+ }
+}
+
+/* Supply register REGNUM from BUF to REGCACHE, using the register map
+ in REGSET. If REGNUM is -1, do this for all registers in REGSET.
+ If BUF is NULL, set the register(s) to "unavailable" status. */
+
+void
+regcache_supply_regset (const struct regset *regset,
+ struct regcache *regcache,
+ int regnum, const void *buf, size_t size)
+{
+ regcache_transfer_regset (regset, regcache, regcache, regnum,
+ buf, NULL, size);
+}
+
+/* Collect register REGNUM from REGCACHE to BUF, using the register
+ map in REGSET. If REGNUM is -1, do this for all registers in
+ REGSET. */
+
+void
+regcache_collect_regset (const struct regset *regset,
+ const struct regcache *regcache,
+ int regnum, void *buf, size_t size)
+{
+ regcache_transfer_regset (regset, regcache, NULL, regnum,
+ NULL, buf, size);
+}
+
/* Special handling for register PC. */
printf_filtered (_("Register cache flushed.\n"));
}
-static void
-dump_endian_bytes (struct ui_file *file, enum bfd_endian endian,
- const unsigned char *buf, long len)
-{
- int i;
-
- switch (endian)
- {
- case BFD_ENDIAN_BIG:
- for (i = 0; i < len; i++)
- fprintf_unfiltered (file, "%02x", buf[i]);
- break;
- case BFD_ENDIAN_LITTLE:
- for (i = len - 1; i >= 0; i--)
- fprintf_unfiltered (file, "%02x", buf[i]);
- break;
- default:
- internal_error (__FILE__, __LINE__, _("Bad switch"));
- }
-}
-
enum regcache_dump_what
{
regcache_dump_none, regcache_dump_raw,
- regcache_dump_cooked, regcache_dump_groups
+ regcache_dump_cooked, regcache_dump_groups,
+ regcache_dump_remote
};
static void
int footnote_register_offset = 0;
int footnote_register_type_name_null = 0;
long register_offset = 0;
- unsigned char buf[MAX_REGISTER_SIZE];
+ gdb_byte buf[MAX_REGISTER_SIZE];
#if 0
fprintf_unfiltered (file, "nr_raw_registers %d\n",
t = n;
}
/* Chop a leading builtin_type. */
- if (strncmp (t, blt, strlen (blt)) == 0)
+ if (startswith (t, blt))
t += strlen (blt);
}
fprintf_unfiltered (file, " %-15s", t);
else
{
regcache_raw_read (regcache, regnum, buf);
- fprintf_unfiltered (file, "0x");
- dump_endian_bytes (file,
- gdbarch_byte_order (gdbarch), buf,
- regcache->descr->sizeof_register[regnum]);
+ print_hex_chars (file, buf,
+ regcache->descr->sizeof_register[regnum],
+ gdbarch_byte_order (gdbarch));
}
}
else if (status == REG_UNAVAILABLE)
fprintf_unfiltered (file, "<unavailable>");
else
- {
- fprintf_unfiltered (file, "0x");
- dump_endian_bytes (file,
- gdbarch_byte_order (gdbarch), buf,
- regcache->descr->sizeof_register[regnum]);
- }
+ print_hex_chars (file, buf,
+ regcache->descr->sizeof_register[regnum],
+ gdbarch_byte_order (gdbarch));
}
}
}
}
+ /* Remote packet configuration. */
+ if (what_to_dump == regcache_dump_remote)
+ {
+ if (regnum < 0)
+ {
+ fprintf_unfiltered (file, "Rmt Nr g/G Offset");
+ }
+ else if (regnum < regcache->descr->nr_raw_registers)
+ {
+ int pnum, poffset;
+
+ if (remote_register_number_and_offset (get_regcache_arch (regcache), regnum,
+ &pnum, &poffset))
+ fprintf_unfiltered (file, "%7d %11d", pnum, poffset);
+ }
+ }
+
fprintf_unfiltered (file, "\n");
}
regcache_print (args, regcache_dump_groups);
}
+static void
+maintenance_print_remote_registers (char *args, int from_tty)
+{
+ regcache_print (args, regcache_dump_remote);
+}
+
extern initialize_file_ftype _initialize_regcache; /* -Wmissing-prototype */
void
"including each register's group.\n"
"Takes an optional file parameter."),
&maintenanceprintlist);
+ add_cmd ("remote-registers", class_maintenance,
+ maintenance_print_remote_registers, _("\
+Print the internal register configuration including each register's\n\
+remote register number and buffer offset in the g/G packets.\n\
+Takes an optional file parameter."),
+ &maintenanceprintlist);
}