This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch 6/7] STT_GNU_IFUNC resolution via breakpoints


Hi,

this is the core functionality which implements the so-called "awkward"
two-step resolution requested in
	http://sourceware.org/ml/gdb-patches/2010-02/msg00419.html

It touches bpstat_what so it is a fragile change.


Thanks,
Jan


gdb/
2011-03-19  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Support resolution of STT_GNU_IFUNC via breakpoints.
	* breakpoint.c (print_it_typical): Support bp_gnu_ifunc_resolver and
	bp_gnu_ifunc_resolver_return.
	(bpstat_what): Rename parameter to bs_head, new variable bs, adjust
	the loop.  Support bp_gnu_ifunc_resolver and
	bp_gnu_ifunc_resolver_return.  New comment after the loop.  New loop
	for bp_gnu_ifunc_resolver and bp_gnu_ifunc_resolver_return
	breakpoints.
	(bptype_string, print_one_breakpoint_location): Support
	bp_gnu_ifunc_resolver and bp_gnu_ifunc_resolver_return.
	(user_settable_breakpoint): Return true also for
	bp_gnu_ifunc_resolver.
	(allocate_bp_location): Support bp_gnu_ifunc_resolver and
	bp_gnu_ifunc_resolver_return.
	(set_breakpoint_location_function): New parameter explicit_loc,
	describe it.  Call find_pc_partial_function_gnu_ifunc with new
	variable IS_GNU_IFUNC and adjust the address for STT_GNU_IFUNC if
	EXPLICIT_LOC is not set.
	(set_raw_breakpoint): Set EXPLICIT_LOC for
	set_breakpoint_location_function.
	(clone_momentary_breakpoint): Use true for EXPLICIT_LOC of
	set_breakpoint_location_function.
	(mention): Support bp_gnu_ifunc_resolver and
	bp_gnu_ifunc_resolver_return.
	(add_location_to_breakpoint): Set EXPLICIT_LOC for
	set_breakpoint_location_function.
	(update_breakpoint_locations): Remove static.
	(breakpoint_re_set_one): Support bp_gnu_ifunc_resolver and
	bp_gnu_ifunc_resolver_return.
	* breakpoint.h (enum bptype): New fields bp_gnu_ifunc_resolver and
	bp_gnu_ifunc_resolver_return.
	(update_breakpoint_locations): New declaration.
	* elfread.c: Include gdbthread.h and regcache.h.
	(elf_gnu_ifunc_resolver_stop, elf_gnu_ifunc_resolver_return_stop): New
	functions.
	(elf_gnu_ifunc_fns): Install them.
	* minsyms.c (stub_gnu_ifunc_resolver_stop)
	(stub_gnu_ifunc_resolver_return_stop): New functions.
	(stub_gnu_ifunc_fns): Install them.
	* symtab.h (struct gnu_ifunc_fns): New fields gnu_ifunc_resolver_stop
	and gnu_ifunc_resolver_return_stop.
	(gnu_ifunc_resolver_stop, gnu_ifunc_resolver_return_stop): New.

--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -3503,6 +3503,8 @@ print_it_typical (bpstat bs)
     case bp_tracepoint:
     case bp_fast_tracepoint:
     case bp_jit_event:
+    case bp_gnu_ifunc_resolver:
+    case bp_gnu_ifunc_resolver_return:
     default:
       result = PRINT_UNKNOWN;
       break;
@@ -4377,7 +4379,7 @@ handle_jit_event (void)
 /* Decide what infrun needs to do with this bpstat.  */
 
 struct bpstat_what
