From 871cde60db9c255e1eac162d656ffd492b92b557 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-components.py | 26 ++++++++++ gdb/gdbarch-gen.h | 12 +++++ gdb/gdbarch.c | 46 ++++++++++++++++++ gdb/remote.c | 1 + gdb/valops.c | 4 +- 11 files changed, 253 insertions(+), 13 deletions(-) diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index e5fdaa13d71..7e3a239e616 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -2130,6 +2130,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); +} + /* Implement the maintenance print capability tag command. */ static void @@ -2520,6 +2538,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); add_cmd ("cap_from_addr", class_maintenance, maint_print_cap_from_addr_cmd, diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 5e29e72169f..14fbe1491fa 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -2025,11 +2025,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) { aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch); @@ -2110,7 +2110,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", @@ -2603,7 +2603,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).data ()); - set_register_tag (gdbarch, regcache, regnum, value_tag (clr)); + aarch64_register_set_tag (gdbarch, regcache, regnum, value_tag (clr)); } if (aarch64_debug) @@ -2634,7 +2634,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).data ()); - set_register_tag (gdbarch, regcache, regnum, value_tag (csp)); + aarch64_register_set_tag (gdbarch, regcache, regnum, value_tag (csp)); } if (aarch64_debug) @@ -2832,7 +2832,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).data ()); - set_register_tag (gdbarch, regcache, regnum, value_tag (csp)); + aarch64_register_set_tag (gdbarch, regcache, regnum, value_tag (csp)); } if (aarch64_debug) @@ -3777,7 +3777,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 @@ -3855,7 +3855,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", @@ -4444,8 +4444,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; } @@ -5780,6 +5780,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 66c19059db2..f8038422033 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -1110,6 +1110,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) @@ -1117,6 +1126,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; +} + /* Static function declarations */ static void alloc_gdbarch_data (struct gdbarch *); diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index 90469d2cb6a..53c8a8d77cf 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -311,7 +311,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 ce95cf8343b..d33ac9260a0 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -1370,6 +1370,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) @@ -1512,6 +1565,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).data (); + + /* 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).data () + offset, + myaddr, curr_len); + put_frame_register (frame, regnum, value_contents_raw (value).data (), + 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, diff --git a/gdb/frame.h b/gdb/frame.h index b9a3793cc23..b33b716abff 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -653,6 +653,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, @@ -668,6 +672,10 @@ extern void put_frame_register_bytes (struct frame_info *frame, int regnum, CORE_ADDR offset, gdb::array_view buffer); +/* 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-components.py b/gdb/gdbarch-components.py index 663e056b0fd..825a48e2f97 100644 --- a/gdb/gdbarch-components.py +++ b/gdb/gdbarch-components.py @@ -1034,6 +1034,17 @@ Return the tag from a capability stored at address ADDR. invalid=False, ) +Method( + comment=""" +Set the tag from a capability stored at address ADDR to TAG. +""", + type="void", + name="set_cap_tag_from_address", + params=[("CORE_ADDR", "addr"), ("bool", "tag")], + predefault="default_set_cap_tag_from_address", + invalid=False, +) + Function( comment=""" Fetch the target specific address used to represent a load module. @@ -2710,3 +2721,18 @@ The default is to always return false. predefault="default_register_tag", invalid=False, ) + +Method( + comment=""" +Sets the register tag to TAG. +""", + type="void", + name="register_set_tag", + params=[ + ("regcache *", "regcache"), + ("int", "cookednum"), + ("bool", "tag"), + ], + predefault="default_register_set_tag", + invalid=False, +) diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 64f887e4dd6..4498bfcc84b 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -553,6 +553,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 bool gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch); @@ -1661,3 +1667,9 @@ extern void set_gdbarch_register_has_tag (struct gdbarch *gdbarch, gdbarch_regis 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); + +/* 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); diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index db62dd682a9..59e830e543d 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -131,6 +131,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; @@ -255,6 +256,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 @@ -330,6 +332,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; @@ -381,6 +384,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; @@ -498,6 +502,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 */ @@ -623,6 +628,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"), @@ -952,6 +958,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_filtered (file, "gdbarch_dump: get_cap_tag_from_address = <%s>\n", host_address_to_string (gdbarch->get_cap_tag_from_address)); + fprintf_filtered (file, + "gdbarch_dump: set_cap_tag_from_address = <%s>\n", + host_address_to_string (gdbarch->set_cap_tag_from_address)); fprintf_filtered (file, "gdbarch_dump: gdbarch_fetch_tls_load_module_address_p() = %d\n", gdbarch_fetch_tls_load_module_address_p (gdbarch)); @@ -1468,6 +1477,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_filtered (file, "gdbarch_dump: register_tag = <%s>\n", host_address_to_string (gdbarch->register_tag)); + fprintf_filtered (file, + "gdbarch_dump: register_set_tag = <%s>\n", + host_address_to_string (gdbarch->register_set_tag)); if (gdbarch->dump_tdep != NULL) gdbarch->dump_tdep (gdbarch, file); } @@ -3015,6 +3027,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; +} + bool gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch) { @@ -5462,3 +5491,20 @@ 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; +} diff --git a/gdb/remote.c b/gdb/remote.c index b941fbfdca4..53208a89038 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -8782,6 +8782,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 d1fe6d1024d..a0f2c31f34e 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1268,9 +1268,9 @@ value_assign (struct value *toval, struct value *fromval) value_contents (fromval).data ()); } else - put_frame_register_bytes (frame, value_reg, + put_frame_register_value (frame, value_reg, value_offset (toval), - value_contents (fromval)); + fromval); } gdb::observers::register_changed.notify (frame, value_reg); -- 2.47.2