[PATCH] gdb.ada/catch_ex.exp, gdb.ada/mi_catch_ex.exp and unsupported catchpoints

Pedro Alves pedro@codesourcery.com
Sat Dec 10 22:53:00 GMT 2011


On Friday 09 December 2011 18:12:13, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
> 
> Pedro> Kidding aside, obviously we wouldn't need to convert
> Pedro> everything at once.  We could even get away with only handling
> Pedro> the simpler prototypes we care about at first.
> 
> Yeah.  And to be totally clear, if someone wants to do this, I am in
> favor of it.

Here's a start.  I didn't really try to confirm if I was extracting the
arguments correctly for any other function bug the Ada runtime's
function in question, but at least it works in the case we're
interested in.  :-)

Unfortunately, libgnat is fully stripped (at least on ubuntu), symbol
table and all, so there'd be a little repackaging work to do to get
rid of the debug info dependency -- otherwise, we still need debug
info to be able to insert the hook breakpoints (but no longer to
be able to extract the hook's argument).

2011-12-10  Pedro Alves  <pedro@codesourcery.com>

	* ada-lang.c (ada_unhandled_exception_name_addr): Extract the
	exception name without using debug info if the gdbarch supports
	it.
	(ada_exception_name_addr_1): Use ada_unhandled_exception_name_addr
	for ex_catch_exception.
	* amd64-tdep.c (amd64_extract_arguments_1)
	(amd64_extract_arguments): New.
	(amd64_init_abi): Install amd64_extract_arguments as
	extract_arguments gdbarch method.
	* i386-tdep.c (i386_extract_arguments): New.
	(i386_gdbarch_init): Install i386_extract_arguments as
	extract_arguments gdbarch method.
	* gdbarch.sh (extract_arguments): New.
	* gdbarch.h, gdbarch.c: Regenerate.
---

 gdb/ada-lang.c   |   61 ++++++++++++++++++++-
 gdb/amd64-tdep.c |  157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdbarch.c    |   33 +++++++++++
 gdb/gdbarch.h    |    6 ++
 gdb/gdbarch.sh   |    7 ++
 gdb/i386-tdep.c  |   50 +++++++++++++++++
 6 files changed, 311 insertions(+), 3 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 3843539..1aecfdb 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10804,7 +10804,63 @@ ada_find_printable_frame (struct frame_info *fi)
 static CORE_ADDR
 ada_unhandled_exception_name_addr (void)
 {
-  return parse_and_eval_address ("e.full_name");
+  struct frame_info *frame = get_current_frame ();
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+
+  /* Try to extract the arguments even if debug info for libgnat isn't
+     present.  */
+  if (gdbarch_extract_arguments_p (gdbarch))
+    {
+      struct type **args_in = alloca (1 * sizeof (struct type *));
+      struct value **args_out = alloca (1 * sizeof (struct value *));
+      struct type *void_ptr_type
+	= lookup_pointer_type (builtin_type (gdbarch)->builtin_void);
+      CORE_ADDR e;
+      struct value *full_name_val;
+      CORE_ADDR full_name;
+
+      /* Argument is a pointer to a system.standard_library.exception_data
+	 object.  E.g.,
+
+	 <__gnat_debug_raise_exception> (e=0x7ffff7dcc2e0) at s-except.adb:46
+	 (gdb) p e
+	 $1 = (access system.standard_library.exception_data) 0x7ffff7dcc2e0
+	 (gdb) p *e
+	 $2 = (
+	   not_handled_by_others => false,
+	   lang => 65 'A',
+	   name_length => 17,
+	   full_name => (system.address) 0x7ffff7b624a0,
+	   htable_ptr => 0x7ffff7dcc320,
+	   import_code => 0,
+	   raise_hook => 0
+	 )
+
+	 We're interested in FULL_NAME, which is at offset 8 of E in
+	 all ABIs we care about.
+      */
+
+      /* Extract the sole function's argument.  This is a pointer to a
+	 system.standard_library.exception_data object.  */
+      args_in[0] = void_ptr_type;
+      gdbarch_extract_arguments (gdbarch, frame, 1, args_in, args_out,
+				 NULL, NULL);
+
+      /* Get the argument pointer.  */
+      e = value_as_address (args_out[0]);
+
+      /* The FULL_NAME pointer is at offset 8 of the object pointed by
+	 E.  */
+      full_name_val = value_at (void_ptr_type, e + 8);
+      full_name = value_as_address (full_name_val);
+      return full_name;
+    }
+  else
+    {
+      /* No support for extracting arguments.  Will need to rely on
+	 debug info being present.  */
+      return parse_and_eval_address ("e.full_name");
+    }
 }
 
 /* Same as ada_unhandled_exception_name_addr, except that this function
@@ -10859,8 +10915,7 @@ ada_exception_name_addr_1 (enum exception_catchpoint_kind ex,
   switch (ex)
     {
       case ex_catch_exception:
-        return (parse_and_eval_address ("e.full_name"));
-        break;
+	return ada_unhandled_exception_name_addr ();
 
       case ex_catch_exception_unhandled:
         return exception_info->unhandled_exception_name_addr ();
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 803a20f..4f506ed 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -905,6 +905,162 @@ amd64_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
 
   return sp + 16;
 }
+
+static void
+amd64_extract_arguments_1 (struct frame_info *frame, int nargs,
+			   struct type **args_in, struct value **args_out,
+			   int struct_return)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  int *integer_regs = tdep->call_dummy_integer_regs;
+  int num_integer_regs = tdep->call_dummy_num_integer_regs;
+
+  static int sse_regnum[] =
+  {
+    /* %xmm0 ... %xmm7 */
+    AMD64_XMM0_REGNUM + 0, AMD64_XMM1_REGNUM,
+    AMD64_XMM0_REGNUM + 2, AMD64_XMM0_REGNUM + 3,
+    AMD64_XMM0_REGNUM + 4, AMD64_XMM0_REGNUM + 5,
+    AMD64_XMM0_REGNUM + 6, AMD64_XMM0_REGNUM + 7,
+  };
+  struct type **stack_args = alloca (nargs * sizeof (struct type *));
+  /* We ignore that some arguments that are passed by MEMORY are
+     mirrored in registers.  */
+  int num_stack_args = 0;
+  int element = 0;
+  int integer_reg = 0;
+  int sse_reg = 0;
+  int i;
+  CORE_ADDR sp;
+
+  gdb_assert (tdep->classify);
+
+  /* Reserve a register for the "hidden" argument.  */
+  if (struct_return)
+    integer_reg++;
+
+  for (i = 0; i < nargs; i++)
+    {
+      struct type *type = args_in[i];
+      int len = TYPE_LENGTH (type);
+      enum amd64_reg_class class[2];
+      int needed_integer_regs = 0;
+      int needed_sse_regs = 0;
+      int j;
+
+      /* Classify argument.  */
+      tdep->classify (type, class);
+
+      /* Calculate the number of integer and SSE registers needed for
+         this argument.  */
+      for (j = 0; j < 2; j++)
+	{
+	  if (class[j] == AMD64_INTEGER)
+	    needed_integer_regs++;
+	  else if (class[j] == AMD64_SSE)
+	    needed_sse_regs++;
+	}
+
+      /* Check whether the argument was passed in registers.  */
+      if (integer_reg + needed_integer_regs > num_integer_regs
+	  || sse_reg + needed_sse_regs > ARRAY_SIZE (sse_regnum)
+	  || (needed_integer_regs == 0 && needed_sse_regs == 0))
+	{
+	  /* The argument was passed on the stack.  */
+	  stack_args[num_stack_args] = args_in[i];
+          num_stack_args++;
+	}
+      else
+	{
+	  /* The argument was passed in registers.  We assume if more
+	     than one register was necessary, then N contiguous
+	     registers starting from REGNUM were used.  */
+
+	  struct value *reg_val;
+	  int regnum = -1;
+	  int offset = 0;
+
+	  switch (class[0])
+	    {
+	    case AMD64_INTEGER:
+	      regnum = integer_regs[integer_reg++];
+	      break;
+
+	    case AMD64_SSE:
+	      regnum = sse_regnum[sse_reg++];
+	      break;
+
+	    case AMD64_SSEUP:
+	      gdb_assert (sse_reg > 0);
+	      regnum = sse_regnum[sse_reg - 1];
+	      offset = 8;
+	      break;
+
+	    default:
+	      gdb_assert (!"Unexpected register class.");
+	    }
+
+	  gdb_assert (regnum != -1);
+
+	  reg_val = allocate_value_lazy (args_in[i]);
+	  VALUE_LVAL (reg_val) = lval_register;
+	  VALUE_REGNUM (reg_val) = regnum;
+	  VALUE_FRAME_ID (reg_val) = get_frame_id (frame);
+	  set_value_offset (reg_val, offset);
+
+	  args_out[i] = reg_val;
+	}
+    }
+
+  /* Get the first arg's slot, but unwind, rather than assuming a
+     regular frame with frame pointer.  */
+  sp = get_frame_register_unsigned (get_prev_frame (frame),
+				    AMD64_RSP_REGNUM);
+
+  /* Extract the stack arguments.  */
+  for (i = 0; i < num_stack_args; i++)
+    {
+      struct type *type = stack_args[i];
+      int len = TYPE_LENGTH (type);
+      CORE_ADDR arg_addr = sp + element * 8;
+
+      args_out[i] = value_from_contents_and_address (type, NULL, arg_addr);
+      element += ((len + 7) / 8);
+    }
+}
+
+/* Implementation of gdbarch method extract_arguments.  */
+
+static void
+amd64_extract_arguments (struct frame_info *frame,
+			 int nargs, struct type **args_in,
+			 struct value **args_out,
+			 struct type *struct_return_in,
+			 struct value **struct_return_out)
+{
+  /* Extract arguments.  */
+  amd64_extract_arguments_1 (frame, nargs, args_in, args_out,
+			     struct_return_in != NULL);
+
+  /* Extract "hidden" argument".  */
+  if (struct_return_in)
+    {
+      struct gdbarch *gdbarch = get_frame_arch (frame);
+      struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+      CORE_ADDR struct_addr;
+      /* The "hidden" argument is passed throught the first argument
+         register.  */
+      const int arg_regnum = tdep->call_dummy_integer_regs[0];
+
+      struct_addr = get_frame_register_unsigned (frame, arg_regnum);
+      *struct_return_out
+	= value_from_contents_and_address (struct_return_in,
+					   NULL,
+					   struct_addr);
+    }
+}
+
 

 /* Displaced instruction handling.  */
 