-bpstat_what (bpstat bs)
+bpstat_what (bpstat bs_head)
 {
   struct bpstat_what retval;
   /* We need to defer calling `solib_add', as adding new symbols
@@ -4385,12 +4387,13 @@ bpstat_what (bpstat bs)
      and hence may clear unprocessed entries in the BS chain.  */
   int shlib_event = 0;
   int jit_event = 0;
+  bpstat bs;
 
   retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
   retval.call_dummy = STOP_NONE;
   retval.is_longjmp = 0;
 
-  for (; bs != NULL; bs = bs->next)
+  for (bs = bs_head; bs != NULL; bs = bs->next)
     {
       /* Extract this BS's action.  After processing each BS, we check
 	 if its action overrides all we've seem so far.  */
@@ -4520,6 +4523,16 @@ bpstat_what (bpstat bs)
 	     out already.  */
 	  internal_error (__FILE__, __LINE__,
 			  _("bpstat_what: tracepoint encountered"));
+	  break;
+	case bp_gnu_ifunc_resolver:
+	  /* Step over it (and insert bp_gnu_ifunc_resolver_return).  */
+	  this_action = BPSTAT_WHAT_SINGLE;
+	  break;
+	case bp_gnu_ifunc_resolver_return:
+	  /* The breakpoint will be removed, execution will restart from the
+	     PC of the former breakpoint.  */
+	  this_action = BPSTAT_WHAT_KEEP_CHECKING;
+	  break;
 	default:
 	  internal_error (__FILE__, __LINE__,
 			  _("bpstat_what: unhandled bptype %d"), (int) bptype);
@@ -4528,6 +4541,9 @@ bpstat_what (bpstat bs)
       retval.main_action = max (retval.main_action, this_action);
     }
 
+  /* These operations may affect the bs->breakpoint_at state so they are
+     delayed after MAIN_ACTION is decided above.  */
+
   if (shlib_event)
     {
       if (debug_infrun)
@@ -4557,6 +4573,23 @@ bpstat_what (bpstat bs)
       handle_jit_event ();
     }
 
+  for (bs = bs_head; bs != NULL; bs = bs->next)
+    {
+      struct breakpoint *b = bs->breakpoint_at;
+
+      if (b == NULL)
+	continue;
+      switch (b->type)
+	{
+	case bp_gnu_ifunc_resolver:
+	  gnu_ifunc_resolver_stop (b);
+	  break;
+	case bp_gnu_ifunc_resolver_return:
+	  gnu_ifunc_resolver_return_stop (b);
+	  break;
+	}
+    }
+
   return retval;
 }
 
@@ -4712,6 +4745,8 @@ bptype_string (enum bptype type)
     {bp_fast_tracepoint, "fast tracepoint"},
     {bp_static_tracepoint, "static tracepoint"},
     {bp_jit_event, "jit events"},
+    {bp_gnu_ifunc_resolver, "STT_GNU_IFUNC resolver"},
+    {bp_gnu_ifunc_resolver_return, "STT_GNU_IFUNC resolver return"},
   };
 
   if (((int) type >= (sizeof (bptypes) / sizeof (bptypes[0])))
@@ -4846,6 +4881,8 @@ print_one_breakpoint_location (struct breakpoint *b,
       case bp_fast_tracepoint:
       case bp_static_tracepoint:
       case bp_jit_event:
+      case bp_gnu_ifunc_resolver:
+      case bp_gnu_ifunc_resolver_return:
 	if (opts.addressprint)
 	  {
 	    annotate_field (4);
@@ -5121,7 +5158,8 @@ user_settable_breakpoint (const struct breakpoint *b)
 	  || b->type == bp_catchpoint
 	  || b->type == bp_hardware_breakpoint
 	  || is_tracepoint (b)
-	  || is_watchpoint (b));
+	  || is_watchpoint (b)
+	  || b->type == bp_gnu_ifunc_resolver);
 }
 
 /* Return true if this breakpoint was set by the user, false if it is
@@ -5617,6 +5655,8 @@ allocate_bp_location (struct breakpoint *bpt)
     case bp_longjmp_master:
     case bp_std_terminate_master:
     case bp_exception_master:
+    case bp_gnu_ifunc_resolver:
+    case bp_gnu_ifunc_resolver_return:
       loc->loc_type = bp_loc_software_breakpoint;
       break;
     case bp_hardware_breakpoint:
@@ -5723,9 +5763,12 @@ set_raw_breakpoint_without_location (struct gdbarch *gdbarch,
   return b;
 }
 
-/* Initialize loc->function_name.  */
+/* Initialize loc->function_name.  EXPLICIT_LOC says no indirect function
+   resolutions should be made as the user specified the location explicitly
+   enough.  */
+
 static void
