ARM寄存器的定义

转载时请注明出处和作者联系方式: http://blog.csdn.net/mimepp
ARM系统中, 当crash发生时的back trace调试输出
作者:余涛(yut616_at_sohu.com)
关键字:一般保护错  back trace

在ARM target board上, 如果发生异常,如内存访问越界等情况,有时会非常难debug到底是哪里出错,近来看了一下back trace回溯的功能及实现,在这里做个笔记。
 
首先,back trace要涉及到一堆寄存器, 不过不用担心, 实际上没你想象的那么难。
 
在linux kernel的代码中可以看到关于ARM寄存器的定义,在这里列一下:
linux / include / asm - arm / proc - armv / ptrace.h
struct  pt_regs 
       
long uregs[18]; 
}

   
#define  ARM_cpsr            uregs[16]  // 当前程序状态寄存器 
#define  ARM_pc              uregs[15]  // 程序计数器 指令计数器,下一条将要执行的指令
#define  ARM_lr              uregs[14]  // 链接link寄存器,是退出时要装载到pc中的值,返回回来时就是到这个里面去执行之前的代码
#define  ARM_sp              uregs[13]  // 栈指针 
#define  ARM_ip              uregs[12]  // 指令指针,指向下一指令
#define  ARM_fp              uregs[11]  // 帧指针
#define  ARM_r10             uregs[10] 
#define  ARM_r9              uregs[9] 
#define  ARM_r8              uregs[8] 
#define  ARM_r7              uregs[7] 
#define  ARM_r6              uregs[6] 
#define  ARM_r5              uregs[5] 
#define  ARM_r4              uregs[4] 
#define  ARM_r3              uregs[3] 
#define  ARM_r2              uregs[2] 
#define  ARM_r1              uregs[1] 
#define  ARM_r0              uregs[0] 
#define  ARM_ORIG_r0         uregs[17] 

 这里比较重要的几个寄存器是fp,lr,sp,继续往后讨论。
 
我们可以先想象一个情景,某个调用过程
A(…)
{
     …;
    B(…);
   printf("aaa ");
}
 
在A调B的时候,大体的过程会是A在执行,执行到B,进入B执行,B执行完后,在A中的B下面的部分继续执行。
这样的话,在转到B去执行的时候,要准备好一些东西,来保证B完成时还回到A里来继续往下执行。
怎么来保证上面的这个问题呢,那就是要用到lr寄存器来存A处的信息,这个是link register链接寄存器的缩写,它就好比一个链条一样,把一堆调用给串起来。
所以上面的过程就会是,要执行到B时,把B的地址放入pc中,这样下面就要执行B了。Pc是指令计数器,放在其中的地址就将被执行。这个时候还要保存刚才我们提到的A的信息,把需要返回的值放在lr中。
 
在ARM的过程调用中,有个结构叫回溯结构,还有两个概念叫帧和栈,它们的关系是:
   回溯结构
|-----------|------------------------------|   帧
栈     …                                             帧
…                                             帧
 
回溯结构在帧的高端,通过这个回溯结构,就可以把一个一个的帧连接成栈。
帧指针fp,frame pointer就是指向把栈给串起来的一个一个回溯结构所组成的列表中的最后一个结构。这样就可以通过这个fp来追溯函数的调用顺序,在系统崩溃时就可以得到最后调用的函数都是哪些。
 
回溯结构是:
                                                                 高地址
保存代码指针          [fp]           fp指向这里
返回lr值              [fp, #
- 4 ]
返回sp值              [fp, #
- 8 ]
返回fp值              [fp, #
- 12 ]     指向下一个结构

                                                                 低地址
 
从上面的内容,我们只要循环打印上面lr中的值就可以得到我们想要的一系列的地址了。
long  fp, nextfp;
fp 
=  regs -> ARM_fp;
long  lr;
for (XXX;XXX;XXX) //循环多少,可以自己决定
{
lr 
= *(( long *)(fp - 4)); 
nextfp 
= *(( long *) fp - 12));
fp 
= nextfp;
 
printk(“call address: 0x
%lx ”, lr- current->mm->start_code - 4);
}

上面的current->mm->start_code是当前进程的数据段基地址,再减4,就能得到返回处前面的地址。打印出来的内容也就是调用函数的地址。
 如:
call address:  0x2849cc
call address: 
0x3b8b70
call address: 
0x2c270
call address: 
0x2c5ec
call address: 
0x2b298
call address: 
0x283934
call address: 
0x3c41a8

你可以用objdump得到你的执行文件的sym文件,如:
arm-elf-objdump -SD test.gdb > /tmp/test.gdb.sym
然后就可以拿这个循环打印出来的地址,来搜索出错的位置了。
   
上面的代码只是一个参考,具体还要看你的系统实际的情况。 
   
另外还看到一个start_thread,列一下做个记载。
#define  start_thread(regs,pc,sp)                                 
(
{                                                             
       unsigned 
long *stack = (unsigned long *)sp;                      
       set_fs(USER_DS);                                       
       memzero(regs
->uregs, sizeof(regs->uregs));                      
   
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值