@@ -2656,6 +2812,7 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
     ARRAY_SIZE (amd64_dummy_call_integer_regs);
   tdep->call_dummy_integer_regs = amd64_dummy_call_integer_regs;
   tdep->classify = amd64_classify;
+  set_gdbarch_extract_arguments (gdbarch, amd64_extract_arguments);
 
   set_gdbarch_convert_register_p (gdbarch, i387_convert_register_p);
   set_gdbarch_register_to_value (gdbarch, i387_register_to_value);
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 1ada504..88e6728 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -183,6 +183,7 @@ struct gdbarch
   gdbarch_push_dummy_call_ftype *push_dummy_call;
   int call_dummy_location;
   gdbarch_push_dummy_code_ftype *push_dummy_code;
+  gdbarch_extract_arguments_ftype *extract_arguments;
   gdbarch_print_registers_info_ftype *print_registers_info;
   gdbarch_print_float_info_ftype *print_float_info;
   gdbarch_print_vector_info_ftype *print_vector_info;
@@ -338,6 +339,7 @@ struct gdbarch startup_gdbarch =
   0,  /* push_dummy_call */
   0,  /* call_dummy_location */
   0,  /* push_dummy_code */
+  0,  /* extract_arguments */
   default_print_registers_info,  /* print_registers_info */
   0,  /* print_float_info */
   0,  /* print_vector_info */
