Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
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  * Unwind the current stack frame and store the new register values in the
0015  * structure passed as argument. Unwinding is equivalent to a function return,
0016  * hence the new PC value rather than LR should be used for backtrace.
0017  *
0018  * With framepointer enabled, a simple function prologue looks like this:
0019  *  mov ip, sp
0020  *  stmdb   sp!, {fp, ip, lr, pc}
0021  *  sub fp, ip, #4
0022  *
0023  * A simple function epilogue looks like this:
0024  *  ldm sp, {fp, sp, pc}
0025  *
0026  * When compiled with clang, pc and sp are not pushed. A simple function
0027  * prologue looks like this when built with clang:
0028  *
0029  *  stmdb   {..., fp, lr}
0030  *  add fp, sp, #x
0031  *  sub sp, sp, #y
0032  *
0033  * A simple function epilogue looks like this when built with clang:
0034  *
0035  *  sub sp, fp, #x
0036  *  ldm {..., fp, pc}
0037  *
0038  *
0039  * Note that with framepointer enabled, even the leaf functions have the same
0040  * prologue and epilogue, therefore we can ignore the LR value in this case.
0041  */
0042 int notrace unwind_frame(struct stackframe *frame)
0043 {
0044     unsigned long high, low;
0045     unsigned long fp = frame->fp;
0046 
0047     /* only go to a higher address on the stack */
0048     low = frame->sp;
0049     high = ALIGN(low, THREAD_SIZE);
0050 
0051 #ifdef CONFIG_CC_IS_CLANG
0052     /* check current frame pointer is within bounds */
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     /* check current frame pointer is within bounds */
0061     if (fp < low + 12 || fp > high - 4)
0062         return -EINVAL;
0063 
0064     /* restore the registers from the stack frame */
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)&regs[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 /* This must be noinline to so that our skip calculation works correctly */
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          * What guarantees do we have here that 'tsk' is not
0147          * running on another CPU?  For now, ignore it as we
0148          * can't guarantee we won't explode.
0149          */
0150         return;
0151 #else
0152         frame.fp = thread_saved_fp(tsk);
0153         frame.sp = thread_saved_sp(tsk);
0154         frame.lr = 0;       /* recovered from the stack */
0155         frame.pc = thread_saved_pc(tsk);
0156 #endif
0157     } else {
0158         /* We don't want this function nor the caller */
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