From: Thiago Jung Bauermann Date: Sun, 20 Apr 2025 23:31:33 +0000 (-0300) Subject: Support unwinding tdesc parameters in the signal trampoline unwinder X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=acd65f30229a8a8b389394b5cf41b92e10ef24bb;p=thirdparty%2Fbinutils-gdb.git Support unwinding tdesc parameters in the signal trampoline unwinder --- diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index 20469e85017..6fe727fa021 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -1139,6 +1139,12 @@ default_fetch_tdesc_parameter (struct gdbarch *gdbarch, gdbarch_bfd_arch_info (get_current_arch ())->printable_name); } +static bool +default_register_is_variable_size (struct gdbarch *gdbarch, int regno) +{ + return false; +} + /* Non-zero if we want to trace architecture code. */ #ifndef GDBARCH_DEBUG diff --git a/gdb/dwarf2/frame.c b/gdb/dwarf2/frame.c index 1b4de0da2f6..1cf5f530249 100644 --- a/gdb/dwarf2/frame.c +++ b/gdb/dwarf2/frame.c @@ -1331,6 +1331,22 @@ dwarf2_frame_sniffer (const struct frame_unwind *self, return 1; } +static value * +dwarf2_frame_prev_tdesc_parameter (const frame_info_ptr &this_frame, + void **this_prologue_cache, + unsigned int parameter_id) +{ + /* There's no DWARF concept of target description parameter, so we can't + find it. But sometimes we may be asked about it so forward the + question to next frame's unwinder. This is analogous to the + DWARF2_FRAME_REG_UNSPECIFIED case in dwarf2_frame_prev_register. */ + frame_info_ptr next_frame = get_next_frame (this_frame); + if (next_frame == nullptr) + return nullptr; + else + return frame_unwind_tdesc_parameter_value (next_frame, parameter_id); +} + static const struct frame_unwind_legacy dwarf2_frame_unwind ( "dwarf2", NORMAL_FRAME, @@ -1340,7 +1356,9 @@ static const struct frame_unwind_legacy dwarf2_frame_unwind ( dwarf2_frame_prev_register, NULL, dwarf2_frame_sniffer, - dwarf2_frame_dealloc_cache + dwarf2_frame_dealloc_cache, + nullptr, /* prev_arch */ + dwarf2_frame_prev_tdesc_parameter ); static const struct frame_unwind_legacy dwarf2_signal_frame_unwind ( diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c index d8cfd66e07a..c03e24e9b39 100644 --- a/gdb/frame-unwind.c +++ b/gdb/frame-unwind.c @@ -306,8 +306,21 @@ struct value * frame_unwind_got_memory (const frame_info_ptr &frame, int regnum, CORE_ADDR addr) { struct gdbarch *gdbarch = frame_unwind_arch (frame); - struct value *v = value_at_lazy (register_type (gdbarch, regnum, &frame), - addr); + const frame_info_ptr *prev_frame_ptr = nullptr; + + if (gdbarch_register_is_variable_size (gdbarch, regnum)) + { + /* Resolve type using previous frame, because, that's the context in + which the type's dynamic properties need to be resolved. It's not + always possible to get the previous frame though (we may be in the + process of computing its frame id) so only do it for variable-size + registers, which is the case when register_type actually needs the + frame argument. */ + frame_info_ptr prev_frame = get_prev_frame (frame); + prev_frame_ptr = &prev_frame; + } + struct value *v = value_at_lazy (register_type (gdbarch, regnum, + prev_frame_ptr), addr); v->set_stack (true); return v; diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index e82972a3473..275145d69a0 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -263,6 +263,7 @@ struct gdbarch gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes; gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context; gdbarch_fetch_tdesc_parameter_ftype *fetch_tdesc_parameter = default_fetch_tdesc_parameter; + gdbarch_register_is_variable_size_ftype *register_is_variable_size = default_register_is_variable_size; }; /* Create a new ``struct gdbarch'' based on information provided by @@ -537,6 +538,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0. */ /* Skip verify of core_parse_exec_context, invalid_p == 0. */ /* Skip verify of fetch_tdesc_parameter, invalid_p == 0. */ + /* Skip verify of register_is_variable_size, invalid_p == 0. */ if (!log.empty ()) internal_error (_("verify_gdbarch: the following are invalid ...%s"), log.c_str ()); @@ -1411,6 +1413,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: fetch_tdesc_parameter = <%s>\n", host_address_to_string (gdbarch->fetch_tdesc_parameter)); + gdb_printf (file, + "gdbarch_dump: register_is_variable_size = <%s>\n", + host_address_to_string (gdbarch->register_is_variable_size)); if (gdbarch->dump_tdep != NULL) gdbarch->dump_tdep (gdbarch, file); } @@ -5573,3 +5578,20 @@ set_gdbarch_fetch_tdesc_parameter (struct gdbarch *gdbarch, { gdbarch->fetch_tdesc_parameter = fetch_tdesc_parameter; } + +bool +gdbarch_register_is_variable_size (struct gdbarch *gdbarch, int regno) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->register_is_variable_size != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_register_is_variable_size called\n"); + return gdbarch->register_is_variable_size (gdbarch, regno); +} + +void +set_gdbarch_register_is_variable_size (struct gdbarch *gdbarch, + gdbarch_register_is_variable_size_ftype register_is_variable_size) +{ + gdbarch->register_is_variable_size = register_is_variable_size; +} diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 7371c400e5f..d6058da93be 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -1807,3 +1807,9 @@ extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc typedef void (gdbarch_fetch_tdesc_parameter_ftype) (struct gdbarch *gdbarch, readable_regcache *regcache, unsigned int parameter_id); extern void gdbarch_fetch_tdesc_parameter (struct gdbarch *gdbarch, readable_regcache *regcache, unsigned int parameter_id); extern void set_gdbarch_fetch_tdesc_parameter (struct gdbarch *gdbarch, gdbarch_fetch_tdesc_parameter_ftype *fetch_tdesc_parameter); + +/* Can the size of this register change during runtime? */ + +typedef bool (gdbarch_register_is_variable_size_ftype) (struct gdbarch *gdbarch, int regno); +extern bool gdbarch_register_is_variable_size (struct gdbarch *gdbarch, int regno); +extern void set_gdbarch_register_is_variable_size (struct gdbarch *gdbarch, gdbarch_register_is_variable_size_ftype *register_is_variable_size); diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index 87694eb82b4..c893d5c941a 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -2862,3 +2862,14 @@ Obtain or calculate target description parameter. predefault="default_fetch_tdesc_parameter", invalid=False, ) + +Method( + comment=""" +Can the size of this register change during runtime? +""", + type="bool", + name="register_is_variable_size", + params=[("int", "regno")], + predefault="default_register_is_variable_size", + invalid=False, +) diff --git a/gdb/regcache.c b/gdb/regcache.c index ab98db02109..fd0824da5b8 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -375,9 +375,11 @@ reg_buffer::initialize_variable_size_registers () continue; } + /* Assume the register cache is for the current frame. */ + frame_info_ptr frame = get_current_frame (); m_variable_size_register_type[i] = resolve_dynamic_type (m_descr->register_type[i], {}, - /* Unused address. */ 0, nullptr); + /* Unused address. */ 0, &frame); ULONGEST size = m_variable_size_register_type[i]->length (); gdb_assert (size != 0); diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c index d1d55ad8290..06b65d59059 100644 --- a/gdb/target-descriptions.c +++ b/gdb/target-descriptions.c @@ -1045,6 +1045,16 @@ tdesc_register_type (struct gdbarch *gdbarch, int regno) return arch_reg->type; } +static bool +tdesc_register_is_variable_size (struct gdbarch *gdbarch, int regno) +{ + struct tdesc_reg *reg = tdesc_find_register (gdbarch, regno); + + gdb_assert (reg != nullptr); + + return reg->bitsize_parameter != nullptr; +} + static int tdesc_remote_register_number (struct gdbarch *gdbarch, int regno) { @@ -1238,6 +1248,8 @@ tdesc_use_registers (struct gdbarch *gdbarch, set_gdbarch_remote_register_number (gdbarch, tdesc_remote_register_number); set_gdbarch_register_reggroup_p (gdbarch, tdesc_register_reggroup_p); + set_gdbarch_register_is_variable_size (gdbarch, + tdesc_register_is_variable_size); } /* See gdbsupport/tdesc.h. */ diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c index 0e31ad37dae..4936de7740a 100644 --- a/gdb/tramp-frame.c +++ b/gdb/tramp-frame.c @@ -61,9 +61,10 @@ class frame_unwind_trampoline : public frame_unwind { public: frame_unwind_trampoline (enum frame_type type, const struct frame_data *data, - frame_prev_arch_ftype *prev_arch_func) + frame_prev_arch_ftype *prev_arch_func, + frame_prev_tdesc_parameter_ftype *prev_tdesc_parameter_func) : frame_unwind ("trampoline", type, FRAME_UNWIND_GDB, data), - m_prev_arch (prev_arch_func) + m_prev_arch (prev_arch_func), m_prev_tdesc_parameter (prev_tdesc_parameter_func) { } int sniff (const frame_info_ptr &this_frame, @@ -84,8 +85,22 @@ public: return m_prev_arch (this_frame, this_prologue_cache); } + /* Get the value of a target description parameter in a previous frame. */ + + value *prev_tdesc_parameter (const frame_info_ptr &this_frame, + void **this_prologue_cache, + unsigned int parameter_id) const override + { + if (m_prev_tdesc_parameter == nullptr) + return frame_unwind::prev_tdesc_parameter (this_frame, this_prologue_cache, + parameter_id); + return m_prev_tdesc_parameter (this_frame, this_prologue_cache, parameter_id); + } + private: frame_prev_arch_ftype *m_prev_arch; + + frame_prev_tdesc_parameter_ftype *m_prev_tdesc_parameter; }; void @@ -196,6 +211,7 @@ tramp_frame_prepend_unwinder (struct gdbarch *gdbarch, unwinder = obstack_new (gdbarch_obstack (gdbarch), tramp_frame->frame_type, data, - tramp_frame->prev_arch); + tramp_frame->prev_arch, + tramp_frame->prev_tdesc_parameter); frame_unwind_prepend_unwinder (gdbarch, unwinder); } diff --git a/gdb/tramp-frame.h b/gdb/tramp-frame.h index 204444150a0..43c8119f7e8 100644 --- a/gdb/tramp-frame.h +++ b/gdb/tramp-frame.h @@ -80,6 +80,9 @@ struct tramp_frame /* Given the current frame in THIS_FRAME and a frame cache in FRAME_CACHE, return the architecture of the previous frame. */ frame_prev_arch_ftype *prev_arch; + + /* FIXME: Document. */ + frame_prev_tdesc_parameter_ftype *prev_tdesc_parameter; }; void tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,