Skip to content

Commit

Permalink
ARM: ftrace: avoid redundant loads or clobbering IP
Browse files Browse the repository at this point in the history
[ Upstream commit d119678 ]

Tweak the ftrace return paths to avoid redundant loads of SP, as well as
unnecessary clobbering of IP.

This also fixes the inconsistency of using MOV to perform a function
return, which is sub-optimal on recent micro-architectures but more
importantly, does not perform an interworking return, unlike compiler
generated function returns in Thumb2 builds.

Let's fix this by popping PC from the stack like most ordinary code
does.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
ardbiesheuvel authored and gregkh committed Apr 8, 2022
1 parent 2a4be3f commit 07b8403
Showing 1 changed file with 22 additions and 29 deletions.
51 changes: 22 additions & 29 deletions arch/arm/kernel/entry-ftrace.S
Expand Up @@ -22,10 +22,7 @@
* mcount can be thought of as a function called in the middle of a subroutine
* call. As such, it needs to be transparent for both the caller and the
* callee: the original lr needs to be restored when leaving mcount, and no
* registers should be clobbered. (In the __gnu_mcount_nc implementation, we
* clobber the ip register. This is OK because the ARM calling convention
* allows it to be clobbered in subroutines and doesn't use it to hold
* parameters.)
* registers should be clobbered.
*
* When using dynamic ftrace, we patch out the mcount call by a "pop {lr}"
* instead of the __gnu_mcount_nc call (see arch/arm/kernel/ftrace.c).
Expand Down Expand Up @@ -70,26 +67,25 @@

.macro __ftrace_regs_caller

sub sp, sp, #8 @ space for PC and CPSR OLD_R0,
str lr, [sp, #-8]! @ store LR as PC and make space for CPSR/OLD_R0,
@ OLD_R0 will overwrite previous LR

add ip, sp, #12 @ move in IP the value of SP as it was
@ before the push {lr} of the mcount mechanism
ldr lr, [sp, #8] @ get previous LR

str lr, [sp, #0] @ store LR instead of PC
str r0, [sp, #8] @ write r0 as OLD_R0 over previous LR

ldr lr, [sp, #8] @ get previous LR
str lr, [sp, #-4]! @ store previous LR as LR

str r0, [sp, #8] @ write r0 as OLD_R0 over previous LR
add lr, sp, #16 @ move in LR the value of SP as it was
@ before the push {lr} of the mcount mechanism

stmdb sp!, {ip, lr}
stmdb sp!, {r0-r11, lr}
push {r0-r11, ip, lr}

@ stack content at this point:
@ 0 4 48 52 56 60 64 68 72
@ R0 | R1 | ... | LR | SP + 4 | previous LR | LR | PSR | OLD_R0 |
@ R0 | R1 | ... | IP | SP + 4 | previous LR | LR | PSR | OLD_R0 |

mov r3, sp @ struct pt_regs*
mov r3, sp @ struct pt_regs*

ldr r2, =function_trace_op
ldr r2, [r2] @ pointer to the current
Expand All @@ -112,11 +108,9 @@ ftrace_graph_regs_call:
#endif

@ pop saved regs
ldmia sp!, {r0-r12} @ restore r0 through r12
ldr ip, [sp, #8] @ restore PC
ldr lr, [sp, #4] @ restore LR
ldr sp, [sp, #0] @ restore SP
mov pc, ip @ return
pop {r0-r11, ip, lr} @ restore r0 through r12
ldr lr, [sp], #4 @ restore LR
ldr pc, [sp], #12
.endm

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
Expand All @@ -132,11 +126,9 @@ ftrace_graph_regs_call:
bl prepare_ftrace_return

@ pop registers saved in ftrace_regs_caller
ldmia sp!, {r0-r12} @ restore r0 through r12
ldr ip, [sp, #8] @ restore PC
ldr lr, [sp, #4] @ restore LR
ldr sp, [sp, #0] @ restore SP
mov pc, ip @ return
pop {r0-r11, ip, lr} @ restore r0 through r12
ldr lr, [sp], #4 @ restore LR
ldr pc, [sp], #12

.endm
#endif
Expand Down Expand Up @@ -202,16 +194,17 @@ ftrace_graph_call\suffix:
.endm

.macro mcount_exit
ldmia sp!, {r0-r3, ip, lr}
ret ip
ldmia sp!, {r0-r3}
ldr lr, [sp, #4]
ldr pc, [sp], #8
.endm

ENTRY(__gnu_mcount_nc)
UNWIND(.fnstart)
#ifdef CONFIG_DYNAMIC_FTRACE
mov ip, lr
ldmia sp!, {lr}
ret ip
push {lr}
ldr lr, [sp, #4]
ldr pc, [sp], #8
#else
__mcount
#endif
Expand Down

0 comments on commit 07b8403

Please sign in to comment.