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 = *(unsigned long *)(fp + 4); 58 #else 59 /* check current frame pointer is within bounds */ 60 if (fp < low + 12 || fp > high - 4) 61 return -EINVAL; 62 63 /* restore the registers from the stack frame */ 64 frame->fp = *(unsigned long *)(fp - 12); 65 frame->sp = *(unsigned long *)(fp - 8); 66 frame->pc = *(unsigned long *)(fp - 4); 67 #endif 68 69 return 0; 70 } 71 #endif 72 73 void notrace walk_stackframe(struct stackframe *frame, 74 int (*fn)(struct stackframe *, void *), void *data) 75 { 76 while (1) { 77 int ret; 78 79 if (fn(frame, data)) 80 break; 81 ret = unwind_frame(frame); 82 if (ret < 0) 83 break; 84 } 85 } 86 EXPORT_SYMBOL(walk_stackframe); 87 88 #ifdef CONFIG_STACKTRACE 89 struct stack_trace_data { 90 struct stack_trace *trace; 91 unsigned int no_sched_functions; 92 unsigned int skip; 93 }; 94 95 static int save_trace(struct stackframe *frame, void *d) 96 { 97 struct stack_trace_data *data = d; 98 struct stack_trace *trace = data->trace; 99 struct pt_regs *regs; 100 unsigned long addr = frame->pc; 101 102 if (data->no_sched_functions && in_sched_functions(addr)) 103 return 0; 104 if (data->skip) { 105 data->skip--; 106 return 0; 107 } 108 109 trace->entries[trace->nr_entries++] = addr; 110 111 if (trace->nr_entries >= trace->max_entries) 112 return 1; 113 114 if (!in_entry_text(frame->pc)) 115 return 0; 116 117 regs = (struct pt_regs *)frame->sp; 118 if ((unsigned long)®s[1] > ALIGN(frame->sp, THREAD_SIZE)) 119 return 0; 120 121 trace->entries[trace->nr_entries++] = regs->ARM_pc; 122 123 return trace->nr_entries >= trace->max_entries; 124 } 125 126 /* This must be noinline to so that our skip calculation works correctly */ 127 static noinline void __save_stack_trace(struct task_struct *tsk, 128 struct stack_trace *trace, unsigned int nosched) 129 { 130 struct stack_trace_data data; 131 struct stackframe frame; 132 133 data.trace = trace; 134 data.skip = trace->skip; 135 data.no_sched_functions = nosched; 136 137 if (tsk != current) { 138 #ifdef CONFIG_SMP 139 /* 140 * What guarantees do we have here that 'tsk' is not 141 * running on another CPU? For now, ignore it as we 142 * can't guarantee we won't explode. 143 */ 144 return; 145 #else 146 frame.fp = thread_saved_fp(tsk); 147 frame.sp = thread_saved_sp(tsk); 148 frame.lr = 0; /* recovered from the stack */ 149 frame.pc = thread_saved_pc(tsk); 150 #endif 151 } else { 152 /* We don't want this function nor the caller */ 153 data.skip += 2; 154 frame.fp = (unsigned long)__builtin_frame_address(0); 155 frame.sp = current_stack_pointer; 156 frame.lr = (unsigned long)__builtin_return_address(0); 157 frame.pc = (unsigned long)__save_stack_trace; 158 } 159 160 walk_stackframe(&frame, save_trace, &data); 161 } 162 163 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) 164 { 165 struct stack_trace_data data; 166 struct stackframe frame; 167 168 data.trace = trace; 169 data.skip = trace->skip; 170 data.no_sched_functions = 0; 171 172 frame.fp = regs->ARM_fp; 173 frame.sp = regs->ARM_sp; 174 frame.lr = regs->ARM_lr; 175 frame.pc = regs->ARM_pc; 176 177 walk_stackframe(&frame, save_trace, &data); 178 } 179 180 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) 181 { 182 __save_stack_trace(tsk, trace, 1); 183 } 184 EXPORT_SYMBOL(save_stack_trace_tsk); 185 186 void save_stack_trace(struct stack_trace *trace) 187 { 188 __save_stack_trace(current, trace, 0); 189 } 190 EXPORT_SYMBOL_GPL(save_stack_trace); 191 #endif 192