在移植 uboot 时,接触到一个概念叫做 位置无关码,那么与它对应的就是位置有关码。提到这两个概念就还得提一提链接地址、加载地址
链接地址,链接脚本里指定的,理论上程序运行时所处的地址。在编译时,编译器会根据链接地址来翻译位置有关码。
加载地址,程序运行时,实际所处的地址。
位置无关码,位置有关码,是相对于一条指令的正常目的来说的。比如 ldr r0 ,=标号,它的正常目的是取得标号处的地址,对于这个目的,它是位置有关码,运行的地址不对就获取不到正确的标号地址,其实它无论在哪都是获取的程序加载地址等于链接地址时,标号的地址,如果你就是想要这个值,那么用这条指令是非常正确的,就不用理会什么位置无关码,位置有关码的概念了,这一点非常重要。
因此,当加载地址不等于链接地址时,并不是不可以用位置无关码,而是要看你用位置无关码是否达到了你想要的目的。
位置无关码,依赖于程序当前运行的PC值,进行相对的跳转,导致的结果就是,无论代码在哪,总能达到指令的正常目的,因此是位置无关的。
位置有关码,不依赖当前PC值,是绝对跳转,只有程序运行在链接地址处时,才能达到指令的正常目的,因此是位置有关系的。
下面,我们来看常用的汇编指令以及C语言中哪些操作是位置有关码,哪些是位置无关码。
如果加载地址为 0 ,那么代码将按照下面的顺序排放
如果加载地址为0x33f80000 则按照下边的顺序排放
一、B BL指令
- bl close_watch_dog
33f80000:eb000006bl33f80020
b 是相对跳转:PC + 偏移值 (PC值等于当前地址+8)
偏移值:机器码 0xeb000006 低 24位 0x000006 按符号为扩展为 32 位 0x00000006 正数,向后跳转 0x6 个 4字节 也就是 0x1c
- bl _start
33f80004:ebfffffdbl33f80000
偏移值:机器码 0xebfffffd 低 24位 fffffd 按符号位扩展为 32 位 0xfffffffd 负数(-3),向前跳转 0x3 个 4字节 也就是 0xc
通过以上分析,我们知道B是相对跳转,位置无关码,也可以知道为什么32为arm指令集,B的范围为正负32M了,24位去掉1位符号位,恰好等于32M。
二、ADR
- adr r0, close_watch_dog /* 获取标号处的地址,位置无关 */
33f80008:e28f0010addr0, pc, #16
adr 获取的是标号处的“实际”地址,标号在哪就是哪个地址,跟位置无关,总能获得想要的值。
三、LDR
- ldr r0, SMRDATA /* 获取标号处的值,位置无关 */
33f8000c:e59f0018ldrr0, [pc, #24]; 33f8002c
- ldr r0, =0x12345678 /* 常数赋值,位置无关*/
33f80010:e59f0018ldrr0, [pc, #24]; 33f80030
- ldr r0, =SMRDATA /* 获取标号地址,位置有关 */
33f80014:e59f0018ldrr0, [pc, #24]; 33f80034
- ldr r0, =main/* 获取函数名的地址,位置有关 */
- ldr r0 ,=__bss_start /* 获取链接脚本里标号的地址,位置有关 */
这俩和ldr r0, =SMRDATA 一致,位置有关,在0地址处运行不正确。
四、C函数
1、全局变量
r3 为全局变量 a 的地址,a 是存放在 0起始的地址还是0x33f80000起始的地址,它都认为 a 的地址是 0x33f800a0 。因此,C函数中调用全局变量是位置有关码。
2、函数调用
33f80084:ebffffedbl33f80040
由于 main 函数和 abc 函数挨得比较近,在32M范围之内,因此被翻译成了一条 bl 指令,那么与位置无关。
如果,调用的函数比较远,大于32M的话,我认为是与位置有关系的,这个不再验证了。
3、局部变量
局部变量在函数刚开始的地方被压入栈,赋值语句被翻译成:
33f8007c:e3a03001movr3, #1
33f80080: e50b3008 str r3, [fp, #-8]
位置无关。
评论
查看更多