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