@@ -626,6 +628,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of push_dummy_call, has predicate.  */
   /* Skip verify of call_dummy_location, invalid_p == 0 */
   /* Skip verify of push_dummy_code, has predicate.  */
+  /* Skip verify of extract_arguments, has predicate.  */
   /* Skip verify of print_registers_info, invalid_p == 0 */
   /* Skip verify of print_float_info, has predicate.  */
   /* Skip verify of print_vector_info, has predicate.  */
@@ -907,6 +910,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
                       "gdbarch_dump: elf_make_msymbol_special = <%s>\n",
                       host_address_to_string (gdbarch->elf_make_msymbol_special));
   fprintf_unfiltered (file,
+                      "gdbarch_dump: gdbarch_extract_arguments_p() = %d\n",
+                      gdbarch_extract_arguments_p (gdbarch));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: extract_arguments = <%s>\n",
+                      host_address_to_string (gdbarch->extract_arguments));
+  fprintf_unfiltered (file,
                       "gdbarch_dump: fast_tracepoint_valid_at = <%s>\n",
                       host_address_to_string (gdbarch->fast_tracepoint_valid_at));
   fprintf_unfiltered (file,
@@ -2153,6 +2162,30 @@ set_gdbarch_push_dummy_code (struct gdbarch *gdbarch,
   gdbarch->push_dummy_code = push_dummy_code;
 }
 
+int
+gdbarch_extract_arguments_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->extract_arguments != NULL;
+}
+
+void
+gdbarch_extract_arguments (struct gdbarch *gdbarch, struct frame_info *frame, int nargs, struct type **args_in, struct value **args_out, struct type *struct_return_in, struct value **struct_return_out)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->extract_arguments != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_extract_arguments called\n");
+  gdbarch->extract_arguments (frame, nargs, args_in, args_out, struct_return_in, struct_return_out);
+}
+
+void
+set_gdbarch_extract_arguments (struct gdbarch *gdbarch,
+                               gdbarch_extract_arguments_ftype extract_arguments)
+{
+  gdbarch->extract_arguments = extract_arguments;
+}
+
 void
 gdbarch_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file, struct frame_info *frame, int regnum, int all)
 {
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 46c5afa..fb0a51c 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -356,6 +356,12 @@ typedef CORE_ADDR (gdbarch_push_dummy_code_ftype) (struct gdbarch *gdbarch, CORE
 extern CORE_ADDR gdbarch_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, CORE_ADDR funaddr, struct value **args, int nargs, struct type *value_type, CORE_ADDR *real_pc, CORE_ADDR *bp_addr, struct regcache *regcache);
 extern void set_gdbarch_push_dummy_code (struct gdbarch *gdbarch, gdbarch_push_dummy_code_ftype *push_dummy_code);
 
+extern int gdbarch_extract_arguments_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_extract_arguments_ftype) (struct frame_info *frame, int nargs, struct type **args_in, struct value **args_out, struct type *struct_return_in, struct value **struct_return_out);
+extern void gdbarch_extract_arguments (struct gdbarch *gdbarch, struct frame_info *frame, int nargs, struct type **args_in, struct value **args_out, struct type *struct_return_in, struct value **struct_return_out);
+extern void set_gdbarch_extract_arguments (struct gdbarch *gdbarch, gdbarch_extract_arguments_ftype *extract_arguments);
+
 typedef void (gdbarch_print_registers_info_ftype) (struct gdbarch *gdbarch, struct ui_file *file, struct frame_info *frame, int regnum, int all);
 extern void gdbarch_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file, struct frame_info *frame, int regnum, int all);
 extern void set_gdbarch_print_registers_info (struct gdbarch *gdbarch, gdbarch_print_registers_info_ftype *print_registers_info);
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index a9ca03d..d04f923 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -478,6 +478,13 @@ M:CORE_ADDR:push_dummy_call:struct value *function, struct regcache *regcache, C
 v:int:call_dummy_location::::AT_ENTRY_POINT::0
 M:CORE_ADDR:push_dummy_code:CORE_ADDR sp, CORE_ADDR funaddr, struct value **args, int nargs, struct type *value_type, CORE_ADDR *real_pc, CORE_ADDR *bp_addr, struct regcache *regcache:sp, funaddr, args, nargs, value_type, real_pc, bp_addr, regcache
 
