在学习ARM指令时,经常会对adr和ldr这两个命令产生疑惑,那他们究竟有什么区别呢?其实这两个都是伪指令:adr是小范围的地址读取伪指令,相于PC 寄存器或其它寄存器的小范围转移;ldr是大范围的读取地址伪指令,相当于PC 寄存器或其它寄存器的长转移。
看下面的例子:
AREA test, CODE, READONLY
ENTRY
start
ldr r0, _start
nop
nop
adr r0, _start
nop
nop
ldr r0, =_start
nop
nop
_start
nop
END
反汇编后的结果:
1 0x00000000: e59f001c .... LDR r0,[pc,#28] ; [0x24] = 0xe1a00000
2 0x00000004: e1a00000 .... MOV r0,r0
3 0x00000008: e1a00000 .... MOV r0,r0
4 0x0000000c: e28f0010 .... ADD r0,{pc}+0x18 ; 0x24
5 0x00000010: e1a00000 .... MOV r0,r0
6 0x00000014: e1a00000 .... MOV r0,r0
7 0x00000018: e59f0008 .... LDR r0,[pc,#8] ; [0x28] = 0x24
8 0x0000001c: e1a00000 .... MOV r0,r0
9 0x00000020: e1a00000 .... MOV r0,r0
_start
10 0x00000024: e1a00000 .... MOV r0,r0
$d
11 0x00000028: 00000024 $... DCD 36
第1行,寄存器间接寻址,获取 _start 的地址,此时,r0 = 0x00000024。
第4行,此时,r0 = 0x00000024。
第7行,取得标号 _start 的绝对地址,这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,一条是指令,另一条是 _start 的数据。因为在编译的时候不能确定 _start 的值,而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需要多出一个空间存放 _start 的真正数据,在这里就是 0x00000024。由此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x00000024。
原文来之:华清远见嵌入式学院