Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1337539
  • 博文数量: 482
  • 博客积分: 13297
  • 博客等级: 上将
  • 技术积分: 2890
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-12 16:25
文章分类

全部博文(482)

文章存档

2012年(9)

2011年(407)

2010年(66)

分类: LINUX

2011-08-10 22:10:50

先来说说arm指令的处理过程:

pc寄存器保存的总是当前正在取指的指令的地址。

如果有如下指令序列S1,S2,S3,S4,S5这五条指令在内存中分布为:

PC寄存器保存的总是当前正在取址的指令的地址,arm态下指令长度为4个字节,如图所示当前正在执行的指令为pc-8即pc的值为当前正在执行的指令加8

同理在arm9五级流水的情况下:

了解一下pc与arm处理指令的过程有利于我们理解异常处理进入时lr保存的pc值  与函数返回时所要执行的指令之间的关系。

在异常发生时常常要保存当前指令地址,即pc保存到lr中。以用来异常处理完毕的返回。

要清楚返回地址,我们必须弄清楚三点:

1.该种异常或者程序跳转发生在什么时候?

2.异常发生时pc的值是否更新

3.异常返回时要接着执行的指令是哪一条?

接着我们来看看各种异常发生后  进入处理函数 处理完毕如何返回 返回包含如下两个操作:
1.将SPSR_mode复制给CPSR
2.返回到异常发生处所要执行的下一条指令   之所以这么说是因为可能返回的时候进行取指操作的第一条指令还是那条触发异常发生的指令  或者触发异常发生的指令的下一条指令  而非仅仅是将lr赋给pc  
 

在谈异常返回pc的值与lr_mode之间的换算关系前    先看看两个跳转指令B 和 BL

还有一点我们要清楚的是PC总是指向当前正在执行的指令的下两条指令    即pc指向的是当前正在取址的指令

对比一下两个代码:

 

 

  1. mov lr,pc  
  2. b main  
  3. add r0,r0,r1  
 
  1. bl main  
  2. add r0,r0,r1  
  3. sub r0,r3,r2  

这两段代码的执行效果是一样的   只是第一段代码采用B指令跳转的   我们要清楚当前执行的指令为mov lr,pc  而当前取指的指令为add r0,r0,r1    所以pc保存的是add r0,r0,r1的地址    所以执行完mov lr,pc后   lr保存的也是add r0,r0,r1的地址 

而第二段指令采用BL跳转  lr的值由arm计算得到     其实当执行bl main的时候   pc指向的是sub r0,r3,r2     但是arm核自动保存pc-4到LR中(arm态)   thumb态下为PC-2  

 

所以跳转的返回只需要mov pc,lr  上面两个跳转就能正常的返回了。

之所以对比这两个   我想弄清楚的是  当arm核自动保存返回地址到lr中的情况下   arm状态总是保存pc-4    thumb状态保存pc-2 也就是说自动保存的lr总是指向当前执行指令的下一条指令

现在可以来分析一下各种异常处理返回时如何手动编码了    (*^__^*) 嘻嘻……   当然这个不可能自动编码  自动编码的是进入异常  (*^__^*) 嘻嘻……

1.SWI和未定义指令异常中断处理程序的返回

我们还是以S1,S2,S3,S4,S5的指令序列为例:

SWI和未定义指令异常是执行的指令触发的,如果当前执行的指令为S1,即触发SWI和未定义指令异常的指令是S1,此时PC指向的是S3(S3此时取指),异常发生时arm核自动将PC-4(arm态)或者PC-2(thumb态)存入LR_mode,级LR_mode保存的是S2的地址。

SWI和未定义指令异常返回时应该进行取指操作的指令为触发异常的指令的下一条指令即S2所以返回时应有的PC值和LR相等。

所以在不使用堆栈数据的时候,可以用如下指令返回

  1. MOVS PC,LR  
 

这里MOVS区别于mov的是:movs在将LR赋给PC的同时,将SPSR_mode赋给CPSR

而在使用堆栈的情况下就应该用以下指令处理了

  1. STMFD SP!,{要入栈的寄存器,LR}  
  2. @@@  
  3. @这里是异常处理的代码  
  4. @@@  
  5. LDMFD sp!,{入栈的寄存器,PC}^  
 

注意这里^表示在指令执行的同时将SPSR_mode赋给CPSR

 

 

2.IRQ和FIQ异常中断处理程序的返回

这两类中断发生在当前指令执行完毕之后,arm核查询发现有中断发生才触发的异常,此时PC已经更新。如果当前刚执行完毕S1指令,PC更新指向了S4指令,此时触发了IRQ或者FIQ异常,则LR自动保存的值为PC-4(arm态)或者PC-2(thumb态)此时LR_mode保存的是S3的地址。而异常返回时应该执行的指令为S2,所以我们返回时可以采用如下指令返回:

不使用堆栈的情况下:

  1. SUBS PC,LR,#4  
 

subs和movs相同,即将LR-4保存到PC的同时  将SPSR_mode赋给CPSR

使用堆栈的情况下:

  1. SUBS PC,LR,#4  
  2. STMFD SP!,{要入栈的寄存器,LR}  
  3. @@@  
  4. @这里是异常处理的代码  
  5. @@@  
  6. LDMFD SP!,{入栈的寄存器,PC}^  
 

3.指令预取终止异常中断处理程序的返回

指令预取终止异常发生在指令执行期,其实在指令取指的时候已经被标识为有问题的指令,只是在执行时才触发异常,而指令预取终止异常返回时仍需对这条指令重新取指。比如S1执行时触发了指令预取终止异常,则LR_mode保存的是S2的地址,而返回时需要重新取指。所以,返回取址的指令为S1,即PC-4(arm态)或者PC-2(thumb态)

所以返回时可以用如下方式:

不使用堆栈的情况下:

  1. SUBS PC,LR,#4  
 

使用堆栈的情况下:

  1. SUBS LR,LR,#4  
  2. STMFD SP!,{要入栈的寄存器,LR}  
  3. @@@  
  4. @这里是异常处理的代码  
  5. @@@  
  6. LDMFD SP!,{入栈的寄存器,PC}^  
 

 

 

4.数据访问终止异常中断处理程序的返回

数据访问终止异常是指令在访存期触发的,比如触发该异常的为S1,则当前执行的指令为S2,PC执行的指令为S4,当异常发生时LR_mode保存的是S3的地址,而数据访问终止异常返回时要求重新取指该异常指令。所以,返回取址的指令为S1,即PC-8(arm态)或者PC-4(thumb态)

 

所以返回时可以用如下方式:

不使用堆栈的情况下:

  1. SUBS PC,LR,#8  
 

使用堆栈的情况下:

  1. SUBS LR,LR,#8  
  2. STMFD SP!,{要入栈的寄存器,LR}  
  3. @@@  
  4. @这里是异常处理的代码  
  5. @@@  
  6. LDMFD SP!,{入栈的寄存器,PC}^  
 

好了这篇文章也差不多了,花了一天多的时间来理解这个,可能我的理解有误或者该方法完全只适合记忆,非常欢迎看到此文的朋友一起探讨指正!

====

http://blog.csdn.net/leftover_crazy/article/details/6064169

阅读(1838) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~