]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Support unwinding tdesc parameters in the signal trampoline unwinder
authorThiago Jung Bauermann <thiago.bauermann@linaro.org>
Sun, 20 Apr 2025 23:31:33 +0000 (20:31 -0300)
committerThiago Jung Bauermann <thiago.bauermann@linaro.org>
Thu, 23 Oct 2025 02:47:59 +0000 (23:47 -0300)
gdb/arch-utils.c
gdb/dwarf2/frame.c
gdb/frame-unwind.c
gdb/gdbarch-gen.c
gdb/gdbarch-gen.h
gdb/gdbarch_components.py
gdb/regcache.c
gdb/target-descriptions.c
gdb/tramp-frame.c
gdb/tramp-frame.h

index fbcbc1e82fa6d231e4068cc7ad12533994cfcb02..46603487ebfa2d4c571218db85458de4f680738b 100644 (file)
@@ -1133,6 +1133,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
index 1570fd8d714785d9ba7b4fd59186c98ecc8c963a..2ef14f8a7600ccf68576b083031faecdff438167 100644 (file)
@@ -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 (
index 8f86fdc4825dcc7aaabc71ed549f7458596ffdd0..2aeb0d992f6fc75305d508134d41fef8b4c6a7e8 100644 (file)
@@ -316,8 +316,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;
index c5c6f2b5bb159db1b1c7e5ce8cbc6d875e48077e..aa322c8cf6b77e40fa72c0f393261312a52d706d 100644 (file)
@@ -263,6 +263,7 @@ struct gdbarch
   gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
   gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer;
   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 shadow_stack_push, has predicate.  */
   /* Skip verify of get_shadow_stack_pointer, 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 ());
@@ -1414,6 +1416,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);
 }
@@ -5583,3 +5588,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;
+}
index 25a199a6c0d03014431e7e576f09cb31a90c6089..ad76d167d1fb8ba7c291e8df1a4272884bb7f7f8 100644 (file)
@@ -1819,3 +1819,9 @@ extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbar
 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);
index 780dafb4ef9a5e221ca9429459531a332879efb9..28d31e60df3fdb0958a38f5a443f70a4fbcc572c 100644 (file)
@@ -2887,3 +2887,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,
+)
index 3cd913edebe15bd316dbb68c7667a0c0efa61261..03856ab867a75a01f64853b03e0e219b0f46688a 100644 (file)
@@ -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);
index 72418b1c4c7e7f8efb64b34f4d060933c3384ee5..7fc53d3ca59518b5c2d45b9a3ec8a000c27cf524 100644 (file)
@@ -1041,6 +1041,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)
 {
@@ -1234,6 +1244,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.  */
index 5fea5bbbd696b1f8b33b4e2f48e2cafafb330338..58c408ce76ac5c16d3c8f15f61ad008de700883e 100644 (file)
@@ -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 <frame_unwind_trampoline> (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);
 }
index 204444150a08e1fc2c90d82d1f0483d780b71d57..43c8119f7e8b8262e578b38bd89cedd2ee55de44 100644 (file)
@@ -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,