|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
| 2 | +/* |
| 3 | + * arm implementation of rethook. Mostly copied from arch/arm/probes/kprobes/core.c |
| 4 | + */ |
| 5 | + |
| 6 | +#include <linux/kprobes.h> |
| 7 | +#include <linux/rethook.h> |
| 8 | + |
| 9 | +/* Called from arch_rethook_trampoline */ |
| 10 | +static __used unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs) |
| 11 | +{ |
| 12 | + return rethook_trampoline_handler(regs, regs->ARM_fp); |
| 13 | +} |
| 14 | +NOKPROBE_SYMBOL(arch_rethook_trampoline_callback); |
| 15 | + |
| 16 | +/* |
| 17 | + * When a rethook'ed function returns, it returns to arch_rethook_trampoline |
| 18 | + * which calls rethook callback. We construct a struct pt_regs to |
| 19 | + * give a view of registers r0-r11, sp, lr, and pc to the user |
| 20 | + * return-handler. This is not a complete pt_regs structure, but that |
| 21 | + * should be enough for stacktrace from the return handler with or |
| 22 | + * without pt_regs. |
| 23 | + */ |
| 24 | +asm( |
| 25 | + ".text\n" |
| 26 | + ".global arch_rethook_trampoline\n" |
| 27 | + ".type arch_rethook_trampoline, %function\n" |
| 28 | + "arch_rethook_trampoline:\n" |
| 29 | +#ifdef CONFIG_FRAME_POINTER |
| 30 | + "ldr lr, =arch_rethook_trampoline \n\t" |
| 31 | + /* this makes a framepointer on pt_regs. */ |
| 32 | +#ifdef CONFIG_CC_IS_CLANG |
| 33 | + "stmdb sp, {sp, lr, pc} \n\t" |
| 34 | + "sub sp, sp, #12 \n\t" |
| 35 | + /* In clang case, pt_regs->ip = lr. */ |
| 36 | + "stmdb sp!, {r0 - r11, lr} \n\t" |
| 37 | + /* fp points regs->r11 (fp) */ |
| 38 | + "add fp, sp, #44 \n\t" |
| 39 | +#else /* !CONFIG_CC_IS_CLANG */ |
| 40 | + /* In gcc case, pt_regs->ip = fp. */ |
| 41 | + "stmdb sp, {fp, sp, lr, pc} \n\t" |
| 42 | + "sub sp, sp, #16 \n\t" |
| 43 | + "stmdb sp!, {r0 - r11} \n\t" |
| 44 | + /* fp points regs->r15 (pc) */ |
| 45 | + "add fp, sp, #60 \n\t" |
| 46 | +#endif /* CONFIG_CC_IS_CLANG */ |
| 47 | +#else /* !CONFIG_FRAME_POINTER */ |
| 48 | + "sub sp, sp, #16 \n\t" |
| 49 | + "stmdb sp!, {r0 - r11} \n\t" |
| 50 | +#endif /* CONFIG_FRAME_POINTER */ |
| 51 | + "mov r0, sp \n\t" |
| 52 | + "bl arch_rethook_trampoline_callback \n\t" |
| 53 | + "mov lr, r0 \n\t" |
| 54 | + "ldmia sp!, {r0 - r11} \n\t" |
| 55 | + "add sp, sp, #16 \n\t" |
| 56 | +#ifdef CONFIG_THUMB2_KERNEL |
| 57 | + "bx lr \n\t" |
| 58 | +#else |
| 59 | + "mov pc, lr \n\t" |
| 60 | +#endif |
| 61 | + ".size arch_rethook_trampoline, .-arch_rethook_trampoline\n" |
| 62 | +); |
| 63 | +NOKPROBE_SYMBOL(arch_rethook_trampoline); |
| 64 | + |
| 65 | +/* |
| 66 | + * At the entry of function with mcount. The stack and registers are prepared |
| 67 | + * for the mcount function as below. |
| 68 | + * |
| 69 | + * mov ip, sp |
| 70 | + * push {fp, ip, lr, pc} |
| 71 | + * sub fp, ip, #4 ; FP[0] = PC, FP[-4] = LR, and FP[-12] = call-site FP. |
| 72 | + * push {lr} |
| 73 | + * bl <__gnu_mcount_nc> ; call ftrace |
| 74 | + * |
| 75 | + * And when returning from the function, call-site FP, SP and PC are restored |
| 76 | + * from stack as below; |
| 77 | + * |
| 78 | + * ldm sp, {fp, sp, pc} |
| 79 | + * |
| 80 | + * Thus, if the arch_rethook_prepare() is called from real function entry, |
| 81 | + * it must change the LR and save FP in pt_regs. But if it is called via |
| 82 | + * mcount context (ftrace), it must change the LR on stack, which is next |
| 83 | + * to the PC (= FP[-4]), and save the FP value at FP[-12]. |
| 84 | + */ |
| 85 | +void arch_rethook_prepare(struct rethook_node *rh, struct pt_regs *regs, bool mcount) |
| 86 | +{ |
| 87 | + unsigned long *ret_addr, *frame; |
| 88 | + |
| 89 | + if (mcount) { |
| 90 | + ret_addr = (unsigned long *)(regs->ARM_fp - 4); |
| 91 | + frame = (unsigned long *)(regs->ARM_fp - 12); |
| 92 | + } else { |
| 93 | + ret_addr = ®s->ARM_lr; |
| 94 | + frame = ®s->ARM_fp; |
| 95 | + } |
| 96 | + |
| 97 | + rh->ret_addr = *ret_addr; |
| 98 | + rh->frame = *frame; |
| 99 | + |
| 100 | + /* Replace the return addr with trampoline addr. */ |
| 101 | + *ret_addr = (unsigned long)arch_rethook_trampoline; |
| 102 | +} |
| 103 | +NOKPROBE_SYMBOL(arch_rethook_prepare); |
0 commit comments