Skip to content

Commit ea88dc9

Browse files
committed
Merge branch 'for-next/ftrace' into for-next/core
* for-next/ftrace: arm64: ftrace: Simplify get_ftrace_plt arm64: ftrace: Add direct call support ftrace: selftest: remove broken trace_direct_tramp ftrace: Make DIRECT_CALLS work WITH_ARGS and !WITH_REGS ftrace: Store direct called addresses in their ops ftrace: Rename _ftrace_direct_multi APIs to _ftrace_direct APIs ftrace: Remove the legacy _ftrace_direct API ftrace: Replace uses of _ftrace_direct APIs with _ftrace_direct_multi ftrace: Let unregister_ftrace_direct_multi() call ftrace_free_filter()
2 parents 31eb87c + 0f59dca commit ea88dc9

File tree

19 files changed

+244
-516
lines changed

19 files changed

+244
-516
lines changed

arch/arm64/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ config ARM64
185185
select HAVE_DEBUG_KMEMLEAK
186186
select HAVE_DMA_CONTIGUOUS
187187
select HAVE_DYNAMIC_FTRACE
188+
select HAVE_DYNAMIC_FTRACE_WITH_ARGS \
189+
if $(cc-option,-fpatchable-function-entry=2)
190+
select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS \
191+
if DYNAMIC_FTRACE_WITH_ARGS && DYNAMIC_FTRACE_WITH_CALL_OPS
188192
select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS \
189193
if (DYNAMIC_FTRACE_WITH_ARGS && !CFI_CLANG && \
190194
!CC_OPTIMIZE_FOR_SIZE)

arch/arm64/include/asm/ftrace.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,19 @@ struct ftrace_ops;
7070

7171
#define arch_ftrace_get_regs(regs) NULL
7272

73+
/*
74+
* Note: sizeof(struct ftrace_regs) must be a multiple of 16 to ensure correct
75+
* stack alignment
76+
*/
7377
struct ftrace_regs {
7478
/* x0 - x8 */
7579
unsigned long regs[9];
80+
81+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
82+
unsigned long direct_tramp;
83+
#else
7684
unsigned long __unused;
85+
#endif
7786

7887
unsigned long fp;
7988
unsigned long lr;
@@ -136,6 +145,19 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
136145
void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
137146
struct ftrace_ops *op, struct ftrace_regs *fregs);
138147
#define ftrace_graph_func ftrace_graph_func
148+
149+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
150+
static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs,
151+
unsigned long addr)
152+
{
153+
/*
154+
* The ftrace trampoline will return to this address instead of the
155+
* instrumented function.
156+
*/
157+
fregs->direct_tramp = addr;
158+
}
159+
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
160+
139161
#endif
140162

141163
#define ftrace_return_address(n) return_address(n)

arch/arm64/kernel/asm-offsets.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ int main(void)
9393
DEFINE(FREGS_LR, offsetof(struct ftrace_regs, lr));
9494
DEFINE(FREGS_SP, offsetof(struct ftrace_regs, sp));
9595
DEFINE(FREGS_PC, offsetof(struct ftrace_regs, pc));
96+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
97+
DEFINE(FREGS_DIRECT_TRAMP, offsetof(struct ftrace_regs, direct_tramp));
98+
#endif
9699
DEFINE(FREGS_SIZE, sizeof(struct ftrace_regs));
97100
BLANK();
98101
#endif
@@ -197,6 +200,9 @@ int main(void)
197200
#endif
198201
#ifdef CONFIG_FUNCTION_TRACER
199202
DEFINE(FTRACE_OPS_FUNC, offsetof(struct ftrace_ops, func));
203+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
204+
DEFINE(FTRACE_OPS_DIRECT_CALL, offsetof(struct ftrace_ops, direct_call));
205+
#endif
200206
#endif
201207
return 0;
202208
}

arch/arm64/kernel/entry-ftrace.S

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,31 @@
3636
SYM_CODE_START(ftrace_caller)
3737
bti c
3838