-set_breakpoint_location_function (struct bp_location *loc)
+set_breakpoint_location_function (struct bp_location *loc, int explicit_loc)
 {
   gdb_assert (loc->owner != NULL);
 
@@ -5733,8 +5776,33 @@ set_breakpoint_location_function (struct bp_location *loc)
       || loc->owner->type == bp_hardware_breakpoint
       || is_tracepoint (loc->owner))
     {
-      find_pc_partial_function (loc->address, &(loc->function_name), 
-				NULL, NULL);
+      int is_gnu_ifunc;
+
+      find_pc_partial_function_gnu_ifunc (loc->address, &loc->function_name,
+					  NULL, NULL, &is_gnu_ifunc);
+
+      if (is_gnu_ifunc && !explicit_loc)
+	{
+	  struct breakpoint *b = loc->owner;
+
+	  gdb_assert (loc->pspace == current_program_space);
+	  if (gnu_ifunc_resolve_name (loc->function_name,
+				      &loc->requested_address))
+	    {
+	      /* Recalculate ADDRESS based on new REQUESTED_ADDRESS.  */
+	      loc->address = adjust_breakpoint_address (loc->gdbarch,
+							loc->requested_address,
+							b->type);
+	    }
+	  else if (b->type == bp_breakpoint && b->loc == loc
+	           && loc->next == NULL && b->related_breakpoint == b)
+	    {
+	      /* Create only the whole new breakpoint of this type but do not
+		 mess more complicated breakpoints with multiple locations.  */
+	      b->type = bp_gnu_ifunc_resolver;
+	    }
+	}
+
       if (loc->function_name)
 	loc->function_name = xstrdup (loc->function_name);
     }
@@ -5809,7 +5877,8 @@ set_raw_breakpoint (struct gdbarch *gdbarch,
   b->loc->section = sal.section;
   b->line_number = sal.line;
 
-  set_breakpoint_location_function (b->loc);
+  set_breakpoint_location_function (b->loc,
+				    sal.explicit_pc || sal.explicit_line);
 
   breakpoints_changed ();
 
@@ -6926,7 +6995,7 @@ clone_momentary_breakpoint (struct breakpoint *orig)
 
   copy = set_raw_breakpoint_without_location (orig->gdbarch, orig->type);
   copy->loc = allocate_bp_location (copy);
-  set_breakpoint_location_function (copy->loc);
+  set_breakpoint_location_function (copy->loc, 1);
 
   copy->loc->gdbarch = orig->loc->gdbarch;
   copy->loc->requested_address = orig->loc->requested_address;
@@ -7026,6 +7095,7 @@ mention (struct breakpoint *b)
 	do_cleanups (ui_out_chain);
 	break;
       case bp_breakpoint:
+      case bp_gnu_ifunc_resolver:
 	if (ui_out_is_mi_like_p (uiout))
 	  {
 	    say_where = 0;
@@ -7036,6 +7106,8 @@ mention (struct breakpoint *b)
 	else
 	  printf_filtered (_("Breakpoint"));
 	printf_filtered (_(" %d"), b->number);
+	if (b->type == bp_gnu_ifunc_resolver)
+	  printf_filtered (_(" at gnu-indirect-function resolver"));
 	say_where = 1;
 	break;
       case bp_hardware_breakpoint:
@@ -7095,6 +7167,7 @@ mention (struct breakpoint *b)
       case bp_longjmp_master:
       case bp_std_terminate_master:
       case bp_exception_master:
+      case bp_gnu_ifunc_resolver_return:
 	break;
       }
 
@@ -7155,7 +7228,8 @@ add_location_to_breakpoint (struct breakpoint *b,
   gdb_assert (loc->pspace != NULL);
   loc->section = sal->section;
 
-  set_breakpoint_location_function (loc);
+  set_breakpoint_location_function (loc,
+				    sal->explicit_pc || sal->explicit_line);
   return loc;
 }
 
@@ -10394,7 +10468,7 @@ update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
   return sal;
 }
 
-static void
+void
 update_breakpoint_locations (struct breakpoint *b,
 			     struct symtabs_and_lines sals)
 {
@@ -10526,6 +10600,7 @@ breakpoint_re_set_one (void *bint)
     case bp_tracepoint:
     case bp_fast_tracepoint:
     case bp_static_tracepoint:
+    case bp_gnu_ifunc_resolver:
       /* Do not attempt to re-set breakpoints disabled during startup.  */
       if (b->enable_state == bp_startup_disabled)
 	return 0;
@@ -10696,6 +10771,7 @@ breakpoint_re_set_one (void *bint)
     case bp_exception:
     case bp_exception_resume:
     case bp_jit_event:
+    case bp_gnu_ifunc_resolver_return:
       break;
     }
 
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -149,6 +149,19 @@ enum bptype
 
     /* Event for JIT compiled code generation or deletion.  */
     bp_jit_event,
