ARM指令在Thumb模式和arm模式之间跳转

ARM 80浏览

根据arm spec, 跳转地址最低位( lsb ) 为0表示 arm 指令;最低位为1表示thumb指令。

一、绝对地址跳转进入 thumb模式

如下指令可以切换到thumb模式。

[cpp] view
plain
 copy

 在CODE上查看代码片派生到我的代码片

  1. LDR R6, =0x24000  
  2. ADD R6, #1       @ (set lsb to 1)  
  3. BX R6  

如果已经知道 0x24000 地址存放的是 thumb 指令, 可以直接使用如下方式切换到arm 模式

[java] view
plain
 copy

 在CODE上查看代码片派生到我的代码片

  1. LDR R6, =0x24001  
  2. BX R6  


因此, 无论当前处理器模式是thumb或者arm, 只要0x24000 地址处存放的是 thumb 指令, 就可以通过 BX 指令跳转到 thumb 模式。

二、标号跳转进入thumb 模式

如果不知道链接地址,只知道标号,可以用如下指令跳转。

ldr r6,=label
bx r6

上述指令的问题在于,如果linker 知道 label 对应的是一条 arm 指令或者 thumb 指令,该指令能正常工作。

如果linker不能正确区别 label 的指令类型,将会导致错误。 

指令示例

[java] view
plain
 copy

 在CODE上查看代码片派生到我的代码片

  1. .thumb  
  2. ping:  
  3.     ldr r0,=pong  
  4.     bx r0  
  5. .code 32  
  6. pong:  
  7.     ldr r0,=ping  
  8.     bx r0  


编译链接后如下

[java] view
plain
 copy

 在CODE上查看代码片派生到我的代码片

  1. d6008148 <ping>:  
  2. d6008148:   4803        ldr r0, [pc, #12]   ; (d6008158 <pong+0xc>)  
  3. d600814a:   4700        bx  r0  
  4.   
  5. d600814c <pong>:  
  6. d600814c:   e59f0008    ldr r0, [pc, #8]    ; d600815c <pong+0x10>  
  7. d6008150:   e12fff10    bx  r0  
  8.   
  9. d6008158:   d600814c    strle   r8, [r0], -ip, asr #2  
  10. d600815c:   d6008148    strle   r8, [r0], -r8, asr #2  


ping 可以正确 bx r0 跳转到 0xd6008158。 

但是pong 不能。 因为 pong 通过 bx r0 跳转到 0xd600815c 地址后得到的是一条 arm 地址 0xd6008148 ( lsb=0 )。

GNU linker 帮助开发者解决了这些麻烦。GNU 汇编器 as 定义了 .thumb_func 符号。

如果正确的使用了该符号, GNU assembler/linker 将会正确的生成对应的指令序列。

改写后的指令示例

[java] view
plain
 copy

 在CODE上查看代码片派生到我的代码片

  1. .thumb  
  2. .thumb_func  
  3. ping:  
  4.     ldr r0,=pong  
  5.     bx r0  
  6. .code 32  
  7. pong:  
  8.     ldr r0,=ping  
  9.     bx r0  


编译链接后的指令

[java] view
plain
 copy

 在CODE上查看代码片派生到我的代码片

  1. d6008148 <ping>:  
  2. d6008148:   4803        ldr r0, [pc, #12]   ; (d6008158 <pong+0xc>)  
  3. d600814a:   4700        bx  r0  
  4.   
  5. d600814c <pong>:  
  6. d600814c:   e59f0008    ldr r0, [pc, #8]    ; d600815c <pong+0x10>  
  7. d6008150:   e12fff10    bx  r0  
  8.   
  9. d6008158:   d600814c    strle   r8, [r0], -ip, asr #2  
  10. d600815c:   d6008149    strle   r8, [r0], -r9, asr #2  


这样生成的代码正是我们所期待的。 0xd600815c 地址对应的指令 ( lsb=1 ) 已经被正确设置成 thumb 指令。

对于 C 函数, GNU Compiler 将会正确处理类似关系。

但是对于 arm assmebly 函数, 必须要通过 使用.thumb_func 符号或者其他被  as 工具定义的符号, 使得 as 工具知道这是一个thumb label.

参考

1. http://stackoverflow.com/questions/9368360/arm-thumb-using-bx-in-thumb-code-to-call-a-thumb-function-or-to-jump-to-a-thu

2. http://stuff.mit.edu/afs/sipb/project/egcs/src/egcs/gcc/config/arm/README-interworking

原帖:http://blog.csdn.net/cfy_phonex/article/details/18667299