39+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
40+
/*
41+
* The literal pointer to the ops is at an 8-byte aligned boundary
42+
* which is either 12 or 16 bytes before the BL instruction in the call
43+
* site. See ftrace_call_adjust() for details.
44+
*
45+
* Therefore here the LR points at `literal + 16` or `literal + 20`,
46+
* and we can find the address of the literal in either case by
47+
* aligning to an 8-byte boundary and subtracting 16. We do the
48+
* alignment first as this allows us to fold the subtraction into the
49+
* LDR.
50+
*/
51+
bic x11, x30, 0x7
52+
ldr x11, [x11, #-(4 * AARCH64_INSN_SIZE)] // op
53+
54+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
55+
/*
56+
* If the op has a direct call, handle it immediately without
57+
* saving/restoring registers.
58+
*/
59+
ldr x17, [x11, #FTRACE_OPS_DIRECT_CALL] // op->direct_call
60+
cbnz x17, ftrace_caller_direct
61+
#endif
62+
#endif
63+
3964
/* Save original SP */
4065
mov x10, sp
4166

@@ -49,6 +74,10 @@ SYM_CODE_START(ftrace_caller)
4974
stp x6, x7, [sp, #FREGS_X6]
5075
str x8, [sp, #FREGS_X8]
5176

77+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
78+
str xzr, [sp, #FREGS_DIRECT_TRAMP]
79+
#endif
80+
5281
/* Save the callsite's FP, LR, SP */
5382
str x29, [sp, #FREGS_FP]
5483
str x9, [sp, #FREGS_LR]
@@ -71,20 +100,7 @@ SYM_CODE_START(ftrace_caller)
71100
mov x3, sp // regs
72101

73102
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS
74-
/*
75-
* The literal pointer to the ops is at an 8-byte aligned boundary
76-
* which is either 12 or 16 bytes before the BL instruction in the call
77-
* site. See ftrace_call_adjust() for details.
78-
*
79-
* Therefore here the LR points at `literal + 16` or `literal + 20`,
80-
* and we can find the address of the literal in either case by
81-
* aligning to an 8-byte boundary and subtracting 16. We do the
82-
* alignment first as this allows us to fold the subtraction into the
83-
* LDR.
84-
*/
85-
bic x2, x30, 0x7
86-
ldr x2, [x2, #-16] // op
87-
103+
mov x2, x11 // op
88104
ldr x4, [x2, #FTRACE_OPS_FUNC] // op->func
89105
blr x4 // op->func(ip, parent_ip, op, regs)
90106

@@ -107,17 +123,61 @@ SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
107123
ldp x6, x7, [sp, #FREGS_X6]
108124
ldr x8, [sp, #FREGS_X8]
109125

110-
/* Restore the callsite's FP, LR, PC */
126+
/* Restore the callsite's FP */
111127
ldr x29, [sp, #FREGS_FP]
128+
129+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
130+
ldr x17, [sp, #FREGS_DIRECT_TRAMP]
131+
cbnz x17, ftrace_caller_direct_late
132+
#endif
133+
134+
/* Restore the callsite's LR and PC */
112135
ldr x30, [sp, #FREGS_LR]
113136
ldr x9, [sp, #FREGS_PC]
114137

115138
/* Restore the callsite's SP */
116139
add sp, sp, #FREGS_SIZE + 32
117140

118141
ret x9
142+
143+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
144+
SYM_INNER_LABEL(ftrace_caller_direct_late, SYM_L_LOCAL)
145+
/*
146+
* Head to a direct trampoline in x17 after having run other tracers.
147+
* The ftrace_regs are live, and x0-x8 and FP have been restored. The
148+
* LR, PC, and SP have not been restored.
149+
*/
150+
151+
/*
152+
* Restore the callsite's LR and PC matching the trampoline calling
153+
* convention.
154+
*/
155+
ldr x9, [sp, #FREGS_LR]
156+
ldr x30, [sp, #FREGS_PC]
157+
158+
/* Restore the callsite's SP */
159+
add sp, sp, #FREGS_SIZE + 32
160+
161+
SYM_INNER_LABEL(ftrace_caller_direct, SYM_L_LOCAL)
162+
/*
163+
* Head to a direct trampoline in x17.
164+
*
165+
* We use `BR X17` as this can safely land on a `BTI C` or `PACIASP` in
166+
* the trampoline, and will not unbalance any return stack.
167+
*/
168+
br x17
169+
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
119170
SYM_CODE_END(ftrace_caller)
120171

172+
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
173+
SYM_CODE_START(ftrace_stub_direct_tramp)
174+
bti c
175+
mov x10, x30
176+
mov x30, x9
177+
ret x10
178+
SYM_CODE_END(ftrace_stub_direct_tramp)
179+
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
180+
121181
#else /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */
122182

123183
/*

arch/arm64/kernel/ftrace.c

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,22 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
195195
return ftrace_modify_code(pc, 0, new, false);
196196
}
197197

198-
static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
198+
static struct plt_entry *get_ftrace_plt(struct module *mod)
199199
{
200200
#ifdef CONFIG_ARM64_MODULE_PLTS
201201
struct plt_entry *plt = mod->arch.ftrace_trampolines;
202202

203-
if (addr == FTRACE_ADDR)
204-
return &plt[FTRACE_PLT_IDX];
205-
#endif
203+
return &plt[FTRACE_PLT_IDX];
204+
#else
206205
return NULL;
206+
#endif
207+
}
208+
209+
static bool reachable_by_bl(unsigned long addr, unsigned long pc)
210+
{
211+
long offset = (long)addr - (long)pc;
212+
213+
return offset >= -SZ_128M && offset < SZ_128M;
207214
}
208215

209216
/*
@@ -220,14 +227,21 @@ static bool ftrace_find_callable_addr(struct dyn_ftrace *rec,
220227
unsigned long *addr)
221228
{
222229
unsigned long pc = rec->ip;
223-
long offset = (long)*addr - (long)pc;
224230
struct plt_entry *plt;
225231

232+
/*
233+
* If a custom trampoline is unreachable, rely on the ftrace_caller
234+
* trampoline which knows how to indirectly reach that trampoline
235+
* through ops->direct_call.
236+
*/
237+
if (*addr != FTRACE_ADDR && !reachable_by_bl(*addr, pc))
238+
*addr = FTRACE_ADDR;
239+
226240
/*
227241
* When the target is within range of the 'BL' instruction, use 'addr'
228242
* as-is and branch to that directly.
229243
*/
230-
if (offset >= -SZ_128M && offset < SZ_128M)
244+
if (reachable_by_bl(*addr, pc))
231245
return true;
232246

233247
/*
@@ -256,7 +270,7 @@ static bool ftrace_find_callable_addr(struct dyn_ftrace *rec,
256270
if (WARN_ON(!mod))
257271
return false;
258272

259-
plt = get_ftrace_plt(mod, *addr);
273+
plt = get_ftrace_plt(mod);
260274
if (!plt) {
261275
pr_err("ftrace: no module PLT for %ps\n", (void *)*addr);
262276
return false;
@@ -330,12 +344,24 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
330344
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
331345
unsigned long addr)
332346
{
333-
if (WARN_ON_ONCE(old_addr != (unsigned long)ftrace_caller))
347+
unsigned long pc = rec->ip;
348+
u32 old, new;
349+
int ret;
350+
351+
ret = ftrace_rec_set_ops(rec, arm64_rec_get_ops(rec));
352+
if (ret)
353+
return ret;
354+
355+
if (!ftrace_find_callable_addr(rec, NULL, &old_addr))
334356
return -EINVAL;
335-
if (WARN_ON_ONCE(addr != (unsigned long)ftrace_caller))
357+
if (!ftrace_find_callable_addr(rec, NULL, &addr))
336358
return -EINVAL;
337359

338-
return ftrace_rec_update_ops(rec);
360+
old = aarch64_insn_gen_branch_imm(pc, old_addr,
361+
AARCH64_INSN_BRANCH_LINK);
362+
new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);
363+
364+
return ftrace_modify_code(pc, old, new, true);
339365
}
340366
#endif
341367

arch/s390/kernel/mcount.S

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ ENTRY(ftrace_stub)
3232
BR_EX %r14
3333
ENDPROC(ftrace_stub)
3434

35+
SYM_CODE_START(ftrace_stub_direct_tramp)
36+
lgr %r1, %r0
37+
BR_EX %r1
38+
SYM_CODE_END(ftrace_stub_direct_tramp)
39+
3540
.macro ftrace_regs_entry, allregs=0
3641
stg %r14,(__SF_GPRS+8*8)(%r15) # save traced function caller
3742

arch/x86/kernel/ftrace_32.S

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
163163
jmp .Lftrace_ret
164164
SYM_CODE_END(ftrace_regs_caller)
165165

166+
SYM_FUNC_START(ftrace_stub_direct_tramp)
167+
CALL_DEPTH_ACCOUNT
168+
RET
169+
SYM_FUNC_END(ftrace_stub_direct_tramp)
170+
166171
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
167172
SYM_CODE_START(ftrace_graph_caller)
168173
pushl %eax

arch/x86/kernel/ftrace_64.S

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
309309
SYM_FUNC_END(ftrace_regs_caller)
310310
STACK_FRAME_NON_STANDARD_FP(ftrace_regs_caller)
311311

312+
SYM_FUNC_START(ftrace_stub_direct_tramp)
313+
CALL_DEPTH_ACCOUNT
314+
RET
315+
SYM_FUNC_END(ftrace_stub_direct_tramp)
312316

313317
#else /* ! CONFIG_DYNAMIC_FTRACE */
314318

0 commit comments

Comments
 (0)