From e3636ab1e171a27bb7b45734fdb45a4f7e3e8bc9 Mon Sep 17 00:00:00 2001 From: Luis Machado Date: Wed, 29 Dec 2021 08:20:53 -0300 Subject: [PATCH] Support assignment of capabilities to C registers Enable assignment of capabilities to C registers while preserving the capability tag. This enables operations like the following: set $c0=$c1 set $c0=p, where "p" is a capability (pointer) in AARCH64-CAP Due to the X/C register overlap, we also force a re-read of register data after every register write. So any 'G' packets are immediately followed by a 'g' packet. --- gdb/aarch64-linux-tdep.c | 20 ++++++++ gdb/aarch64-tdep.c | 23 ++++----- gdb/arch-utils.c | 17 +++++++ gdb/arch-utils.h | 9 ++++ gdb/frame.c | 100 +++++++++++++++++++++++++++++++++++++++ gdb/frame.h | 8 ++++ gdb/gdbarch.c | 46 ++++++++++++++++++ gdb/gdbarch.h | 12 +++++ gdb/gdbarch.sh | 5 ++ gdb/remote.c | 1 + gdb/valops.c | 5 +- 11 files changed, 232 insertions(+), 14 deletions(-) diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 356f20342e5..dd92b31fd59 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -1637,6 +1637,24 @@ aarch64_linux_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr) return cap[0] != 0; } +/* AArch64 Linux implementation of the set_cap_tag_from_address gdbarch + hook. Sets the tag from the capability located at ADDR to TAG. */ + +static void +aarch64_linux_set_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr, + bool tag) +{ + gdb::byte_vector cap; + + /* Read original capability at ADDR. */ + cap = target_read_capability (addr); + + cap[0] = tag? 1 : 0; + + /* Write back the same contents but with a custom tag. */ + target_write_capability (addr, cap); +} + /* Return the number of Morello memory tag granules contained in the memory range [addr, addr + len). */ @@ -2115,6 +2133,8 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) aarch64_linux_report_signal_info); set_gdbarch_get_cap_tag_from_address (gdbarch, aarch64_linux_get_cap_tag_from_address); + set_gdbarch_set_cap_tag_from_address (gdbarch, + aarch64_linux_set_cap_tag_from_address); /* Core file helpers. */ diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 2928bc6d44b..c1ab29094ec 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -1984,11 +1984,11 @@ struct aarch64_call_info std::vector si; }; -/* Helper function to set a TAG for a particular register REGNUM. */ +/* Implementation of the gdbarch_register_set_tag hook. */ static void -set_register_tag (struct gdbarch *gdbarch, struct regcache *regcache, - int regnum, bool tag) +aarch64_register_set_tag (struct gdbarch *gdbarch, struct regcache *regcache, + int regnum, bool tag) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); @@ -2069,7 +2069,7 @@ pass_in_c (struct gdbarch *gdbarch, struct regcache *regcache, /* We need to read the tags from memory. */ gdb::byte_vector cap = target_read_capability (address); bool tag = cap[0] == 0 ? false : true; - set_register_tag (gdbarch, regcache, regnum, tag); + aarch64_register_set_tag (gdbarch, regcache, regnum, tag); if (aarch64_debug) debug_printf ("aarch64: %s Read tag %s from address %s\n", @@ -2570,7 +2570,7 @@ morello_push_dummy_call (struct gdbarch *gdbarch, struct value *function, capability. */ struct value *clr = regcache->cooked_read_value (regnum); regcache->cooked_write (regnum, value_contents (clr)); - set_register_tag (gdbarch, regcache, regnum, value_tag (clr)); + aarch64_register_set_tag (gdbarch, regcache, regnum, value_tag (clr)); } if (aarch64_debug) @@ -2601,7 +2601,7 @@ morello_push_dummy_call (struct gdbarch *gdbarch, struct value *function, capability. */ struct value *csp = regcache->cooked_read_value (regnum); regcache->cooked_write (regnum, value_contents (csp)); - set_register_tag (gdbarch, regcache, regnum, value_tag (csp)); + aarch64_register_set_tag (gdbarch, regcache, regnum, value_tag (csp)); } if (aarch64_debug) @@ -2799,7 +2799,7 @@ morello_push_dummy_call (struct gdbarch *gdbarch, struct value *function, struct value *csp = regcache->cooked_read_value (regnum); regcache->cooked_write (regnum, value_contents (csp)); - set_register_tag (gdbarch, regcache, regnum, value_tag (csp)); + aarch64_register_set_tag (gdbarch, regcache, regnum, value_tag (csp)); } if (aarch64_debug) @@ -3743,7 +3743,7 @@ morello_store_return_value (struct value *value, struct regcache *regs, /* Also store the tag if we are dealing with a capability. */ if (aapcs64_cap || type->code () == TYPE_CODE_CAPABILITY) - set_register_tag (gdbarch, regs, regno, value_tag (value)); + aarch64_register_set_tag (gdbarch, regs, regno, value_tag (value)); } else if (type->code () == TYPE_CODE_INT || type->code () == TYPE_CODE_CHAR @@ -3821,7 +3821,7 @@ morello_store_return_value (struct value *value, struct regcache *regs, /* We need to read the tags from memory. */ gdb::byte_vector cap = target_read_capability (address); bool tag = cap[0] == 0 ? false : true; - set_register_tag (gdbarch, regs, regno, tag); + aarch64_register_set_tag (gdbarch, regs, regno, tag); if (aarch64_debug) debug_printf ("aarch64: %s Read tag %s from address %s\n", @@ -4399,8 +4399,8 @@ aarch64_pseudo_write (struct gdbarch *gdbarch, struct regcache *regcache, regcache->raw_write_part (c_real_regnum, 0, 8, lower_bytes); regcache->raw_write_part (c_real_regnum, 8, 8, upper_bytes); - set_register_tag (gdbarch, regcache, c_real_regnum, - (tag != 0)? true : false); + aarch64_register_set_tag (gdbarch, regcache, c_real_regnum, + (tag != 0)? true : false); return; } @@ -5673,6 +5673,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* For fetching register tag information. */ set_gdbarch_register_has_tag (gdbarch, aarch64_register_has_tag); set_gdbarch_register_tag (gdbarch, aarch64_register_tag); + set_gdbarch_register_set_tag (gdbarch, aarch64_register_set_tag); /* Create the Morello register aliases. */ /* cip0 and cip1 */ diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index 2136e8a2225..79d36cadc0d 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -1070,6 +1070,15 @@ default_register_tag (struct gdbarch *gdbarch, return false; } +/* See arch-utils.h. */ +void +default_register_set_tag (struct gdbarch *gdbarch, + regcache *regcache, + int cookednum, bool tag) +{ + return; +} + /* See arch-utils.h. */ bool default_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr) @@ -1077,6 +1086,14 @@ bool default_get_cap_tag_from_address (struct gdbarch *gdbarch, return false; } +/* See arch-utils.h. */ +void +default_set_cap_tag_from_address (struct gdbarch *gdbarch, + CORE_ADDR addr, bool tag) +{ + return; +} + void _initialize_gdbarch_utils (); void _initialize_gdbarch_utils () diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index fb35d731a42..e6dd69962c4 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -303,7 +303,16 @@ extern bool default_register_tag (struct gdbarch *gdbarch, readable_regcache *regcache, int cookednum); +/* Default implementation of gdbarch_register_set_tag. */ +extern void default_register_set_tag (struct gdbarch *gdbarch, + regcache *regcache, + int cookednum, bool tag); + /* Default implementation of gdbarch_cap_tag_from_address. */ extern bool default_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr); + +/* Default implementation of gdbarch_set_cap_tag_from_address. */ +extern void default_set_cap_tag_from_address (struct gdbarch *gdbarch, + CORE_ADDR addr, bool tag); #endif /* ARCH_UTILS_H */ diff --git a/gdb/frame.c b/gdb/frame.c index 6bfa31e630e..9609fcd07a6 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -1415,6 +1415,59 @@ read_frame_register_unsigned (frame_info *frame, int regnum, return false; } +void +put_frame_register (struct frame_info *frame, int regnum, + const gdb_byte *buf, struct value *fromval) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + int realnum; + int optim; + int unavail; + enum lval_type lval; + CORE_ADDR addr; + + frame_register (frame, regnum, &optim, &unavail, + &lval, &addr, &realnum, NULL); + if (optim) + error (_("Attempt to assign to a register that was not saved.")); + + struct type *val_type = value_type (fromval); + + switch (lval) + { + case lval_memory: + { + write_memory (addr, buf, register_size (gdbarch, regnum)); + + /* If this value is a capability, we need to handle the capability tag + as well. */ + if ((val_type->code () == TYPE_CODE_CAPABILITY + || (val_type->code () == TYPE_CODE_PTR + && TYPE_CAPABILITY (val_type))) + && value_tagged (fromval)) + gdbarch_set_cap_tag_from_address (gdbarch, addr, value_tag (fromval)); + break; + } + case lval_register: + { + get_current_regcache ()->cooked_write (realnum, buf); + /* If this value is a capability, we need to handle the capability tag + as well. */ + if ((val_type->code () == TYPE_CODE_CAPABILITY + || (val_type->code () == TYPE_CODE_PTR + && TYPE_CAPABILITY (val_type))) + && value_tagged (fromval)) + { + gdbarch_register_set_tag (gdbarch, get_current_regcache (), + regnum, value_tag (fromval)); + } + } + break; + default: + error (_("Attempt to assign to an unmodifiable value.")); + } +} + void put_frame_register (struct frame_info *frame, int regnum, const gdb_byte *buf) @@ -1551,6 +1604,53 @@ get_frame_register_bytes (frame_info *frame, int regnum, return true; } +void +put_frame_register_value (struct frame_info *frame, int regnum, + CORE_ADDR offset, struct value *fromval) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + int len = TYPE_LENGTH (value_type (fromval)); + const gdb_byte *myaddr = value_contents (fromval); + + /* Skip registers wholly inside of OFFSET. */ + while (offset >= register_size (gdbarch, regnum)) + { + offset -= register_size (gdbarch, regnum); + regnum++; + } + + /* Copy the data. */ + while (len > 0) + { + int curr_len = register_size (gdbarch, regnum) - offset; + + if (curr_len > len) + curr_len = len; + + if (curr_len == register_size (gdbarch, regnum)) + { + put_frame_register (frame, regnum, myaddr, fromval); + } + else + { + struct value *value = frame_unwind_register_value (frame->next, + regnum); + gdb_assert (value != NULL); + + memcpy ((char *) value_contents_writeable (value) + offset, myaddr, + curr_len); + put_frame_register (frame, regnum, value_contents_raw (value), + fromval); + release_value (value); + } + + myaddr += curr_len; + len -= curr_len; + offset = 0; + regnum++; + } +} + void put_frame_register_bytes (struct frame_info *frame, int regnum, CORE_ADDR offset, int len, const gdb_byte *myaddr) diff --git a/gdb/frame.h b/gdb/frame.h index 3ceb7b32eff..acf63851086 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -617,6 +617,10 @@ extern void frame_register (struct frame_info *frame, int regnum, extern void put_frame_register (struct frame_info *frame, int regnum, const gdb_byte *buf); +/* Same as put_frame_register, but passing a struct value *. */ +extern void put_frame_register (struct frame_info *frame, int regnum, + const gdb_byte *buf, struct value *fromval); + /* Read LEN bytes from one or multiple registers starting with REGNUM in frame FRAME, starting at OFFSET, into BUF. If the register contents are optimized out or unavailable, set *OPTIMIZEDP, @@ -632,6 +636,10 @@ extern void put_frame_register_bytes (struct frame_info *frame, int regnum, CORE_ADDR offset, int len, const gdb_byte *myaddr); +/* Same as put_frame_register_bytes, but passing a struct value *. */ +extern void put_frame_register_value (struct frame_info *frame, int regnum, + CORE_ADDR offset, struct value *fromval); + /* Unwind the PC. Strictly speaking return the resume address of the calling frame. For GDB, `pc' is the resume address and not a specific register. */ diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index a79d9bdfcf3..bc6c125b414 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -243,6 +243,7 @@ struct gdbarch CORE_ADDR deprecated_function_start_offset; gdbarch_remote_register_number_ftype *remote_register_number; gdbarch_get_cap_tag_from_address_ftype *get_cap_tag_from_address; + gdbarch_set_cap_tag_from_address_ftype *set_cap_tag_from_address; gdbarch_fetch_tls_load_module_address_ftype *fetch_tls_load_module_address; gdbarch_get_thread_local_address_ftype *get_thread_local_address; CORE_ADDR frame_args_skip; @@ -357,6 +358,7 @@ struct gdbarch gdbarch_read_core_file_mappings_ftype *read_core_file_mappings; gdbarch_register_has_tag_ftype *register_has_tag; gdbarch_register_tag_ftype *register_tag; + gdbarch_register_set_tag_ftype *register_set_tag; }; /* Create a new ``struct gdbarch'' based on information provided by @@ -432,6 +434,7 @@ gdbarch_alloc (const struct gdbarch_info *info, gdbarch->memory_remove_breakpoint = default_memory_remove_breakpoint; gdbarch->remote_register_number = default_remote_register_number; gdbarch->get_cap_tag_from_address = default_get_cap_tag_from_address; + gdbarch->set_cap_tag_from_address = default_set_cap_tag_from_address; gdbarch->unwind_pc = default_unwind_pc; gdbarch->unwind_sp = default_unwind_sp; gdbarch->stabs_argument_has_addr = default_stabs_argument_has_addr; @@ -478,6 +481,7 @@ gdbarch_alloc (const struct gdbarch_info *info, 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->register_set_tag = default_register_set_tag; /* gdbarch_alloc() */ return gdbarch; @@ -624,6 +628,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of deprecated_function_start_offset, invalid_p == 0 */ /* Skip verify of remote_register_number, invalid_p == 0 */ /* Skip verify of get_cap_tag_from_address, invalid_p == 0 */ + /* Skip verify of set_cap_tag_from_address, invalid_p == 0 */ /* Skip verify of fetch_tls_load_module_address, has predicate. */ /* Skip verify of get_thread_local_address, has predicate. */ /* Skip verify of frame_args_skip, invalid_p == 0 */ @@ -737,6 +742,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* 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 */ + /* Skip verify of register_set_tag, invalid_p == 0 */ if (!log.empty ()) internal_error (__FILE__, __LINE__, _("verify_gdbarch: the following are invalid ...%s"), @@ -1348,6 +1354,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: register_reggroup_p = <%s>\n", host_address_to_string (gdbarch->register_reggroup_p)); + fprintf_unfiltered (file, + "gdbarch_dump: register_set_tag = <%s>\n", + host_address_to_string (gdbarch->register_set_tag)); fprintf_unfiltered (file, "gdbarch_dump: register_sim_regno = <%s>\n", host_address_to_string (gdbarch->register_sim_regno)); @@ -1390,6 +1399,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: sdb_reg_to_regnum = <%s>\n", host_address_to_string (gdbarch->sdb_reg_to_regnum)); + fprintf_unfiltered (file, + "gdbarch_dump: set_cap_tag_from_address = <%s>\n", + host_address_to_string (gdbarch->set_cap_tag_from_address)); fprintf_unfiltered (file, "gdbarch_dump: short_bit = %s\n", plongest (gdbarch->short_bit)); @@ -3107,6 +3119,23 @@ set_gdbarch_get_cap_tag_from_address (struct gdbarch *gdbarch, gdbarch->get_cap_tag_from_address = get_cap_tag_from_address; } +void +gdbarch_set_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr, bool tag) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->set_cap_tag_from_address != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_set_cap_tag_from_address called\n"); + gdbarch->set_cap_tag_from_address (gdbarch, addr, tag); +} + +void +set_gdbarch_set_cap_tag_from_address (struct gdbarch *gdbarch, + gdbarch_set_cap_tag_from_address_ftype set_cap_tag_from_address) +{ + gdbarch->set_cap_tag_from_address = set_cap_tag_from_address; +} + int gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch) { @@ -5362,6 +5391,23 @@ set_gdbarch_register_tag (struct gdbarch *gdbarch, gdbarch->register_tag = register_tag; } +void +gdbarch_register_set_tag (struct gdbarch *gdbarch, regcache *regcache, int cookednum, bool tag) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->register_set_tag != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_register_set_tag called\n"); + gdbarch->register_set_tag (gdbarch, regcache, cookednum, tag); +} + +void +set_gdbarch_register_set_tag (struct gdbarch *gdbarch, + gdbarch_register_set_tag_ftype register_set_tag) +{ + gdbarch->register_set_tag = register_set_tag; +} + /* Keep a registry of per-architecture data-pointers required by GDB modules. */ diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 49c7f1c7a8d..bbf1fe4e890 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -645,6 +645,12 @@ typedef bool (gdbarch_get_cap_tag_from_address_ftype) (struct gdbarch *gdbarch, extern bool gdbarch_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr); extern void set_gdbarch_get_cap_tag_from_address (struct gdbarch *gdbarch, gdbarch_get_cap_tag_from_address_ftype *get_cap_tag_from_address); +/* Set the tag from a capability stored at address ADDR to TAG. */ + +typedef void (gdbarch_set_cap_tag_from_address_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr, bool tag); +extern void gdbarch_set_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr, bool tag); +extern void set_gdbarch_set_cap_tag_from_address (struct gdbarch *gdbarch, gdbarch_set_cap_tag_from_address_ftype *set_cap_tag_from_address); + /* Fetch the target specific address used to represent a load module. */ extern int gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch); @@ -1683,6 +1689,12 @@ typedef bool (gdbarch_register_tag_ftype) (struct gdbarch *gdbarch, readable_reg 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); +/* Sets the register tag to TAG. */ + +typedef void (gdbarch_register_set_tag_ftype) (struct gdbarch *gdbarch, regcache *regcache, int cookednum, bool tag); +extern void gdbarch_register_set_tag (struct gdbarch *gdbarch, regcache *regcache, int cookednum, bool tag); +extern void set_gdbarch_register_set_tag (struct gdbarch *gdbarch, gdbarch_register_set_tag_ftype *register_set_tag); + extern struct gdbarch_tdep *gdbarch_tdep (struct gdbarch *gdbarch); diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 01d1a6972b2..2ce7c18d87f 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -574,6 +574,9 @@ m;int;remote_register_number;int regno;regno;;default_remote_register_number;;0 # Return the tag from a capability stored at address ADDR. m;bool;get_cap_tag_from_address;CORE_ADDR addr;addr;0;default_get_cap_tag_from_address;;0 +# Set the tag from a capability stored at address ADDR to TAG. +m;void;set_cap_tag_from_address;CORE_ADDR addr, bool tag;addr, tag;0;default_set_cap_tag_from_address;;0 + # Fetch the target specific address used to represent a load module. F;CORE_ADDR;fetch_tls_load_module_address;struct objfile *objfile;objfile @@ -1202,6 +1205,8 @@ m;bool;register_has_tag;readable_regcache *regcache, int cookednum;regcache, coo # Returns true if the register tag bit is 1 and false otherwise. # The default is to always return false. m;bool;register_tag;readable_regcache *regcache, int cookednum;regcache, cookednum;;default_register_tag;;0 +# Sets the register tag to TAG. +m;void;register_set_tag;regcache *regcache, int cookednum, bool tag;regcache, cookednum, tag;;default_register_set_tag;;0 EOF } diff --git a/gdb/remote.c b/gdb/remote.c index 5fff3520e0d..80c287de8ef 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -8451,6 +8451,7 @@ remote_target::store_registers (struct regcache *regcache, int regnum) return; store_registers_using_G (regcache); + fetch_registers_using_g (regcache); return; } diff --git a/gdb/valops.c b/gdb/valops.c index d0f1a1ed604..d3339712e9f 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1175,10 +1175,9 @@ value_assign (struct value *toval, struct value *fromval) } else { - put_frame_register_bytes (frame, value_reg, + put_frame_register_value (frame, value_reg, value_offset (toval), - TYPE_LENGTH (type), - value_contents (fromval)); + fromval); } } -- 2.39.2