0001
0002 #include <linux/export.h>
0003 #include <linux/kprobes.h>
0004 #include <linux/sched.h>
0005 #include <linux/sched/debug.h>
0006 #include <linux/stacktrace.h>
0007
0008 #include <asm/sections.h>
0009 #include <asm/stacktrace.h>
0010 #include <asm/traps.h>
0011
0012 #if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 int notrace unwind_frame(struct stackframe *frame)
0043 {
0044 unsigned long high, low;
0045 unsigned long fp = frame->fp;
0046
0047
0048 low = frame->sp;
0049 high = ALIGN(low, THREAD_SIZE);
0050
0051 #ifdef CONFIG_CC_IS_CLANG
0052
0053 if (fp < low + 4 || fp > high - 4)
0054 return -EINVAL;
0055
0056 frame->sp = frame->fp;
0057 frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp));
0058 frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp + 4));
0059 #else
0060
0061 if (fp < low + 12 || fp > high - 4)
0062 return -EINVAL;
0063
0064
0065 frame->fp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 12));
0066 frame->sp = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 8));
0067 frame->pc = READ_ONCE_NOCHECK(*(unsigned long *)(fp - 4));
0068 #endif
0069 #ifdef CONFIG_KRETPROBES
0070 if (is_kretprobe_trampoline(frame->pc))
0071 frame->pc = kretprobe_find_ret_addr(frame->tsk,
0072 (void *)frame->fp, &frame->kr_cur);
0073 #endif
0074
0075 return 0;
0076 }
0077 #endif
0078
0079 void notrace walk_stackframe(struct stackframe *frame,
0080 int (*fn)(struct stackframe *, void *), void *data)
0081 {
0082 while (1) {
0083 int ret;
0084
0085 if (fn(frame, data))
0086 break;
0087 ret = unwind_frame(frame);
0088 if (ret < 0)
0089 break;
0090 }
0091 }
0092 EXPORT_SYMBOL(walk_stackframe);
0093
0094 #ifdef CONFIG_STACKTRACE
0095 struct stack_trace_data {
0096 struct stack_trace *trace;
0097 unsigned int no_sched_functions;
0098 unsigned int skip;
0099 };
0100
0101 static int save_trace(struct stackframe *frame, void *d)
0102 {
0103 struct stack_trace_data *data = d;
0104 struct stack_trace *trace = data->trace;
0105 struct pt_regs *regs;
0106 unsigned long addr = frame->pc;
0107
0108 if (data->no_sched_functions && in_sched_functions(addr))
0109 return 0;
0110 if (data->skip) {
0111 data->skip--;
0112 return 0;
0113 }
0114
0115 trace->entries[trace->nr_entries++] = addr;
0116
0117 if (trace->nr_entries >= trace->max_entries)
0118 return 1;
0119
0120 if (!in_entry_text(frame->pc))
0121 return 0;
0122
0123 regs = (struct pt_regs *)frame->sp;
0124 if ((unsigned long)®s[1] > ALIGN(frame->sp, THREAD_SIZE))
0125 return 0;
0126
0127 trace->entries[trace->nr_entries++] = regs->ARM_pc;
0128
0129 return trace->nr_entries >= trace->max_entries;
0130 }
0131
0132
0133 static noinline void __save_stack_trace(struct task_struct *tsk,
0134 struct stack_trace *trace, unsigned int nosched)
0135 {
0136 struct stack_trace_data data;
0137 struct stackframe frame;
0138
0139 data.trace = trace;
0140 data.skip = trace->skip;
0141 data.no_sched_functions = nosched;
0142
0143 if (tsk != current) {
0144 #ifdef CONFIG_SMP
0145
0146
0147
0148
0149
0150 return;
0151 #else
0152 frame.fp = thread_saved_fp(tsk);
0153 frame.sp = thread_saved_sp(tsk);
0154 frame.lr = 0;
0155 frame.pc = thread_saved_pc(tsk);
0156 #endif
0157 } else {
0158
0159 data.skip += 2;
0160 frame.fp = (unsigned long)__builtin_frame_address(0);
0161 frame.sp = current_stack_pointer;
0162 frame.lr = (unsigned long)__builtin_return_address(0);
0163 here:
0164 frame.pc = (unsigned long)&&here;
0165 }
0166 #ifdef CONFIG_KRETPROBES
0167 frame.kr_cur = NULL;
0168 frame.tsk = tsk;
0169 #endif
0170
0171 walk_stackframe(&frame, save_trace, &data);
0172 }
0173
0174 void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
0175 {
0176 struct stack_trace_data data;
0177 struct stackframe frame;
0178
0179 data.trace = trace;
0180 data.skip = trace->skip;
0181 data.no_sched_functions = 0;
0182
0183 frame.fp = regs->ARM_fp;
0184 frame.sp = regs->ARM_sp;
0185 frame.lr = regs->ARM_lr;
0186 frame.pc = regs->ARM_pc;
0187 #ifdef CONFIG_KRETPROBES
0188 frame.kr_cur = NULL;
0189 frame.tsk = current;
0190 #endif
0191
0192 walk_stackframe(&frame, save_trace, &data);
0193 }
0194
0195 void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
0196 {
0197 __save_stack_trace(tsk, trace, 1);
0198 }
0199 EXPORT_SYMBOL(save_stack_trace_tsk);
0200
0201 void save_stack_trace(struct stack_trace *trace)
0202 {
0203 __save_stack_trace(current, trace, 0);
0204 }
0205 EXPORT_SYMBOL_GPL(save_stack_trace);
0206 #endif