1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/export.h> 3 #include <linux/sched.h> 4 #include <linux/sched/debug.h> 5 #include <linux/stacktrace.h> 6 7 #include <asm/sections.h> 8 #include <asm/stacktrace.h> 9 #include <asm/traps.h> 10 11 #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) 12 /* 13 * Unwind the current stack frame and store the new register values in the 14 * structure passed as argument. Unwinding is equivalent to a function return, 15 * hence the new PC value rather than LR should be used for backtrace. 16 * 17 * With framepointer enabled, a simple function prologue looks like this: 18 * mov ip, sp 19 * stmdb sp!, {fp, ip, lr, pc} 20 * sub fp, ip, #4 21 * 22 * A simple function epilogue looks like this: 23 * ldm sp, {fp, sp, pc} 24 * 25 * When compiled with clang, pc and sp are not pushed. A simple function 26 * prologue looks like this when built with clang: 27 * 28 * stmdb {..., fp, lr} 29 * add fp, sp, #x 30 * sub sp, sp, #y 31 * 32 * A simple function epilogue looks like this when built with clang: 33 * 34 * sub sp, fp, #x 35 * ldm {..., fp, pc} 36 * 37 * 38 * Note that with framepointer enabled, even the leaf functions have the same 39 * prologue and epilogue, therefore we can ignore the LR value in this case. 40 */ 41 int notrace unwind_frame(struct stackframe *frame) 42 { 43 unsigned long high, low; 44 unsigned long fp = frame->fp; 45 46 /* only go to a higher address on the stack */ 47 low = frame->sp; 48 high = ALIGN(low, THREAD_SIZE); 49 50 #ifdef CONFIG_CC_IS_CLANG 51 /* check current frame pointer is within bounds */ 52 if (fp < low + 4 || fp > high - 4) 53 return -EINVAL; 54 55 frame->sp = frame->fp; 56 frame->fp = *(unsigned long *)(fp); 57 frame->pc = frame->lr; 58 frame->lr = *(unsigned long *)(fp + 4); 59 #else 60 /* check current frame pointer is within bounds */ 61 if (fp < low + 12 || fp > high - 4) 62 return -EINVAL; 63 64 /* restore the registers from the stack frame */ 65 frame->fp = *(unsigned long *)(fp - 12); 66 frame->sp = *(unsigned long *)(fp - 8); 67 frame->pc = *(unsigned long *)(fp - 4); 68 #endif 69 70 return 0; 71 } 72 #endif 73 74 void notrace walk_stackframe(struct stackframe *frame, 75 int (*fn)(struct stackframe *, void *), void *data) 76 { 77 while (1) { 78 int ret; 79 80 if (fn(frame, data)) 81 break; 82 ret = unwind_frame(frame); 83 if (ret < 0) 84 break; 85 } 86 } 87 EXPORT_SYMBOL(walk_stackframe); 88 89 #ifdef CONFIG_STACKTRACE 90 struct stack_trace_data { 91 struct stack_trace *trace; 92 unsigned int no_sched_functions; 93 unsigned int skip; 94 }; 95 96 static int save_trace(struct stackframe *frame, void *d) 97 { 98 struct stack_trace_data *data = d; 99 struct stack_trace *trace = data->trace; 100 struct pt_regs *regs; 101 unsigned long addr = frame->pc; 102 103 if (data->no_sched_functions && in_sched_functions(addr)) 104 return 0; 105 if (data->skip) { 106 data->skip--; 107 return 0; 108 } 109 110 trace->entries[trace->nr_entries++] = addr; 111 112 if (trace->nr_entries >= trace->max_entries) 113 return 1; 114 115 if (!in_entry_text(frame->pc)) 116 return 0; 117 118 regs = (struct pt_regs *)frame->sp; 119 if ((unsigned long)®s[1] > ALIGN(frame->sp, THREAD_SIZE)) 120 return 0; 121 122 trace->entries[trace->nr_entries++] = regs->ARM_pc; 123 124 return trace->nr_entries >= trace->max_entries; 125 } 126 127 /* This must be noinline to so that our skip calculation works correctly */ 128 static noinline void __save_stack_trace(struct task_struct *tsk, 129 struct stack_trace *trace, unsigned int nosched) 130 { 131 struct stack_trace_data data; 132 struct stackframe frame; 133 134 data.trace = trace; 135 data.skip = trace->skip; 136 data.no_sched_functions = nosched; 137 138 if (tsk != current) { 139 #ifdef CONFIG_SMP 140 /* 141 * What guarantees do we have here that 'tsk' is not 142 * running on another CPU? For now, ignore it as we 143 * can't guarantee we won't explode. 144 */ 145 return; 146 #else 147 frame.fp = thread_saved_fp(tsk); 148 frame.sp = thread_saved_sp(tsk); 149 frame.lr = 0; /* recovered from the stack */ 150 frame.pc = thread_saved_pc(tsk); 151 #endif 152 } else { 153 /* We don't want this function nor the caller */ 154 data.skip += 2; 155 frame.fp = (unsigned long)__builtin_frame_address(0); 156 frame.sp = current_stack_pointer; 157 frame.lr = (unsigned long)__builtin_return_address(0); 158 frame.pc = (unsigned long)__save_stack_trace; 159 } 160 161 walk_stackframe(&frame, save_trace, &data); 162 } 163 164 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 165 { 166 struct stack_trace_data data; 167 struct stackframe frame; 168 169 data.trace = trace; 170 data.skip = trace->skip; 171 data.no_sched_functions = 0; 172 173 frame.fp = regs->ARM_fp; 174 frame.sp = regs->ARM_sp; 175 frame.lr = regs->ARM_lr; 176 frame.pc = regs->ARM_pc; 177 178 walk_stackframe(&frame, save_trace, &data); 179 } 180 181 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 182 { 183 __save_stack_trace(tsk, trace, 1); 184 } 185 EXPORT_SYMBOL(save_stack_trace_tsk); 186 187 void save_stack_trace(struct stack_trace *trace) 188 { 189 __save_stack_trace(current, trace, 0); 190 } 191 EXPORT_SYMBOL_GPL(save_stack_trace); 192 #endif 193