mips的回溯,核心思想就是一层一层的找调用函数的ra 叶子函数的ra就是当前寄存器,非叶子函数的ra保存在函数栈里
例如
func3()
{
}
func2()
{
func3();
}
func1()
{
func2();
}
main()
{
func3();
}
func3: //叶子函数,所以ra一直没有变化过,返回时可以jr ra来返回
addi sp,sp,-24 //于是可以通过寄存器里的ra值得到上一级pc
xxx
jr ra
func2:
addi sp,sp,-52 //非叶子函数,如果要找到ra,就需要根据sp里的值来确认
sw ra,48(sp) //根据当前sp的值,加上52是栈帧,接着减去(52-48),就得到ra保存的位置,实际上就是sp+48,取出ra得到func1里的某句地址
jal func3 //即上一层pc,jal会把pc的下一条指令放到ra里,再进行跳转,相对于jr直接跳转
nop
func1: //同上
addi sp,sp,-48
sw ra,44(sp)
jal func2
nop
打印用户态堆栈回溯
arch/mips/kernel/traps.c
#define USER_LEN 200
int sysctl_user_len = 200;
void show_user_raw_stack(void)
{
struct pt_regs *regs;
unsigned long tmp = 0;
unsigned long sp;
int i=0;
unsigned long val;
int line = 0;
regs = task_pt_regs(current);
sp = regs->regs[29];
if(!current->mm) /*内核态线程没有用户态堆栈*/
return;
printk("current process:[%d %s]-[0x%lx] \n",current->pid,current->comm,regs->cp0_epc);
printk("dump user stack:");
for(;i
if (access_ok(VERIFY_READ, (unsigned long*)sp+i, sizeof(unsigned long)))
{
if(!(line%4))
printk("\n%p:",(unsigned long*)sp+i);
line++;
copy_from_user(&val, (unsigned long*)sp+i, sizeof(unsigned long));
#ifndef CONFIG_64BIT
printk("%08lx ",val);
#else
printk("%016lx ",val);
#endif
}
}
printk("\ndump end\n");
}