diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2008-01-30 07:31:21 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:31:21 -0500 |
commit | da07ab0375897bb9e108b28129df140ecd3ee94e (patch) | |
tree | 2d5f0fe4709cd4e6b6dc9f686514ff5f2ded241c /arch/x86/kernel/kprobes_64.c | |
parent | aa470140e86e45723cf8387292edbce9106ddc1f (diff) |
x86: return probe-booster for x86-64
This patch adds kretprobe-booster to kprobes_64.c.
- Changes are based on x86-32.
- Rewrite register saving/restoring code
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel/kprobes_64.c')
-rw-r--r-- | arch/x86/kernel/kprobes_64.c | 92 |
1 files changed, 65 insertions, 27 deletions
diff --git a/arch/x86/kernel/kprobes_64.c b/arch/x86/kernel/kprobes_64.c index bf0e18473677..bc93b1dd9a01 100644 --- a/arch/x86/kernel/kprobes_64.c +++ b/arch/x86/kernel/kprobes_64.c | |||
@@ -28,6 +28,8 @@ | |||
28 | * Fixed to handle %rip-relative addressing mode correctly. | 28 | * Fixed to handle %rip-relative addressing mode correctly. |
29 | * 2005-May Rusty Lynch <rusty.lynch@intel.com> | 29 | * 2005-May Rusty Lynch <rusty.lynch@intel.com> |
30 | * Added function return probes functionality | 30 | * Added function return probes functionality |
31 | * 2007-Dec Masami Hiramatsu <mhiramat@redhat.com> added kprobe-booster | ||
32 | * and kretprobe-booster for x86-64 | ||
31 | */ | 33 | */ |
32 | 34 | ||
33 | #include <linux/kprobes.h> | 35 | #include <linux/kprobes.h> |
@@ -507,21 +509,65 @@ no_kprobe: | |||
507 | } | 509 | } |
508 | 510 | ||
509 | /* | 511 | /* |
510 | * For function-return probes, init_kprobes() establishes a probepoint | 512 | * When a retprobed function returns, this code saves registers and |
511 | * here. When a retprobed function returns, this probe is hit and | 513 | * calls trampoline_handler() runs, which calls the kretprobe's handler. |
512 | * trampoline_probe_handler() runs, calling the kretprobe's handler. | ||
513 | */ | 514 | */ |
514 | void kretprobe_trampoline_holder(void) | 515 | void __kprobes kretprobe_trampoline_holder(void) |
515 | { | 516 | { |
516 | asm volatile ( ".global kretprobe_trampoline\n" | 517 | asm volatile ( ".global kretprobe_trampoline\n" |
517 | "kretprobe_trampoline: \n" | 518 | "kretprobe_trampoline: \n" |
518 | "nop\n"); | 519 | /* We don't bother saving the ss register */ |
520 | " pushq %rsp\n" | ||
521 | " pushfq\n" | ||
522 | /* | ||
523 | * Skip cs, ip, orig_ax. | ||
524 | * trampoline_handler() will plug in these values | ||
525 | */ | ||
526 | " subq $24, %rsp\n" | ||
527 | " pushq %rdi\n" | ||
528 | " pushq %rsi\n" | ||
529 | " pushq %rdx\n" | ||
530 | " pushq %rcx\n" | ||
531 | " pushq %rax\n" | ||
532 | " pushq %r8\n" | ||
533 | " pushq %r9\n" | ||
534 | " pushq %r10\n" | ||
535 | " pushq %r11\n" | ||
536 | " pushq %rbx\n" | ||
537 | " pushq %rbp\n" | ||
538 | " pushq %r12\n" | ||
539 | " pushq %r13\n" | ||
540 | " pushq %r14\n" | ||
541 | " pushq %r15\n" | ||
542 | " movq %rsp, %rdi\n" | ||
543 | " call trampoline_handler\n" | ||
544 | /* Replace saved sp with true return address. */ | ||
545 | " movq %rax, 152(%rsp)\n" | ||
546 | " popq %r15\n" | ||
547 | " popq %r14\n" | ||
548 | " popq %r13\n" | ||
549 | " popq %r12\n" | ||
550 | " popq %rbp\n" | ||
551 | " popq %rbx\n" | ||
552 | " popq %r11\n" | ||
553 | " popq %r10\n" | ||
554 | " popq %r9\n" | ||
555 | " popq %r8\n" | ||
556 | " popq %rax\n" | ||
557 | " popq %rcx\n" | ||
558 | " popq %rdx\n" | ||
559 | " popq %rsi\n" | ||
560 | " popq %rdi\n" | ||
561 | /* Skip orig_ax, ip, cs */ | ||
562 | " addq $24, %rsp\n" | ||
563 | " popfq\n" | ||
564 | " ret\n"); | ||
519 | } | 565 | } |
520 | 566 | ||
521 | /* | 567 | /* |
522 | * Called when we hit the probe point at kretprobe_trampoline | 568 | * Called from kretprobe_trampoline |
523 | */ | 569 | */ |
524 | int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | 570 | fastcall void * __kprobes trampoline_handler(struct pt_regs *regs) |
525 | { | 571 | { |
526 | struct kretprobe_instance *ri = NULL; | 572 | struct kretprobe_instance *ri = NULL; |
527 | struct hlist_head *head, empty_rp; | 573 | struct hlist_head *head, empty_rp; |
@@ -532,6 +578,10 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | |||
532 | INIT_HLIST_HEAD(&empty_rp); | 578 | INIT_HLIST_HEAD(&empty_rp); |
533 | spin_lock_irqsave(&kretprobe_lock, flags); | 579 | spin_lock_irqsave(&kretprobe_lock, flags); |
534 | head = kretprobe_inst_table_head(current); | 580 | head = kretprobe_inst_table_head(current); |
581 | /* fixup rt_regs */ | ||
582 | regs->cs = __KERNEL_CS; | ||
583 | regs->ip = trampoline_address; | ||
584 | regs->orig_ax = 0xffffffffffffffff; | ||
535 | 585 | ||
536 | /* | 586 | /* |
537 | * It is possible to have multiple instances associated with a given | 587 | * It is possible to have multiple instances associated with a given |
@@ -551,8 +601,12 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | |||
551 | /* another task is sharing our hash bucket */ | 601 | /* another task is sharing our hash bucket */ |
552 | continue; | 602 | continue; |
553 | 603 | ||
554 | if (ri->rp && ri->rp->handler) | 604 | if (ri->rp && ri->rp->handler) { |
605 | __get_cpu_var(current_kprobe) = &ri->rp->kp; | ||
606 | get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; | ||
555 | ri->rp->handler(ri, regs); | 607 | ri->rp->handler(ri, regs); |
608 | __get_cpu_var(current_kprobe) = NULL; | ||
609 | } | ||
556 | 610 | ||
557 | orig_ret_address = (unsigned long)ri->ret_addr; | 611 | orig_ret_address = (unsigned long)ri->ret_addr; |
558 | recycle_rp_inst(ri, &empty_rp); | 612 | recycle_rp_inst(ri, &empty_rp); |
@@ -567,22 +621,14 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) | |||
567 | } | 621 | } |
568 | 622 | ||
569 | kretprobe_assert(ri, orig_ret_address, trampoline_address); | 623 | kretprobe_assert(ri, orig_ret_address, trampoline_address); |
570 | regs->ip = orig_ret_address; | ||
571 | 624 | ||
572 | reset_current_kprobe(); | ||
573 | spin_unlock_irqrestore(&kretprobe_lock, flags); | 625 | spin_unlock_irqrestore(&kretprobe_lock, flags); |
574 | preempt_enable_no_resched(); | ||
575 | 626 | ||
576 | hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { | 627 | hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { |
577 | hlist_del(&ri->hlist); | 628 | hlist_del(&ri->hlist); |
578 | kfree(ri); | 629 | kfree(ri); |
579 | } | 630 | } |
580 | /* | 631 | return (void *)orig_ret_address; |
581 | * By returning a non-zero value, we are telling | ||
582 | * kprobe_handler() that we don't want the post_handler | ||
583 | * to run (and have re-enabled preemption) | ||
584 | */ | ||
585 | return 1; | ||
586 | } | 632 | } |
587 | 633 | ||
588 | /* | 634 | /* |
@@ -881,20 +927,12 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | |||
881 | return 0; | 927 | return 0; |
882 | } | 928 | } |
883 | 929 | ||
884 | static struct kprobe trampoline_p = { | ||
885 | .addr = (kprobe_opcode_t *) &kretprobe_trampoline, | ||
886 | .pre_handler = trampoline_probe_handler | ||
887 | }; | ||
888 | |||
889 | int __init arch_init_kprobes(void) | 930 | int __init arch_init_kprobes(void) |
890 | { | 931 | { |
891 | return register_kprobe(&trampoline_p); | 932 | return 0; |
892 | } | 933 | } |
893 | 934 | ||
894 | int __kprobes arch_trampoline_kprobe(struct kprobe *p) | 935 | int __kprobes arch_trampoline_kprobe(struct kprobe *p) |
895 | { | 936 | { |
896 | if (p->addr == (kprobe_opcode_t *)&kretprobe_trampoline) | ||
897 | return 1; | ||
898 | |||
899 | return 0; | 937 | return 0; |
900 | } | 938 | } |