diff options
author | Viktor Rosendahl <viktor.rosendahl@nokia.com> | 2011-03-28 11:56:05 -0400 |
---|---|---|
committer | Nicolas Pitre <nicolas.pitre@linaro.org> | 2011-04-28 23:40:54 -0400 |
commit | cf3cc1aa9b6d0bf1750143af65829f4368d77492 (patch) | |
tree | cc9027ad2b4a7c7d77afa28049ebf7f97abb14d7 /arch/arm | |
parent | 6221f222c0ebf1acdf7abcf927178f40e1a65e2a (diff) |
kprobes/arm: Fix ldrd/strd emulation
Currently emulate_ldrd and emulate_strd don't even have the adjustment
of the PC value, so in case of Rn == PC, it will not update the PC
incorrectly but instead load/store from the wrong address. Let's add
both the adjustment of the PC value and the check for PC == PC.
Signed-off-by: Viktor Rosendahl <viktor.rosendahl@nokia.com>
Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/kernel/kprobes-decode.c | 20 |
1 files changed, 15 insertions, 5 deletions
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c index 23891317dc4b..3b0cf90cb449 100644 --- a/arch/arm/kernel/kprobes-decode.c +++ b/arch/arm/kernel/kprobes-decode.c | |||
@@ -540,9 +540,12 @@ static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs) | |||
540 | { | 540 | { |
541 | insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; | 541 | insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0]; |
542 | kprobe_opcode_t insn = p->opcode; | 542 | kprobe_opcode_t insn = p->opcode; |
543 | long ppc = (long)p->addr + 8; | ||
543 | int rd = (insn >> 12) & 0xf; | 544 | int rd = (insn >> 12) & 0xf; |
544 | int rn = (insn >> 16) & 0xf; | 545 | int rn = (insn >> 16) & 0xf; |
545 | int rm = insn & 0xf; /* rm may be invalid, don't care. */ | 546 | int rm = insn & 0xf; /* rm may be invalid, don't care. */ |
547 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | ||
548 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; | ||
546 | 549 | ||
547 | /* Not following the C calling convention here, so need asm(). */ | 550 | /* Not following the C calling convention here, so need asm(). */ |
548 | __asm__ __volatile__ ( | 551 | __asm__ __volatile__ ( |
@@ -554,29 +557,36 @@ static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs) | |||
554 | "str r0, %[rn] \n\t" /* in case of writeback */ | 557 | "str r0, %[rn] \n\t" /* in case of writeback */ |
555 | "str r2, %[rd0] \n\t" | 558 | "str r2, %[rd0] \n\t" |
556 | "str r3, %[rd1] \n\t" | 559 | "str r3, %[rd1] \n\t" |
557 | : [rn] "+m" (regs->uregs[rn]), | 560 | : [rn] "+m" (rnv), |
558 | [rd0] "=m" (regs->uregs[rd]), | 561 | [rd0] "=m" (regs->uregs[rd]), |
559 | [rd1] "=m" (regs->uregs[rd+1]) | 562 | [rd1] "=m" (regs->uregs[rd+1]) |
560 | : [rm] "m" (regs->uregs[rm]), | 563 | : [rm] "m" (rmv), |
561 | [cpsr] "r" (regs->ARM_cpsr), | 564 | [cpsr] "r" (regs->ARM_cpsr), |
562 | [i_fn] "r" (i_fn) | 565 | [i_fn] "r" (i_fn) |
563 | : "r0", "r1", "r2", "r3", "lr", "cc" | 566 | : "r0", "r1", "r2", "r3", "lr", "cc" |
564 | ); | 567 | ); |
568 | if (rn != 15) | ||
569 | regs->uregs[rn] = rnv; /* Save Rn in case of writeback. */ | ||
565 | } | 570 | } |
566 | 571 | ||
567 | static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs) | 572 | static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs) |
568 | { | 573 | { |
569 | insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0]; | 574 | insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0]; |
570 | kprobe_opcode_t insn = p->opcode; | 575 | kprobe_opcode_t insn = p->opcode; |
576 | long ppc = (long)p->addr + 8; | ||
571 | int rd = (insn >> 12) & 0xf; | 577 | int rd = (insn >> 12) & 0xf; |
572 | int rn = (insn >> 16) & 0xf; | 578 | int rn = (insn >> 16) & 0xf; |
573 | int rm = insn & 0xf; | 579 | int rm = insn & 0xf; |
574 | long rnv = regs->uregs[rn]; | 580 | long rnv = (rn == 15) ? ppc : regs->uregs[rn]; |
575 | long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ | 581 | /* rm/rmv may be invalid, don't care. */ |
582 | long rmv = (rm == 15) ? ppc : regs->uregs[rm]; | ||
583 | long rnv_wb; | ||
576 | 584 | ||
577 | regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd], | 585 | rnv_wb = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd], |
578 | regs->uregs[rd+1], | 586 | regs->uregs[rd+1], |
579 | regs->ARM_cpsr, i_fn); | 587 | regs->ARM_cpsr, i_fn); |
588 | if (rn != 15) | ||
589 | regs->uregs[rn] = rnv_wb; /* Save Rn in case of writeback. */ | ||
580 | } | 590 | } |
581 | 591 | ||
582 | static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs) | 592 | static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs) |