在學習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.