Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: ftrace: enable the graph tracer with the EABI unwinder

Enable the function graph tracer in combination with the EABI unwinder,
so that Thumb2 builds or Clang ARM builds can make use of it.

This involves using the unwinder to locate the return address of an
instrumented function on the stack, so that it can be overridden and
made to refer to the ftrace handling routines that need to be called at
function return.

Given that for these builds, it is not guaranteed that the value of the
link register is stored on the stack, fall back to the stack slot that
will be used by the ftrace exit code to restore LR in the instrumented
function's execution context.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Steven Rostedt (Google) <rostedt@goodmis.org>

+40 -34
+1 -1
arch/arm/Kconfig
··· 91 91 select HAVE_EXIT_THREAD 92 92 select HAVE_FAST_GUP if ARM_LPAE 93 93 select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL 94 - select HAVE_FUNCTION_GRAPH_TRACER if !THUMB2_KERNEL && !CC_IS_CLANG 94 + select HAVE_FUNCTION_GRAPH_TRACER 95 95 select HAVE_FUNCTION_TRACER if !XIP_KERNEL && !(THUMB2_KERNEL && CC_IS_CLANG) 96 96 select HAVE_FUTEX_CMPXCHG if FUTEX 97 97 select HAVE_GCC_PLUGINS
+1 -1
arch/arm/Kconfig.debug
··· 65 65 66 66 config UNWINDER_ARM 67 67 bool "ARM EABI stack unwinder" 68 - depends on AEABI && !FUNCTION_GRAPH_TRACER 68 + depends on AEABI 69 69 # https://github.com/ClangBuiltLinux/linux/issues/732 70 70 depends on !LD_IS_LLD || LLD_VERSION >= 110000 71 71 select ARM_UNWIND
-18
arch/arm/include/asm/ftrace.h
··· 35 35 36 36 #ifndef __ASSEMBLY__ 37 37 38 - #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 39 - /* 40 - * return_address uses walk_stackframe to do it's work. If both 41 - * CONFIG_FRAME_POINTER=y and CONFIG_ARM_UNWIND=y walk_stackframe uses unwind 42 - * information. For this to work in the function tracer many functions would 43 - * have to be marked with __notrace. So for now just depend on 44 - * !CONFIG_ARM_UNWIND. 45 - */ 46 - 47 38 void *return_address(unsigned int); 48 - 49 - #else 50 - 51 - static inline void *return_address(unsigned int level) 52 - { 53 - return NULL; 54 - } 55 - 56 - #endif 57 39 58 40 #define ftrace_return_address(n) return_address(n) 59 41
+1 -4
arch/arm/kernel/Makefile
··· 25 25 KASAN_SANITIZE_stacktrace.o := n 26 26 KASAN_SANITIZE_traps.o := n 27 27 28 - ifneq ($(CONFIG_ARM_UNWIND),y) 29 - obj-$(CONFIG_FRAME_POINTER) += return_address.o 30 - endif 31 - 28 + obj-y += return_address.o 32 29 obj-$(CONFIG_ATAGS) += atags_parse.o 33 30 obj-$(CONFIG_ATAGS_PROC) += atags_proc.o 34 31 obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += atags_compat.o
+20 -8
arch/arm/kernel/entry-ftrace.S
··· 100 100 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 101 101 .globl ftrace_graph_regs_call 102 102 ftrace_graph_regs_call: 103 - mov r0, r0 103 + ARM( mov r0, r0 ) 104 + THUMB( nop.w ) 104 105 #endif 105 106 106 107 @ pop saved regs ··· 113 112 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 114 113 .macro __ftrace_graph_regs_caller 115 114 116 - sub r0, fp, #4 @ lr of instrumented routine (parent) 115 + #ifdef CONFIG_UNWINDER_FRAME_POINTER 116 + sub r0, fp, #4 @ lr of instrumented routine (parent) 117 + #else 118 + add r0, sp, #S_LR 119 + #endif 117 120 118 121 @ called from __ftrace_regs_caller 119 - ldr r1, [sp, #S_PC] @ instrumented routine (func) 122 + ldr r1, [sp, #S_PC] @ instrumented routine (func) 120 123 mcount_adjust_addr r1, r1 121 124 122 - mov r2, fp @ frame pointer 125 + mov r2, fpreg @ frame pointer 126 + add r3, sp, #PT_REGS_SIZE 123 127 bl prepare_ftrace_return 124 128 125 129 @ pop registers saved in ftrace_regs_caller ··· 155 149 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 156 150 .globl ftrace_graph_call\suffix 157 151 ftrace_graph_call\suffix: 158 - mov r0, r0 152 + ARM( mov r0, r0 ) 153 + THUMB( nop.w ) 159 154 #endif 160 155 161 156 mcount_exit 162 157 .endm 163 158 164 159 .macro __ftrace_graph_caller 160 + #ifdef CONFIG_UNWINDER_FRAME_POINTER 165 161 sub r0, fp, #4 @ &lr of instrumented routine (&parent) 162 + #else 163 + add r0, sp, #20 164 + #endif 166 165 #ifdef CONFIG_DYNAMIC_FTRACE 167 166 @ called from __ftrace_caller, saved in mcount_enter 168 167 ldr r1, [sp, #16] @ instrumented routine (func) ··· 176 165 @ called from __mcount, untouched in lr 177 166 mcount_adjust_addr r1, lr @ instrumented routine (func) 178 167 #endif 179 - mov r2, fp @ frame pointer 168 + mov r2, fpreg @ frame pointer 169 + add r3, sp, #24 180 170 bl prepare_ftrace_return 181 171 mcount_exit 182 172 .endm ··· 256 244 .purgem mcount_exit 257 245 258 246 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 259 - .globl return_to_handler 260 - return_to_handler: 247 + ENTRY(return_to_handler) 261 248 stmdb sp!, {r0-r3} 262 249 add r0, sp, #16 @ sp at exit of instrumented routine 263 250 bl ftrace_return_to_handler 264 251 mov lr, r0 @ r0 has real ret addr 265 252 ldmia sp!, {r0-r3} 266 253 ret lr 254 + ENDPROC(return_to_handler) 267 255 #endif 268 256 269 257 ENTRY(ftrace_stub)
+17 -2
arch/arm/kernel/ftrace.c
··· 22 22 #include <asm/ftrace.h> 23 23 #include <asm/insn.h> 24 24 #include <asm/set_memory.h> 25 + #include <asm/stacktrace.h> 25 26 #include <asm/patch.h> 26 27 27 28 /* ··· 225 224 #endif /* CONFIG_DYNAMIC_FTRACE */ 226 225 227 226 #ifdef CONFIG_FUNCTION_GRAPH_TRACER 227 + asmlinkage 228 228 void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, 229 - unsigned long frame_pointer) 229 + unsigned long frame_pointer, 230 + unsigned long stack_pointer) 230 231 { 231 232 unsigned long return_hooker = (unsigned long) &return_to_handler; 232 233 unsigned long old; ··· 239 236 if (IS_ENABLED(CONFIG_UNWINDER_FRAME_POINTER)) { 240 237 /* FP points one word below parent's top of stack */ 241 238 frame_pointer += 4; 239 + } else { 240 + struct stackframe frame = { 241 + .fp = frame_pointer, 242 + .sp = stack_pointer, 243 + .lr = self_addr, 244 + .pc = self_addr, 245 + }; 246 + if (unwind_frame(&frame) < 0) 247 + return; 248 + if (frame.lr != self_addr) 249 + parent = frame.lr_addr; 250 + frame_pointer = frame.sp; 242 251 } 243 252 244 253 old = *parent; ··· 273 258 unsigned long caller_fn = (unsigned long) func; 274 259 unsigned long pc = (unsigned long) callsite; 275 260 unsigned long branch = arm_gen_branch(pc, caller_fn); 276 - unsigned long nop = 0xe1a00000; /* mov r0, r0 */ 261 + unsigned long nop = arm_gen_nop(); 277 262 unsigned long old = enable ? nop : branch; 278 263 unsigned long new = enable ? branch : nop; 279 264