看下具体调用时序:
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)); }