看下具体调用时序:
void dump_stack(void)
{
dump_backtrace(NULL, NULL);
}
dump_stack
-->dump_backtrace
-->unwind_backtrace
- ->dump_backtrace_entry
-->printk(%pS)
- ->kallsyms_lookup
void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
struct stackframe frame;
......
//得到 FP ,SP ,LR ,及 PC的值
//其中真正用的是: FP及PC
if (regs) {
frame.fp = regs->ARM_fp;
frame.sp = regs->ARM_sp;
frame.lr = regs->ARM_lr;
/* PC might be corrupted, use LR in that case. */
frame.pc = kernel_text_address(regs->ARM_pc)
? regs->ARM_pc : regs->ARM_lr;
}
while (1) {
int urc;
unsigned long where = frame.pc;
//设置上一帧的FP,PC,SP等值
urc = unwind_frame(&frame);
if (urc < 0)
break;
//打印出对应函数指针的函数名及偏移量
dump_backtrace_entry(where, frame.pc, frame.sp - 4);
}
}
// 堆栈的大小,FP指针不能超过此值
#define THREAD_SIZE 8192
int notrace unwind_frame(struct stackframe *frame)
{
unsigned long high, low;
unsigned long fp = frame->fp;
//此时,堆栈地址是一步一步往高地址爬的
low = frame->sp;
high = ALIGN(low, THREAD_SIZE);
//确保帧指针是合法的
if (fp < (low + 12) || fp + 4 >= high)
return -EINVAL;
//从堆栈中取出之前备份的值,设置成上一帧的值
// 为什么是 -12 -8 - 4
//因为函数调用前大概会执行:
// ldm sp, {fp, sp, pc} 把当前fp, sp, pc的值保存到堆栈中
frame->fp = *(unsigned long *)(fp - 12);
frame->sp = *(unsigned long *)(fp - 8);
frame->pc = *(unsigned long *)(fp - 4);
return 0;
}
大家可以想想,为什么有了LR寄存器,还要保存pc值在堆栈中。
个人LR保存的是调用函数指令,下面的代码地址,不能反映当时真实的函数调用
配合下面这张函数调用堆栈图,可能会更清晰些:
当然编译内核的时候,要打开 CONFIG_KALLSYMS ,这样才能查找找函数地址对应的函数名及偏移量
void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
{
#ifdef CONFIG_KALLSYMS
//记住%pS是关键 printk 与 普通printf最大的不同
//惭愧啊,现在才知道此选项
printk("symbol<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from);
#else
printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
#endif
if (in_exception_text(where))
dump_mem("", "Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs));
}