[SPARC64]: Add kretprobe support.
Passes the smoke tests at least, powerpc implementation was used as a guide. Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
13fa14e185
commit
d38f122066
2 changed files with 115 additions and 2 deletions
|
@ -480,8 +480,117 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* architecture specific initialization */
|
/* Called with kretprobe_lock held. The value stored in the return
|
||||||
int arch_init_kprobes(void)
|
* address register is actually 2 instructions before where the
|
||||||
|
* callee will return to. Sequences usually look something like this
|
||||||
|
*
|
||||||
|
* call some_function <--- return register points here
|
||||||
|
* nop <--- call delay slot
|
||||||
|
* whatever <--- where callee returns to
|
||||||
|
*
|
||||||
|
* To keep trampoline_probe_handler logic simpler, we normalize the
|
||||||
|
* value kept in ri->ret_addr so we don't need to keep adjusting it
|
||||||
|
* back and forth.
|
||||||
|
*/
|
||||||
|
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
|
ri->ret_addr = (kprobe_opcode_t *)(regs->u_regs[UREG_RETPC] + 8);
|
||||||
|
|
||||||
|
/* Replace the return addr with trampoline addr */
|
||||||
|
regs->u_regs[UREG_RETPC] =
|
||||||
|
((unsigned long)kretprobe_trampoline) - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called when the probe at kretprobe trampoline is hit
|
||||||
|
*/
|
||||||
|
int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct kretprobe_instance *ri = NULL;
|
||||||
|
struct hlist_head *head, empty_rp;
|
||||||
|
struct hlist_node *node, *tmp;
|
||||||
|
unsigned long flags, orig_ret_address = 0;
|
||||||
|
unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
|
||||||
|
|
||||||
|
INIT_HLIST_HEAD(&empty_rp);
|
||||||
|
spin_lock_irqsave(&kretprobe_lock, flags);
|
||||||
|
head = kretprobe_inst_table_head(current);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It is possible to have multiple instances associated with a given
|
||||||
|
* task either because an multiple functions in the call path
|
||||||
|
* have a return probe installed on them, and/or more then one return
|
||||||
|
* return probe was registered for a target function.
|
||||||
|
*
|
||||||
|
* We can handle this because:
|
||||||
|
* - instances are always inserted at the head of the list
|
||||||
|
* - when multiple return probes are registered for the same
|
||||||
|
* function, the first instance's ret_addr will point to the
|
||||||
|
* real return address, and all the rest will point to
|
||||||
|
* kretprobe_trampoline
|
||||||
|
*/
|
||||||
|
hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
|
||||||
|
if (ri->task != current)
|
||||||
|
/* another task is sharing our hash bucket */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ri->rp && ri->rp->handler)
|
||||||
|
ri->rp->handler(ri, regs);
|
||||||
|
|
||||||
|
orig_ret_address = (unsigned long)ri->ret_addr;
|
||||||
|
recycle_rp_inst(ri, &empty_rp);
|
||||||
|
|
||||||
|
if (orig_ret_address != trampoline_address)
|
||||||
|
/*
|
||||||
|
* This is the real return address. Any other
|
||||||
|
* instances associated with this task are for
|
||||||
|
* other calls deeper on the call stack
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
kretprobe_assert(ri, orig_ret_address, trampoline_address);
|
||||||
|
regs->tpc = orig_ret_address;
|
||||||
|
regs->tnpc = orig_ret_address + 4;
|
||||||
|
|
||||||
|
reset_current_kprobe();
|
||||||
|
spin_unlock_irqrestore(&kretprobe_lock, flags);
|
||||||
|
preempt_enable_no_resched();
|
||||||
|
|
||||||
|
hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
|
||||||
|
hlist_del(&ri->hlist);
|
||||||
|
kfree(ri);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* By returning a non-zero value, we are telling
|
||||||
|
* kprobe_handler() that we don't want the post_handler
|
||||||
|
* to run (and have re-enabled preemption)
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kretprobe_trampoline_holder(void)
|
||||||
|
{
|
||||||
|
asm volatile(".global kretprobe_trampoline\n"
|
||||||
|
"kretprobe_trampoline:\n"
|
||||||
|
"\tnop\n"
|
||||||
|
"\tnop\n");
|
||||||
|
}
|
||||||
|
static struct kprobe trampoline_p = {
|
||||||
|
.addr = (kprobe_opcode_t *) &kretprobe_trampoline,
|
||||||
|
.pre_handler = trampoline_probe_handler
|
||||||
|
};
|
||||||
|
|
||||||
|
int __init arch_init_kprobes(void)
|
||||||
|
{
|
||||||
|
return register_kprobe(&trampoline_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int __kprobes arch_trampoline_kprobe(struct kprobe *p)
|
||||||
|
{
|
||||||
|
if (p->addr == (kprobe_opcode_t *)&kretprobe_trampoline)
|
||||||
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,15 @@ typedef u32 kprobe_opcode_t;
|
||||||
|
|
||||||
#define arch_remove_kprobe(p) do {} while (0)
|
#define arch_remove_kprobe(p) do {} while (0)
|
||||||
|
|
||||||
|
#define ARCH_SUPPORTS_KRETPROBES
|
||||||
|
|
||||||
#define flush_insn_slot(p) \
|
#define flush_insn_slot(p) \
|
||||||
do { flushi(&(p)->ainsn.insn[0]); \
|
do { flushi(&(p)->ainsn.insn[0]); \
|
||||||
flushi(&(p)->ainsn.insn[1]); \
|
flushi(&(p)->ainsn.insn[1]); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
void kretprobe_trampoline(void);
|
||||||
|
|
||||||
/* Architecture specific copy of original instruction*/
|
/* Architecture specific copy of original instruction*/
|
||||||
struct arch_specific_insn {
|
struct arch_specific_insn {
|
||||||
/* copy of the original instruction */
|
/* copy of the original instruction */
|
||||||
|
|
Reference in a new issue