Skip to content

Commit 515a491

Browse files
mhiramatAlexei Starovoitov
authored andcommitted
ARM: rethook: Add rethook arm implementation
Add rethook arm implementation. Most of the code has been copied from kretprobes on arm. Since the arm's ftrace implementation is a bit special, this needs a special care using from fprobe. Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (Google) <[email protected]> Tested-by: Steven Rostedt (Google) <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/164735289643.1084943.15184590256680485720.stgit@devnote2
1 parent 02752bd commit 515a491

File tree

5 files changed

+113
-2
lines changed

5 files changed

+113
-2
lines changed

arch/arm/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ config ARM
107107
select HAVE_MOD_ARCH_SPECIFIC
108108
select HAVE_NMI
109109
select HAVE_OPTPROBES if !THUMB2_KERNEL
110+
select HAVE_RETHOOK
110111
select HAVE_PERF_EVENTS
111112
select HAVE_PERF_REGS
112113
select HAVE_PERF_USER_STACK_DUMP

arch/arm/include/asm/stacktrace.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct stackframe {
1414
unsigned long sp;
1515
unsigned long lr;
1616
unsigned long pc;
17-
#ifdef CONFIG_KRETPROBES
17+
#if defined(CONFIG_KRETPROBES) || defined(CONFIG_RETHOOK)
1818
struct llist_node *kr_cur;
1919
struct task_struct *tsk;
2020
#endif
@@ -27,7 +27,7 @@ void arm_get_current_stackframe(struct pt_regs *regs, struct stackframe *frame)
2727
frame->sp = regs->ARM_sp;
2828
frame->lr = regs->ARM_lr;
2929
frame->pc = regs->ARM_pc;
30-
#ifdef CONFIG_KRETPROBES
30+
#if defined(CONFIG_KRETPROBES) || defined(CONFIG_RETHOOK)
3131
frame->kr_cur = NULL;
3232
frame->tsk = current;
3333
#endif

arch/arm/kernel/stacktrace.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22
#include <linux/export.h>
33
#include <linux/kprobes.h>
4+
#include <linux/rethook.h>
45
#include <linux/sched.h>
56
#include <linux/sched/debug.h>
67
#include <linux/stacktrace.h>
@@ -66,6 +67,11 @@ int notrace unwind_frame(struct stackframe *frame)
6667
frame->sp = *(unsigned long *)(fp - 8);
6768
frame->pc = *(unsigned long *)(fp - 4);
6869
#endif
70+
#ifdef CONFIG_RETHOOK
71+
if (is_rethook_trampoline(frame->pc))
72+
frame->pc = rethook_find_ret_addr(frame->tsk, frame->fp,
73+
&frame->kr_cur);
74+
#endif
6975
#ifdef CONFIG_KRETPROBES
7076
if (is_kretprobe_trampoline(frame->pc))
7177
frame->pc = kretprobe_find_ret_addr(frame->tsk,

arch/arm/probes/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ obj-$(CONFIG_KPROBES) += decode-thumb.o
66
else
77
obj-$(CONFIG_KPROBES) += decode-arm.o
88
endif
9+
obj-$(CONFIG_RETHOOK) += rethook.o

arch/arm/probes/rethook.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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 = &regs->ARM_lr;
94+
frame = &regs->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

Comments
 (0)