aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog45
-rw-r--r--gdb/breakpoint.c98
-rw-r--r--gdb/breakpoint.h16
-rw-r--r--gdb/elfread.c109
-rw-r--r--gdb/minsyms.c20
-rw-r--r--gdb/symtab.h9
6 files changed, 286 insertions, 11 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 0895286..03885dd 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,50 @@
2011-03-28 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.
+
+2011-03-28 Jan Kratochvil <jan.kratochvil@redhat.com>
+
STT_GNU_IFUNC reader implementation.
* elfread.c: Include gdbtypes.h, value.h and infcall.h.
(SYMBOL_GOT_PLT_SUFFIX, elf_rel_plt_read)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 1a9d963..c300df9 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -3504,6 +3504,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;
@@ -4378,7 +4380,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
@@ -4386,12 +4388,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. */
@@ -4521,6 +4524,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);
@@ -4529,6 +4542,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)
@@ -4558,6 +4574,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;
}
@@ -4715,6 +4748,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])))
@@ -4849,6 +4884,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);
@@ -5124,7 +5161,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
@@ -5620,6 +5658,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:
@@ -5726,9 +5766,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);
@@ -5736,8 +5779,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);
}
@@ -5812,7 +5880,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 ();
@@ -6929,7 +6998,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;
@@ -7029,6 +7098,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;
@@ -7039,6 +7109,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:
@@ -7098,6 +7170,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;
}
@@ -7158,7 +7231,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;
}
@@ -10399,7 +10473,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)
{
@@ -10531,6 +10605,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;
@@ -10701,6 +10776,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;
}
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index d5af928..2cb56b7 100644
--- 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. */
@@ -890,6 +903,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 *);
diff --git a/gdb/elfread.c b/gdb/elfread.c
index 115251c..68bed7e 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -41,6 +41,8 @@
#include "gdbtypes.h"
#include "value.h"
#include "infcall.h"
+#include "gdbthread.h"
+#include "regcache.h"
extern void _initialize_elfread (void);
@@ -948,6 +950,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
diff --git a/gdb/minsyms.c b/gdb/minsyms.c
index 80f9497..b054e3f 100644
--- a/gdb/minsyms.c
+++ b/gdb/minsyms.c
@@ -729,12 +729,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. */
diff --git a/gdb/symtab.h b/gdb/symtab.h
index bc48d3c..abe5e86 100644
--- 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;