This patch teaches GDB how to print the capability tag from registers.
gdb/ChangeLog:
2020-10-20 Luis Machado <luis.machado@arm.com>
* aarch64-tdep.c (aarch64_register_has_tag, aarch64_register_tag): New
functions.
(aarch64_gdbarch_init): Register hooks.
* arch-utils.c (default_register_has_tag, default_register_tag): New
functions.
* arch-utils.h (default_register_has_tag, default_register_tag): New
prototypes.
* gdbarch.c: Regenerate.
* gdbarch.h: Likewise.
* gdbarch.sh (register_has_tag, register_tag): New gdbarch hooks.
* regcache.c (readable_regcache::cooked_read_value): Fetch the tag
metadata from registers.
* valprint.c (generic_value_print_capability): Display register tags.
* value.c (struct value) <tagged, tag>: New fields.
(value_contents_copy_raw): Handle tags.
(value_tagged, set_value_tagged, value_tag, set_value_tag): New
functions.
(value_copy): Handle tags.
* value.h (value_tagged, set_value_tagged, value_tag)
(set_value_tag): New prototypes.
name);
}
+/* Implements the gdbarch_register_has_tag hook. */
+
+static bool
+aarch64_register_has_tag (struct gdbarch *gdbarch,
+ readable_regcache *regcache,
+ int regnum)
+{
+ if (aarch64_debug)
+ debug_printf ("%s: Entering\n", __func__);
+
+ aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+
+ /* Only Morello's registers have tags. */
+ if (!tdep->has_capability ())
+ return false;
+
+ /* The last two registers of the C register set don't have tags. */
+ if (regnum < tdep->cap_reg_base ||
+ regnum > tdep->cap_reg_last - 2)
+ return false;
+
+ if (aarch64_debug)
+ debug_printf ("%s: regnum %d\n", __func__, regnum);
+
+ return true;
+}
+
+/* Implements the gdbarch_register_tag hook. */
+
+static bool
+aarch64_register_tag (struct gdbarch *gdbarch,
+ readable_regcache *regcache,
+ int regnum)
+{
+ if (aarch64_debug)
+ debug_printf ("%s: Entering\n", __func__);
+
+ aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+
+ /* Only Morello's registers have tags. */
+ if (!tdep->has_capability ())
+ return false;
+
+ /* The last two registers of the C register set don't have tags. */
+ if (regnum < tdep->cap_reg_base ||
+ regnum > tdep->cap_reg_last - 2)
+ return false;
+
+ /* Find the proper bit within the tag_map. */
+ int shift = regnum - tdep->cap_reg_base;
+ ULONGEST tag_map = 0;
+
+ /* Fetch the tag_map register. */
+ regcache->cooked_read (tdep->cap_reg_last - 1, &tag_map);
+
+ if (aarch64_debug)
+ debug_printf ("%s: regnum %d, shift %d, tag bit %ld, tag_map %lx\n",
+ __func__, regnum, shift,
+ (tag_map >> shift) & 1, tag_map);
+
+ if (((tag_map >> shift) & 1) == 0)
+ return false;
+
+ return true;
+}
+
/* Initialize the current architecture based on INFO. If possible,
re-use an architecture from ARCHES, which is a list of
architectures already created during this debugging session.
set_gdbarch_record_special_symbol (gdbarch,
aarch64_record_special_symbol);
+ /* For fetching register tag information. */
+ set_gdbarch_register_has_tag (gdbarch, aarch64_register_has_tag);
+ set_gdbarch_register_tag (gdbarch, aarch64_register_tag);
+
/* Create the Morello register aliases. */
/* cip0 and cip1 */
aarch64_morello_register_aliases[0].regnum = tdep->cap_reg_base + 16;
{
}
+/* See arch-utils.h. */
+bool
+default_register_has_tag (struct gdbarch *gdbarch,
+ readable_regcache *regcache,
+ int cookednum)
+{
+ return false;
+}
+
+/* See arch-utils.h. */
+bool
+default_register_tag (struct gdbarch *gdbarch,
+ readable_regcache *regcache,
+ int cookednum)
+{
+ return false;
+}
+
/* Static function declarations */
static void alloc_gdbarch_data (struct gdbarch *);
struct bfd *cbfd,
read_core_file_mappings_pre_loop_ftype pre_loop_cb,
read_core_file_mappings_loop_ftype loop_cb);
+
+/* Default implementation of gdbarch_register_has_tag. */
+extern bool default_register_has_tag (struct gdbarch *gdbarch,
+ readable_regcache *regcache,
+ int cookednum);
+
+/* Default implementation of gdbarch_register_tag. */
+extern bool default_register_tag (struct gdbarch *gdbarch,
+ readable_regcache *regcache,
+ int cookednum);
#endif /* ARCH_UTILS_H */
predefault="default_read_core_file_mappings",
invalid=False,
)
+
+Method(
+ comment="""
+Returns true if register COOKEDNUM has a tag and false otherwise.
+The default is to always return false.
+""",
+ type="bool",
+ name="register_has_tag",
+ params=[
+ ("readable_regcache *", "regcache"),
+ ("int", "cookednum"),
+ ],
+ predefault="default_register_has_tag",
+ invalid=False,
+)
+
+Method(
+ comment="""
+Returns true if the register tag bit is 1 and false otherwise.
+The default is to always return false.
+""",
+ type="bool",
+ name="register_tag",
+ params=[
+ ("readable_regcache *", "regcache"),
+ ("int", "cookednum"),
+ ],
+ predefault="default_register_tag",
+ invalid=False,
+)
typedef void (gdbarch_read_core_file_mappings_ftype) (struct gdbarch *gdbarch, struct bfd *cbfd, read_core_file_mappings_pre_loop_ftype pre_loop_cb, read_core_file_mappings_loop_ftype loop_cb);
extern void gdbarch_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd, read_core_file_mappings_pre_loop_ftype pre_loop_cb, read_core_file_mappings_loop_ftype loop_cb);
extern void set_gdbarch_read_core_file_mappings (struct gdbarch *gdbarch, gdbarch_read_core_file_mappings_ftype *read_core_file_mappings);
+
+/* Returns true if register COOKEDNUM has a tag and false otherwise.
+ The default is to always return false. */
+
+typedef bool (gdbarch_register_has_tag_ftype) (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern bool gdbarch_register_has_tag (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern void set_gdbarch_register_has_tag (struct gdbarch *gdbarch, gdbarch_register_has_tag_ftype *register_has_tag);
+
+/* Returns true if the register tag bit is 1 and false otherwise.
+ The default is to always return false. */
+
+typedef bool (gdbarch_register_tag_ftype) (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern bool gdbarch_register_tag (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern void set_gdbarch_register_tag (struct gdbarch *gdbarch, gdbarch_register_tag_ftype *register_tag);
gdbarch_type_align_ftype *type_align;
gdbarch_get_pc_address_flags_ftype *get_pc_address_flags;
gdbarch_read_core_file_mappings_ftype *read_core_file_mappings;
+ gdbarch_register_has_tag_ftype *register_has_tag;
+ gdbarch_register_tag_ftype *register_tag;
};
/* Create a new ``struct gdbarch'' based on information provided by
gdbarch->type_align = default_type_align;
gdbarch->get_pc_address_flags = default_get_pc_address_flags;
gdbarch->read_core_file_mappings = default_read_core_file_mappings;
+ gdbarch->register_has_tag = default_register_has_tag;
+ gdbarch->register_tag = default_register_tag;
/* gdbarch_alloc() */
return gdbarch;
/* Skip verify of type_align, invalid_p == 0 */
/* Skip verify of get_pc_address_flags, invalid_p == 0 */
/* Skip verify of read_core_file_mappings, invalid_p == 0 */
+ /* Skip verify of register_has_tag, invalid_p == 0 */
+ /* Skip verify of register_tag, invalid_p == 0 */
if (!log.empty ())
internal_error (__FILE__, __LINE__,
_("verify_gdbarch: the following are invalid ...%s"),
fprintf_filtered (file,
"gdbarch_dump: read_core_file_mappings = <%s>\n",
host_address_to_string (gdbarch->read_core_file_mappings));
+ fprintf_filtered (file,
+ "gdbarch_dump: register_has_tag = <%s>\n",
+ host_address_to_string (gdbarch->register_has_tag));
+ fprintf_filtered (file,
+ "gdbarch_dump: register_tag = <%s>\n",
+ host_address_to_string (gdbarch->register_tag));
if (gdbarch->dump_tdep != NULL)
gdbarch->dump_tdep (gdbarch, file);
}
{
gdbarch->read_core_file_mappings = read_core_file_mappings;
}
+
+bool
+gdbarch_register_has_tag (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->register_has_tag != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_register_has_tag called\n");
+ return gdbarch->register_has_tag (gdbarch, regcache, cookednum);
+}
+
+void
+set_gdbarch_register_has_tag (struct gdbarch *gdbarch,
+ gdbarch_register_has_tag_ftype register_has_tag)
+{
+ gdbarch->register_has_tag = register_has_tag;
+}
+
+bool
+gdbarch_register_tag (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->register_tag != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_register_tag called\n");
+ return gdbarch->register_tag (gdbarch, regcache, cookednum);
+}
+
+void
+set_gdbarch_register_tag (struct gdbarch *gdbarch,
+ gdbarch_register_tag_ftype register_tag)
+{
+ gdbarch->register_tag = register_tag;
+}
#include "gdbarch-gen.h"
+/* Returns true if register COOKEDNUM has a tag and false otherwise.
+ The default is to always return false. */
+
+typedef bool (gdbarch_register_has_tag_ftype) (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern bool gdbarch_register_has_tag (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern void set_gdbarch_register_has_tag (struct gdbarch *gdbarch, gdbarch_register_has_tag_ftype *register_has_tag);
+
+/* Returns true if the register tag bit is 1 and false otherwise.
+ The default is to always return false. */
+
+typedef bool (gdbarch_register_tag_ftype) (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern bool gdbarch_register_tag (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum);
+extern void set_gdbarch_register_tag (struct gdbarch *gdbarch, gdbarch_register_tag_ftype *register_tag);
+
extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch);
mark_value_bytes_unavailable (result, 0,
TYPE_LENGTH (value_type (result)));
+ if (gdbarch_register_has_tag (m_descr->gdbarch, this, regnum))
+ {
+ set_value_tagged (result, 1);
+
+ bool tag = gdbarch_register_tag (m_descr->gdbarch, this, regnum);
+ set_value_tag (result, tag);
+ }
+
return result;
}
else
const struct value_print_options *options)
{
struct type *type = check_typedef (value_type (val));
- int length = TYPE_LENGTH (type);
+ /* Account for the tag bit in the length. */
+ int length = TYPE_LENGTH (type) + 1;
const gdb_byte *contents = value_contents_for_printing (val).data ();
enum bfd_endian byte_order = type_byte_order (type);
+ bool tag = false;
+
+ switch (VALUE_LVAL (val))
+ {
+ case lval_register:
+ if (value_tagged (val))
+ tag = value_tag (val);
+ break;
+ case lval_memory:
+ /* TODO-Morello: Add hook that reads capabilities from memory. We
+ should use those here to fetch the tag from a memory location. */
+ tag = true;
+ break;
+ default:
+ break;
+ }
if (options->format && options->format == 'x')
- print_hex_chars (stream, contents, length, byte_order, 0);
+ print_hex_chars (stream, contents, length, byte_order, 0);
else
{
uint128_t dummy_cap;
memcpy (&dummy_cap, contents, length);
- capability cap (dummy_cap, false);
+ capability cap (dummy_cap, tag);
fprintf_filtered (stream, "%s", cap.to_str ().c_str ());
}
initialized (1),
stack (0),
is_zero (false),
+ tagged (0),
type (type_),
enclosing_type (type_)
{
otherwise. */
bool is_zero : 1;
+ /* Whether the value has a tag bit. */
+ unsigned int tagged : 1;
+
/* Location of value (if lval). */
union
{
LONGEST embedded_offset = 0;
LONGEST pointed_to_offset = 0;
+ /* The tag value, if tagged. */
+ bool tag;
+
/* Actual contents of the value. Target byte-order.
May be nullptr if the value is lazy or is entirely optimized out.
length * unit_size);
copy (src_contents, dst_contents);
+ /* Copy the tagged and tag metadata. */
+ set_value_tagged (dst, value_tagged (src));
+ set_value_tag (dst, value_tag (src));
+
/* Copy the meta-data, adjusted. */
src_bit_offset = src_offset * unit_size * HOST_CHAR_BIT;
dst_bit_offset = dst_offset * unit_size * HOST_CHAR_BIT;
value->stack = val;
}
+int
+value_tagged (const struct value *value)
+{
+ return value->tagged;
+}
+
+void
+set_value_tagged (struct value *value, int val)
+{
+ value->tagged = val;
+}
+
gdb::array_view<const gdb_byte>
value_contents (struct value *value)
{
value->pointed_to_offset = val;
}
+bool
+value_tag (const struct value *value)
+{
+ return value->tag;
+}
+
+void
+set_value_tag (struct value *value, bool tag)
+{
+ value->tag = tag;
+}
+
const struct lval_funcs *
value_computed_funcs (const struct value *v)
{
val->stack = arg->stack;
val->is_zero = arg->is_zero;
val->initialized = arg->initialized;
+ val->tagged = arg->tagged;
+ val->tag = arg->tag;
val->unavailable = arg->unavailable;
val->optimized_out = arg->optimized_out;
extern LONGEST value_embedded_offset (const struct value *value);
extern void set_value_embedded_offset (struct value *value, LONGEST val);
+extern bool value_tag (const struct value *value);
+extern void set_value_tag (struct value *value, bool tag);
+
/* For lval_computed values, this structure holds functions used to
retrieve and set the value (or portions of the value).
extern int value_stack (const struct value *);
extern void set_value_stack (struct value *value, int val);
+extern int value_tagged (const struct value *);
+extern void set_value_tagged (struct value *value, int val);
+
/* Throw an error complaining that the value has been optimized
out. */