+
+    /* Breakpoint is placed at the STT_GNU_IFUNC resolver.  When hit GDB
+       inserts new bp_gnu_ifunc_resolver_return at the caller.
+       bp_gnu_ifunc_resolver is still being kept here as a different thread
+       may still hit it before bp_gnu_ifunc_resolver_return is hit by the
+       original thread.  */
+    bp_gnu_ifunc_resolver,
+
+    /* On its hit GDB now know the resolved address of the target
+       STT_GNU_IFUNC function.  Associated bp_gnu_ifunc_resolver can be
+       deleted now and the breakpoint moved to the target function entry
+       point.  */
+    bp_gnu_ifunc_resolver_return,
   };
 
 /* States of enablement of breakpoint.  */
@@ -888,6 +901,9 @@ extern int breakpoint_thread_match (struct address_space *,
 
 extern void until_break_command (char *, int, int);
 
+extern void update_breakpoint_locations (struct breakpoint *b,
+					 struct symtabs_and_lines sals);
+
 extern void breakpoint_re_set (void);
 
 extern void breakpoint_re_set_thread (struct breakpoint *);
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -40,6 +40,8 @@
 #include "gdbtypes.h"
 #include "value.h"
 #include "infcall.h"
+#include "gdbthread.h"
+#include "regcache.h"
 
 extern void _initialize_elfread (void);
 
@@ -947,6 +949,111 @@ elf_gnu_ifunc_resolve_addr (struct gdbarch *gdbarch, CORE_ADDR pc)
   return address;
 }
 
+/* Handle inferior hit of bp_gnu_ifunc_resolver, see its definition.  */
+
+static void
+elf_gnu_ifunc_resolver_stop (struct breakpoint *b)
+{
+  struct breakpoint *b_return;
+  struct frame_info *prev_frame = get_prev_frame (get_current_frame ());
+  struct frame_id prev_frame_id = get_stack_frame_id (prev_frame);
+  CORE_ADDR prev_pc = get_frame_pc (prev_frame);
+  int thread_id = pid_to_thread_id (inferior_ptid);
+
+  gdb_assert (b->type == bp_gnu_ifunc_resolver);
+
+  for (b_return = b->related_breakpoint; b_return != b;
+       b_return = b_return->related_breakpoint)
+    {
+      gdb_assert (b_return->type == bp_gnu_ifunc_resolver_return);
+      gdb_assert (b_return->loc != NULL && b_return->loc->next == NULL);
+      gdb_assert (frame_id_p (b_return->frame_id));
+
+      if (b_return->thread == thread_id
+	  && b_return->loc->requested_address == prev_pc
+	  && frame_id_eq (b_return->frame_id, prev_frame_id))
+	break;
+    }
+
+  if (b_return == b)
+    {
+      struct symtab_and_line sal;
+
+      /* No need to call find_pc_line for symbols resolving as this is only
+	 a helper breakpointer never shown to the user.  */
+
+      init_sal (&sal);
+      sal.pspace = current_inferior ()->pspace;
+      sal.pc = prev_pc;
+      sal.section = find_pc_overlay (sal.pc);
+      sal.explicit_pc = 1;
+      b_return = set_momentary_breakpoint (get_frame_arch (prev_frame), sal,
+					   prev_frame_id,
+					   bp_gnu_ifunc_resolver_return);
+
+      /* Add new b_return to the ring list b->related_breakpoint.  */
+      gdb_assert (b_return->related_breakpoint == b_return);
+      b_return->related_breakpoint = b->related_breakpoint;
+      b->related_breakpoint = b_return;
+    }
+}
+
+/* Handle inferior hit of bp_gnu_ifunc_resolver_return, see its definition.  */
+
+static void
+elf_gnu_ifunc_resolver_return_stop (struct breakpoint *b)
+{
+  struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+  struct type *func_func_type = builtin_type (gdbarch)->builtin_func_func;
+  struct type *value_type = TYPE_TARGET_TYPE (func_func_type);
+  struct regcache *regcache = get_thread_regcache (inferior_ptid);
+  struct value *value;
+  CORE_ADDR resolved_address, resolved_pc;
+  struct symtab_and_line sal;
+  struct symtabs_and_lines sals;
+
+  gdb_assert (b->type == bp_gnu_ifunc_resolver_return);
+
+  value = allocate_value (value_type);
+  gdbarch_return_value (gdbarch, func_func_type, value_type, regcache,
+			value_contents_raw (value), NULL);
+  resolved_address = value_as_address (value);
+  resolved_pc = gdbarch_convert_from_func_ptr_addr (gdbarch,
+						    resolved_address,
+						    &current_target);
+
+  while (b->related_breakpoint != b)
+    {
+      struct breakpoint *b_next = b->related_breakpoint;
+
+      switch (b->type)
+	{
+	case bp_gnu_ifunc_resolver:
+	  break;
+	case bp_gnu_ifunc_resolver_return:
+	  delete_breakpoint (b);
+	  break;
+	default:
+	  internal_error (__FILE__, __LINE__,
+			  _("handle_inferior_event: Invalid "
+			    "gnu-indirect-function breakpoint type %d"),
+			  (int) b->type);
+	}
+      b = b_next;
+    }
+  gdb_assert (b->type == bp_gnu_ifunc_resolver);
+
+  gdb_assert (current_program_space == b->pspace);
+  elf_gnu_ifunc_record_cache (b->addr_string, resolved_pc);
+
+  sal = find_pc_line (resolved_pc, 0);
+  sals.nelts = 1;
+  sals.sals = &sal;
+
+  b->type = bp_breakpoint;
+  update_breakpoint_locations (b, sals);
+}
+
 struct build_id
   {
     size_t size;
@@ -1502,6 +1609,8 @@ static const struct gnu_ifunc_fns elf_gnu_ifunc_fns =
 {
   elf_gnu_ifunc_resolve_addr,
   elf_gnu_ifunc_resolve_name,
+  elf_gnu_ifunc_resolver_stop,
+  elf_gnu_ifunc_resolver_return_stop
 };
 
 void
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -728,12 +728,32 @@ stub_gnu_ifunc_resolve_name (const char *function_name,
 	 function_name);
 }
 
