ARM汇编概念(1):
什么是链接地址、运行地址、存储地址?
什么是位置无关码、位置有关码?
ldr和adr的理解?
参考来源:http://blog.sina.com.cn/s/blog_93339c560102wjqm.html
链接地址:
在链接脚本里指定内存地址,编译时指定的代码运行时应该所处的内存地址。它是绝对地址(编译后的定值)。
运行地址:
当前代码运行时实际所处的地址。它是相对地址(相对于当前程序开始运行时的内存地址)。
存储地址:
代码烧写到ROM中的地址,比如NANDFLASH。
链接地址和运行地址的关系:
正常情况下 链接地址 = 运行地址,当运行地址等于链接地址时,程序肯定能运行,因为我们指定链接地址的初衷就是为了让代码能在我们指定的位置中运行。那么当运行地址!=链接地址 时,程序还能跑么?。。。(请继续阅读下面寻找答案)
学习ARM裸机或者uboot的时候时常被一个问题困惑,就是编译出来的文件经过反汇编出来后看到的链接地址和烧写到RAM中的地址不一致时,程序依然诡异的跑起来了,难道程序有bug?
答案显然不是,而是因为当前代码是位置无关码,所以将其加载到RAM的任意地址执行都是可以的。
话说回来,什么是位置无关码?
从本质上讲,位置无关码就是代码中跳转或者取值、赋值的目标地址都使用相对地址,位置有关码使用的是绝对地址。。。
那么问题来了,什么代码是使用相对寻址,什么样的代码是用绝对寻址呢?
可以根据汇编命令上看出来:
位置无关码: B、BL、MOV都是位置无关码
位置有关码: LDR PC, = LABEL等类似的代码
(1)B
B指令接受一个相对地址,因此在汇编里用B跳转到一个标号时,实际编译的结果是一个相对跳转。
相对地址有个范围限制,即目标不能太远,一般目标放在同一个文件里是肯定可以的。
(2) BL
BL用于调用函数,也是一个相对跳转
(3) ADR
获取标号的地址,在编译时会使用 “PC+偏移” 的方式得到该位置的地址。(相对寻址)
例如,当TEXT_BASE是0时,SMRDATA可能被放在0x100的位置,当TEXT_BASE为0x30000000时,放在0x30000100的位置。使用ADR总能获取正确的位置,与程序的加载地址无关。
(4) LDR
当加标号时,LDR可以用于伪指令,也可以真指令。
真指令: (标号前不加=号加上#,表示取标号处的值)
LDR R0, SDRDATA
理解:将地址为SDRDATA的内存内容(机器码)加载到寄存器R0中
实际被编译为:LDR R0, [PC, #NN],其中NN是目标的相对距离
伪指令: (标号前加=号,取标号的地址)
LDR R0, = SDRDATA
理解:将SDRDATA加载到寄存器R0中
实际被编译为:
显然,用LDR时,加不加=号有很大区别。
无=号:取该标号处的值,位置无关
有=号:取该标号的地址,位置相关
下面举一个例子分析一下 LDR 和 ADR命令在代码中区别:
-
此处要注意ldr与adr的区别,看下面的代码片段:
-
ldr r0, _start
-
adr r0, _start
-
ldr r0, =_start
-
nop
-
mov pc, lr
-
_start:
-
nop
-
下面是反汇编的结果:
链接地址 | 机器码 | 汇编指令 -
0c008000 <_start-0x14>:
-
c008000: e59f000c ldr r0, [pc, #12] ; c008014 <_start>
-
c008004: e28f0008 add r0, pc, #8 ; 0x8
-
c008008: e59f0008 ldr r0, [pc, #8] ; c008018 <_start+0x4>
-
c00800c: e1a00000 nop (mov r0,r0)
-
c008010: e1a0f00e mov pc, lr
-
0c008014 <_start>:
-
c008014: e1a00000 nop (mov r0,r0)
-
分析:
-
ldr r0, _start
从内存地址 _start(c008014) 的地方把c008014地址对应的值读入(加载)到r0中。执行这个后,r0 = 0xe1a00000
取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的是相对的位置。例如这段代码在 0x0c008000 运行,那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。即当前PC值加上_start的偏移量。
那么问题又来了,PC=?,偏移量=?
汇编指令中,PC=当前命令的地址+8(因为arm中的指令处理是按3级程序流进行的,具体详解请查看https://blog.csdn.net/abclixu123/article/details/7471822);
从反汇编文件中可以看出,“c008004: e28f0008 add r0, pc, #8 ; 0x8”该偏移量为8,注意这个8是十进制的8;我们带入算一下是否正确,当执行到这一句时 pc= 0xc008004 +8(程序流中的要求) = 0xc00800c;那么 pc + 8 (偏移量)=0xc0080014,刚好是我们的_start函数所在的地址处