相对跳转指令和绝对跳转指令的特殊用法
标签:
编译地址运行地址相对跳转绝对跳转it |
分类: 经验案例 |
接上一篇“从ARM汇编指令机器码解释一些问题”
当程序的编译地址与运行地址不一致时,使用相对跳转指令和绝对跳转指令就需要注意了,本文档将讲述其中原因。
另有一种B指令是BX,它的用法是
BX
其中Rm是通用寄存器,例如BX
http://s8/middle/908da746gb98ebfa32a57&690
可以看到它最后的4位所存储的就是Rm寄存器的编号,共有2^4=16种,对应R0~R15。R寄存器可以存放32bits的数据,可以表示全空间地址范围,因此BX指令是一种绝对跳转指令,并且可以跳转到32位机的全部地址空间。
BL是相对跳转指令,BX是绝对跳转指令,它们虽然都是跳转指令,实现跳转功能,除了可跳转的距离不一样之外,在某些场合还是有限制的。比如linux内核程序编译的基址是0xC0000000,内核程序认为它们都是在0xC0000000以上空间运行的,而内核在进入页机制之前都是运行在以0x00000000为基址的内存空间,编译空间与运行空间不一致这样不会有问题么?答案当然是不会有问题,否则linux怎么会跑起来,这其中就涉及到程序运行的相对寻址和绝对寻址的问题了。
我们知道程序分为顺序执行和跳转执行,顺序执行就是一条指令执行完再去执行它下面的一条指令,而跳转又分为相对跳转和绝对跳转,正如上述的BL和BX。顺序执行和相对跳转执行都是在当前指令的基础上加上相对的偏移量找到下条指令的,而绝对跳转则是需要找到实实在在的地址。
我们先来看一个例子,然后再说明上述情况是怎么实现的。
比如说我坐火车从沈阳到大连,我在2号车厢,餐车在7号车厢,
u
u
u
上面这3种情况都是使用了相对地址,无需知道火车所处的绝对地址。
其中第3种情况就可以解释编译地址与运行地址不一致的问题。
l
l
l
只要将被编译到0xC0000000地址的代码放到0x00000000地址开始执行,如果它们只使用顺序执行或者相对跳转执行方式就可以正常运行,但如果使用了绝对寻址,那么程序就跑飞了。
我们参照下面这段伪代码来说明这个情况。
|
指令编号 |
指令功能 |
|
指令1: |
顺序执行 |
|
指令2: |
顺序执行 |
|
指令3: |
相对跳转到指令5 |
|
指令4: |
顺序执行 |
|
指令5: |
顺序执行 |
|
指令6: |
绝对跳转到指令8 |
|
指令7: |
顺序执行 |
|
指令8: |
顺序执行 |
在编译、链接的时候,这段程序被告知放在0xC0000000地址空间,编译结果为(每条指令以4字节计算):
|
指令地址 |
指令编号 |
指令功能 |
下条指令地址 |
|
0x00000000 |
指令1: |
顺序执行 |
当前地址+4 |
|
0x00000004 |
指令2: |
顺序执行 |
当前地址+4 |
|
0x00000008 |
指令3: |
相对跳转到指令5 |
当前地址+8 |
|
0x0000000C |
指令4: |
顺序执行 |
当前地址+4 |
|
0x00000010 |
指令5: |
顺序执行 |
当前地址+4 |
|
0x00000014 |
指令6: |
绝对跳转到指令8 |
0xC000001C |
|
0x00000018 |
指令7: |
顺序执行 |
当前地址+4 |
|
0x0000001C |
指令8: |
顺序执行 |
当前地址+4 |
当这段程序被放在0xC0000000空间时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,在指令6执行时也可以使用绝对寻址的方法从0xC0000014正确跳转到指令8所在的0xC000001C位置,这段代码运行正常。
当这段代码被放在0x00000000空间时,开始执行指令1,然后采用相对寻址的方法就可以运行到指令6,但在指令6执行时使用绝对寻址的方法从0x00000014跳转到了0xC000001C,但0xC000001C空间没有代码,这样程序就跑飞了。
http://s14/middle/908da746gb98ec5aafa8d&690
因此我们在看linux内核启动这部分的代码时只能看到顺序执行指令和相对跳转指令,不会存在绝对跳转指令,就是这个原因。如果需要使用全局变量或者函数指针时,则需要将这个地址减去0xC0000000的偏移量才可以获取到此时运行的地址,因为全局变量和函数指针在编译时都是按照0xC0000000基址编译出的绝对地址,运行时既然将程序段和数据段都偏移了0xC0000000的距离,那么使用时只要减去这个值就可以找到正确的位置了。
当linux内核完成初始化时,就会开启页机制,0x00000000地址空间就会转换为0xC0000000,再使用一个绝对跳转指令跳转到0xC0000000去执行,在此之后程序就运行在了编译时指定的0xC0000000地址空间,无论是采用相对寻址还是绝对寻址都不会有问题了。

加载中…