首先说明
BL main 为相对跳转指令,与位置无关代码。
LDR PC,=main 为绝对跳转指令,与位置有关代码。
可能看到这的人都有疑问,如下图。链接地址为0x50000000。
两段代码main函数位置如下图:
左右图的第11行 左边是 bl main的反汇编为 “ bl 50000298 ”,右边是 ldr pc,=main的反汇编为 " ldr pc,[pc, #4] "
乍一看第一个 bl 50000298 不是直接跳转到 50000298么,那不就是绝对跳转? ,第二个使用 ldr pc,[pc, #4],使用的时pc偏移量不是相对跳转么?
那你就中记了。
arm-linux-objdump的反汇编太贴心了。
左边图:
反汇编软件在反汇编的时候把你的bl mian这条语句中的main的地址帮你计算了出来。但是,,它并不是按照你的实际运行地址计算的而是按照你的链接地址来的,
所以就出现了bl 50000298;正如左边图示的main函数位置。
右边图:
ldr pc, =main这条指令为伪指令,编译的时候会将main的链接地址存入一个地址。再将 ldr pc, =main转化为 ldr pc, [ pc, offset ]这样一个指令。所以上面的反汇编出来的 ldr pc, =main 就变成了 ldr pc. [ pc, #4 ],这句代码相当于 pc = *(pc+4); 由于ARM使用了流水线的原因,所以在执行 ldr pc. [ pc, #4 ]的时候 pc 不在这句代码这里了,而是跑到了 pc+8的地方,而 pc = *(pc + 4) = 5000029c 注意!!!!!!!! 这里的 5000029c 是存在代码段中的一个常量,并不是计算出来的,不会随程序的位置而改变,所以无论代码和pc怎么变 *(pc+4) 的值时不会变的。
到这还是不明白为什么bl main是相对跳转 ,ldr pc, =main是绝对跳转,反而是两个指令都是绝对跳转了。
上面说了,反汇编软件在反汇编BL main时帮我们把main的地址算出来了,,那我们看看它时怎么算的,,,,,,费了好大力气终于找到了BL跳转指令的计算方法(大神见笑了),看图:(该图取至 《ARM体系结构与编程》--- 杜春雷 Page 59)
此时我们来模拟一下反汇编软件的计算过程:(为方便理解我们假设程序在 50000000处执行)
看一下左图 50000010 处的机器码为 eb0000a0 转化成二进制就是 1110 1011 0000 0000 0000 0000 1010 0000
最高4位时条件码 1110 就是无条件执行 (《ARM体系结构与编程》 Page 23)。
接着的4位 1011 是BL指令的指令码。
后面的24位0000 0000 0000 0000 1010 0000 即为地址码
按照其计算方式:
1、将指令中24位带符号的补码立即数扩展为32位(扩展其符号位)
原数变成 0000 0000 0000 0000 0000 0000 1010 0000
2、将此数左移两位
变成 0000 0000 0000 0000 0000 0010 1000 0000 = 0x280
3、将得到的值加到PC寄存器中得到目标地址
由于ARM流水线原因 此时的 pc = 0x50000010+8 = 0x50000018
pc = pc + 0x280 = 0x50000298与左边图中的mian的地址相等。程序就跳转到了mian
在算的过程中我们使用的始终是PC的值,假设程序在 0 地址处执行,那么计算方法一样,pc 的值变了计算出来的结果也随之改变。所以 BL 的跳转时与位置无关的