|
@@ -27,6 +27,8 @@
|
|
* <prasanna@in.ibm.com> adapted for x86_64
|
|
* <prasanna@in.ibm.com> adapted for x86_64
|
|
* 2005-Mar Roland McGrath <roland@redhat.com>
|
|
* 2005-Mar Roland McGrath <roland@redhat.com>
|
|
* Fixed to handle %rip-relative addressing mode correctly.
|
|
* Fixed to handle %rip-relative addressing mode correctly.
|
|
|
|
+ * 2005-May Rusty Lynch <rusty.lynch@intel.com>
|
|
|
|
+ * Added function return probes functionality
|
|
*/
|
|
*/
|
|
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/config.h>
|
|
@@ -240,6 +242,50 @@ static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
|
|
regs->rip = (unsigned long)p->ainsn.insn;
|
|
regs->rip = (unsigned long)p->ainsn.insn;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+struct task_struct *arch_get_kprobe_task(void *ptr)
|
|
|
|
+{
|
|
|
|
+ return ((struct thread_info *) (((unsigned long) ptr) &
|
|
|
|
+ (~(THREAD_SIZE -1))))->task;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void arch_prepare_kretprobe(struct kretprobe *rp, struct pt_regs *regs)
|
|
|
|
+{
|
|
|
|
+ unsigned long *sara = (unsigned long *)regs->rsp;
|
|
|
|
+ struct kretprobe_instance *ri;
|
|
|
|
+ static void *orig_ret_addr;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Save the return address when the return probe hits
|
|
|
|
+ * the first time, and use it to populate the (krprobe
|
|
|
|
+ * instance)->ret_addr for subsequent return probes at
|
|
|
|
+ * the same addrress since stack address would have
|
|
|
|
+ * the kretprobe_trampoline by then.
|
|
|
|
+ */
|
|
|
|
+ if (((void*) *sara) != kretprobe_trampoline)
|
|
|
|
+ orig_ret_addr = (void*) *sara;
|
|
|
|
+
|
|
|
|
+ if ((ri = get_free_rp_inst(rp)) != NULL) {
|
|
|
|
+ ri->rp = rp;
|
|
|
|
+ ri->stack_addr = sara;
|
|
|
|
+ ri->ret_addr = orig_ret_addr;
|
|
|
|
+ add_rp_inst(ri);
|
|
|
|
+ /* Replace the return addr with trampoline addr */
|
|
|
|
+ *sara = (unsigned long) &kretprobe_trampoline;
|
|
|
|
+ } else {
|
|
|
|
+ rp->nmissed++;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void arch_kprobe_flush_task(struct task_struct *tk)
|
|
|
|
+{
|
|
|
|
+ struct kretprobe_instance *ri;
|
|
|
|
+ while ((ri = get_rp_inst_tsk(tk)) != NULL) {
|
|
|
|
+ *((unsigned long *)(ri->stack_addr)) =
|
|
|
|
+ (unsigned long) ri->ret_addr;
|
|
|
|
+ recycle_rp_inst(ri);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Interrupts are disabled on entry as trap3 is an interrupt gate and they
|
|
* Interrupts are disabled on entry as trap3 is an interrupt gate and they
|
|
* remain disabled thorough out this function.
|
|
* remain disabled thorough out this function.
|
|
@@ -316,6 +362,55 @@ no_kprobe:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * For function-return probes, init_kprobes() establishes a probepoint
|
|
|
|
+ * here. When a retprobed function returns, this probe is hit and
|
|
|
|
+ * trampoline_probe_handler() runs, calling the kretprobe's handler.
|
|
|
|
+ */
|
|
|
|
+ void kretprobe_trampoline_holder(void)
|
|
|
|
+ {
|
|
|
|
+ asm volatile ( ".global kretprobe_trampoline\n"
|
|
|
|
+ "kretprobe_trampoline: \n"
|
|
|
|
+ "nop\n");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Called when we hit the probe point at kretprobe_trampoline
|
|
|
|
+ */
|
|
|
|
+int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
|
|
|
|
+{
|
|
|
|
+ struct task_struct *tsk;
|
|
|
|
+ struct kretprobe_instance *ri;
|
|
|
|
+ struct hlist_head *head;
|
|
|
|
+ struct hlist_node *node;
|
|
|
|
+ unsigned long *sara = (unsigned long *)regs->rsp - 1;
|
|
|
|
+
|
|
|
|
+ tsk = arch_get_kprobe_task(sara);
|
|
|
|
+ head = kretprobe_inst_table_head(tsk);
|
|
|
|
+
|
|
|
|
+ hlist_for_each_entry(ri, node, head, hlist) {
|
|
|
|
+ if (ri->stack_addr == sara && ri->rp) {
|
|
|
|
+ if (ri->rp->handler)
|
|
|
|
+ ri->rp->handler(ri, regs);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void trampoline_post_handler(struct kprobe *p, struct pt_regs *regs,
|
|
|
|
+ unsigned long flags)
|
|
|
|
+{
|
|
|
|
+ struct kretprobe_instance *ri;
|
|
|
|
+ /* RA already popped */
|
|
|
|
+ unsigned long *sara = ((unsigned long *)regs->rsp) - 1;
|
|
|
|
+
|
|
|
|
+ while ((ri = get_rp_inst(sara))) {
|
|
|
|
+ regs->rip = (unsigned long)ri->ret_addr;
|
|
|
|
+ recycle_rp_inst(ri);
|
|
|
|
+ }
|
|
|
|
+ regs->eflags &= ~TF_MASK;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Called after single-stepping. p->addr is the address of the
|
|
* Called after single-stepping. p->addr is the address of the
|
|
* instruction whose first byte has been replaced by the "int 3"
|
|
* instruction whose first byte has been replaced by the "int 3"
|
|
@@ -404,7 +499,8 @@ int post_kprobe_handler(struct pt_regs *regs)
|
|
if (current_kprobe->post_handler)
|
|
if (current_kprobe->post_handler)
|
|
current_kprobe->post_handler(current_kprobe, regs, 0);
|
|
current_kprobe->post_handler(current_kprobe, regs, 0);
|
|
|
|
|
|
- resume_execution(current_kprobe, regs);
|
|
|
|
|
|
+ if (current_kprobe->post_handler != trampoline_post_handler)
|
|
|
|
+ resume_execution(current_kprobe, regs);
|
|
regs->eflags |= kprobe_saved_rflags;
|
|
regs->eflags |= kprobe_saved_rflags;
|
|
|
|
|
|
unlock_kprobes();
|
|
unlock_kprobes();
|