在学习ARM汇编指令的时候,经常会使用到ldr与adr两条指令,相信大部分初学的人曾经都对这两个命令产生过疑惑。
其实这两条指令都是伪指令:
- ldr指令是大范围的地址读取伪指令,相对于PC寄存器或其他寄存器的大范围跳转;
- adr指令是小范围的地址读取伪指令,相对于PC寄存器或其他寄存器的小范围跳转。
要想清楚的说明这两条指令的区别,还是得从实际的例子中来解释,空谈泛泛,难以实际掌握。
ldr r0, _start
nop
nop
adr r0, _start
nop
nop
ldr r0, =_start
nop
nop
_start:
nop
反汇编后的结果如下:
1 0x00000000: e59f001c ... LDR r0, [pc, #28]; [0x24]=e1a00000
2 0x00000004: e1a00000 ... MOV r0, r0
3 0x00000008: e1a00000 ... MOV r0, r0
4 0x0000000c: e28f0010 ... ADD r0, pc, 0x10; r0=x00000024
5 0x00000010: e1a00000 ... MOV r0, r0
6 0x00000014: e1a00000 ... MOV r0, r0
7 0x00000018: e59f0008 ... LDR r0, [pc, #8]; [0x28]=00000024
8 0x0000001c: e1a00000 ... MOV r0, r0
9 0x00000020: e1a00000 ... MOV r0, r0
_start:
10 0x00000024: e1a00000 ... MOV r0, r0
$d 存放_start地址值
11 0x00000028: 00000024 ... DCD 36
第一行,寄存器间接寻址,获取_start的地址,此时,r0=0xe1a00000.
第四行,此时,r0=0x00000024.
第七行,取得标号_start的绝对地址,这个绝对地址是在link的时候确定的,看上去这只是一条指令,但是它要占用2个32bit的空间,一条是指令,另一条是_start的数据。因为在编译的时候不能够确定_start的值,而且也不能够用mov指令来给r0赋一个32bit的常量,所以需要多出一个空间来存放_start的真正数据,在这里就是0x00000024,由此可以看出,这个是绝对寻址,不管代码在什么地方运行,它的结果都是r0=0x00000024.