+# Given a method prototype defined by NARGS args, with each arg of
+# corresponding type in the ARGS_IN type array, build NARGS value
+# objects that extract each of the arguments from the frame.  If
+# STRUCT_RETURN_IN is not NULL, then STRUCT_RETURN_OUT is set to value
+# that extracts the hidden argument of type STRUCT_RETURN_IN
+F:void:extract_arguments:struct frame_info *frame, int nargs, struct type **args_in, struct value **args_out, struct type *struct_return_in, struct value **struct_return_out:frame, nargs, args_in, args_out, struct_return_in, struct_return_out
+
 m:void:print_registers_info:struct ui_file *file, struct frame_info *frame, int regnum, int all:file, frame, regnum, all::default_print_registers_info::0
 M:void:print_float_info:struct ui_file *file, struct frame_info *frame, const char *args:file, frame, args
 M:void:print_vector_info:struct ui_file *file, struct frame_info *frame, const char *args:file, frame, args
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index a4e3a22..4ca873d 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -2417,6 +2417,54 @@ i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
   return sp + 8;
 }
 
+/* Implementation of gdbarch method extract_arguments.  */
+
+static void
+i386_extract_arguments (struct frame_info *frame,
+			int nargs, struct type **args_in,
+			struct value **args_out,
+			struct type *struct_return_in,
+			struct value **struct_return_out)
+{
+  int i;
+  CORE_ADDR sp = 0;
+  int args_space_used = 0;
+
+  /* Get the first arg's slot ( 8(%ebp) ), but unwind, rather than
+     assuming a regular frame with frame pointer.  */
+  sp = get_frame_register_unsigned (get_prev_frame (frame),
+				    I386_ESP_REGNUM);
+
+  if (struct_return_in)
+    {
+      *struct_return_out
+	= value_from_contents_and_address (struct_return_in, NULL, sp);
+
+      args_space_used += 4;
+    }
+
+  for (i = 0; i < nargs; i++)
+    {
+      int len = TYPE_LENGTH (args_in[i]);
+
+      if (i386_16_byte_align_p (args_in[i]))
+	args_space_used = align_up (args_space_used, 16);
+
+      args_out[i]
+	= value_from_contents_and_address (args_in[i], NULL,
+					   (sp + args_space_used));
+
+      /* The System V ABI says that:
+
+	 "An argument's size is increased, if necessary, to make it a
+	 multiple of [32-bit] words.  This may require tail padding,
+	 depending on the size of the argument."
+
+	 This makes sure the stack stays word-aligned.  */
+      args_space_used += align_up (len, 4);
+    }
+}
+
 /* These registers are used for returning integers (and on some
    targets also for returning `struct' and `union' values when their
    size and alignment match an integer type).  */
@@ -7369,6 +7417,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_push_dummy_call (gdbarch, i386_push_dummy_call);
   set_gdbarch_frame_align (gdbarch, i386_frame_align);
 
+  set_gdbarch_extract_arguments (gdbarch, i386_extract_arguments);
+
   set_gdbarch_convert_register_p (gdbarch, i386_convert_register_p);
   set_gdbarch_register_to_value (gdbarch,  i386_register_to_value);
   set_gdbarch_value_to_register (gdbarch, i386_value_to_register);



More information about the Gdb-patches mailing list