diff options
author | Masami Hiramatsu <hiramatu@sdl.hitachi.co.jp> | 2006-03-26 04:38:19 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-26 11:57:04 -0500 |
commit | c9becf58d935265919bf1cb348b2c04492c8949d (patch) | |
tree | 57ec897e82bdd4c30eb19112be425fe4d64a1029 /arch/i386 | |
parent | 311ac88fd2d4194a95e9e38d2fe08917be98723c (diff) |
[PATCH] kretprobe: kretprobe-booster
In normal operation, kretprobe makes a target function return to trampoline
code. A kprobe (called trampoline_probe) has been inserted in the trampoline
code. When the kernel hits this kprobe, it calls kretprobe's handler and it
returns to the original return address.
Kretprobe-booster removes the trampoline_probe. It allows the trampoline code
to call kretprobe's handler directly instead of invoking kprobe. The
trampoline code returns to the original return address.
(changelog from Chuck Ebbert <76306.1226@compuserve.com> - thanks ;))
Signed-off-by: Masami Hiramatsu <hiramatu@sdl.hitachi.co.jp>
Cc: Prasanna S Panchamukhi <prasanna@in.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Chuck Ebbert <76306.1226@compuserve.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/kernel/kprobes.c | 61 |
1 files changed, 39 insertions, 22 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index 137bf612141b..acdcc640a72a 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -333,17 +333,44 @@ no_kprobe: | |||
333 | * here. When a retprobed function returns, this probe is hit and | 333 | * here. When a retprobed function returns, this probe is hit and |
334 | * trampoline_probe_handler() runs, calling the kretprobe's handler. | 334 | * trampoline_probe_handler() runs, calling the kretprobe's handler. |
335 | */ | 335 | */ |
336 | void kretprobe_trampoline_holder(void) | 336 | void __kprobes kretprobe_trampoline_holder(void) |
337 | { | 337 | { |
338 | asm volatile ( ".global kretprobe_trampoline\n" | 338 | asm volatile ( ".global kretprobe_trampoline\n" |
339 | "kretprobe_trampoline: \n" | 339 | "kretprobe_trampoline: \n" |
340 | "nop\n"); | 340 | " pushf\n" |
341 | } | 341 | /* skip cs, eip, orig_eax, es, ds */ |
342 | " subl $20, %esp\n" | ||
343 | " pushl %eax\n" | ||
344 | " pushl %ebp\n" | ||
345 | " pushl %edi\n" | ||
346 | " pushl %esi\n" | ||
347 | " pushl %edx\n" | ||
348 | " pushl %ecx\n" | ||
349 | " pushl %ebx\n" | ||
350 | " movl %esp, %eax\n" | ||
351 | " call trampoline_handler\n" | ||
352 | /* move eflags to cs */ | ||
353 | " movl 48(%esp), %edx\n" | ||
354 | " movl %edx, 44(%esp)\n" | ||
355 | /* save true return address on eflags */ | ||
356 | " movl %eax, 48(%esp)\n" | ||
357 | " popl %ebx\n" | ||
358 | " popl %ecx\n" | ||
359 | " popl %edx\n" | ||
360 | " popl %esi\n" | ||
361 | " popl %edi\n" | ||
362 | " popl %ebp\n" | ||
363 | " popl %eax\n" | ||
364 | /* skip eip, orig_eax, es, ds */ | ||
365 | " addl $16, %esp\n" | ||
366 | " popf\n" | ||
367 | " ret\n"); | ||
368 | } | ||
342 | 369 | ||
343 | /* | 370 | /* |
344 | * Called when we hit the probe point at kretprobe_trampoline | 371 | * Called from kretprobe_trampoline |
345 | */ | 372 | */ |
346 | int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 373 | fastcall void *__kprobes trampoline_handler(struct pt_regs *regs) |
347 | { | 374 | { |
348 | struct kretprobe_instance *ri = NULL; | 375 | struct kretprobe_instance *ri = NULL; |
349 | struct hlist_head *head; | 376 | struct hlist_head *head; |
@@ -372,8 +399,11 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | |||
372 | /* another task is sharing our hash bucket */ | 399 | /* another task is sharing our hash bucket */ |
373 | continue; | 400 | continue; |
374 | 401 | ||
375 | if (ri->rp && ri->rp->handler) | 402 | if (ri->rp && ri->rp->handler){ |
403 | __get_cpu_var(current_kprobe) = &ri->rp->kp; | ||
376 | ri->rp->handler(ri, regs); | 404 | ri->rp->handler(ri, regs); |
405 | __get_cpu_var(current_kprobe) = NULL; | ||
406 | } | ||
377 | 407 | ||
378 | orig_ret_address = (unsigned long)ri->ret_addr; | 408 | orig_ret_address = (unsigned long)ri->ret_addr; |
379 | recycle_rp_inst(ri); | 409 | recycle_rp_inst(ri); |
@@ -388,18 +418,10 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | |||
388 | } | 418 | } |
389 | 419 | ||
390 | BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); | 420 | BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address)); |
391 | regs->eip = orig_ret_address; | ||
392 | 421 | ||
393 | reset_current_kprobe(); | ||
394 | spin_unlock_irqrestore(&kretprobe_lock, flags); | 422 | spin_unlock_irqrestore(&kretprobe_lock, flags); |
395 | preempt_enable_no_resched(); | ||
396 | 423 | ||
397 | /* | 424 | return (void*)orig_ret_address; |
398 | * By returning a non-zero value, we are telling | ||
399 | * kprobe_handler() that we don't want the post_handler | ||
400 | * to run (and have re-enabled preemption) | ||
401 | */ | ||
402 | return 1; | ||
403 | } | 425 | } |
404 | 426 | ||
405 | /* | 427 | /* |
@@ -646,12 +668,7 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | |||
646 | return 0; | 668 | return 0; |
647 | } | 669 | } |
648 | 670 | ||
649 | static struct kprobe trampoline_p = { | ||
650 | .addr = (kprobe_opcode_t *) &kretprobe_trampoline, | ||
651 | .pre_handler = trampoline_probe_handler | ||
652 | }; | ||
653 | |||
654 | int __init arch_init_kprobes(void) | 671 | int __init arch_init_kprobes(void) |
655 | { | 672 | { |
656 | return register_kprobe(&trampoline_p); | 673 | return 0; |
657 | } | 674 | } |