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.
This commit is contained in:
Jan Kratochvil 2011-03-28 20:29:51 +00:00
parent 07be84bf8f
commit 0e30163f12
6 changed files with 286 additions and 11 deletions

View File

@ -1,3 +1,48 @@
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.

View File

@ -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;
}

View File

@ -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 *);

View File

@ -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

View File

@ -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. */

View File

@ -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;