+/* See elf_gnu_ifunc_resolver_stop for its real implementation.  */
+
+static void
+stub_gnu_ifunc_resolver_stop (struct breakpoint *b)
+{
+  internal_error (__FILE__, __LINE__,
+		  _("elf_gnu_ifunc_resolver_stop cannot be reached."));
+}
+
+/* See elf_gnu_ifunc_resolver_return_stop for its real implementation.  */
+
+static void
+stub_gnu_ifunc_resolver_return_stop (struct breakpoint *b)
+{
+  internal_error (__FILE__, __LINE__,
+		  _("elf_gnu_ifunc_resolver_return_stop cannot be reached."));
+}
+
 /* See elf_gnu_ifunc_fns for its real implementation.  */
 
 static const struct gnu_ifunc_fns stub_gnu_ifunc_fns =
 {
   stub_gnu_ifunc_resolve_addr,
   stub_gnu_ifunc_resolve_name,
+  stub_gnu_ifunc_resolver_stop,
+  stub_gnu_ifunc_resolver_return_stop,
 };
 
 /* A placeholder for &elf_gnu_ifunc_fns.  */
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1055,10 +1055,19 @@ struct gnu_ifunc_fns
   /* See elf_gnu_ifunc_resolve_name for its real implementation.  */
   int (*gnu_ifunc_resolve_name) (const char *function_name,
 				 CORE_ADDR *function_address_p);
+
+  /* See elf_gnu_ifunc_resolver_stop for its real implementation.  */
+  void (*gnu_ifunc_resolver_stop) (struct breakpoint *b);
+
+  /* See elf_gnu_ifunc_resolver_return_stop for its real implementation.  */
+  void (*gnu_ifunc_resolver_return_stop) (struct breakpoint *b);
 };
 
 #define gnu_ifunc_resolve_addr gnu_ifunc_fns_p->gnu_ifunc_resolve_addr
 #define gnu_ifunc_resolve_name gnu_ifunc_fns_p->gnu_ifunc_resolve_name
+#define gnu_ifunc_resolver_stop gnu_ifunc_fns_p->gnu_ifunc_resolver_stop
+#define gnu_ifunc_resolver_return_stop \
+  gnu_ifunc_fns_p->gnu_ifunc_resolver_return_stop
 
 extern const struct gnu_ifunc_fns *gnu_ifunc_fns_p;
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]