對比理解adr,ldr指令

不少人在寫簡單的裸機代碼或分析uboot時,經常遇到adr ldr指令。卻分不清這2者的區別,今天就來談談adr與ldr指令。
 
 
參照韋老師的代碼和Makefile寫了test_adr.S
 
.text
.globl _start
_start:
    ldr r0, test
    adr r0, test
    ldr r0, =test
    nop
test:
nop
 
Makefile
 
all:test_adr.S
       arm-linux-gcc -c -o test_adr.o test_adr.S
         arm-linux-ld -Ttext 0x00000000 -gtest_adr.o -o test_adr_elf
        arm-linux-objcopy -O binary -S test_adr_elf test_adr.bin
        arm-linux-objdump -D -m arm test_adr_elf test_adr.dis
clean:
        rm -ftest_adr.dis test_adr.bin test_adr_elf *.o
 
反彙編test_adr.S獲得test_adr.dis
 
test_adr_elf:
file format elf32-littlearm
Disassembly of section .text:
00000000 _start:
0: e59f0008 ldr r0, [pc, #8]; 10 test
4: e28f0004 add r0, pc, #4; 0x4
8: e59f0004 ldr r0, [pc, #4]; 14.text+0x14
c: e1a00000 nop (mov r0,r0)
00000010 test:
10:e1a00000 nop (mov r0,r0)
14:00000010 andeq r0, r0, r0, lsl r0
 
很顯然,ldr獲取的是內存的值(至於這個內存存的是數據仍是地址,不是問題重點),像指針同樣間接尋址(看到了[]符號咯),而adr是獲得一個與PC有關的值,一定是個地址。
 
韋老師舉了個例子:
adr r0, _start,r0就是_start對應指令當前的地址
對於「_start對應指令當前的地址」,我理解了好久,終於想清楚,好比在uboot中,_start標號對應的指令(即b reset)的連接地址是0x33f80000確鑿無疑。
 
若是從NOR Flash啓動,b reset被燒在NOR Flash 0地址,那麼b reset相對於此時的PC來講,它的地址就是0。
 
若是u-boot被直接下載到SDRAM的0x33f80000處運行,那麼b reset天然處在SDRAM的0x33f80000。
 
所謂「當前」---是以運行時的PC爲參照。
 
下面基於以上理解,分析test_adr.dis
 
00000000 _start:
0: e59f0008 ldr r0, [pc, #8]; 10 test
4: e28f0004 add r0, pc, #4; 0x4
8: e59f0004 ldr r0, [pc, #4]; 14.text+0x14
c: e1a00000 nop (mov r0,r0)
 
00000010 test:
10:e1a00000 nop (mov r0,r0)
14:00000010 andeq r0, r0, r0, lsl r0
 
一、先分析第一條指令ldr r0,test被編譯成ldr
r0, [pc, #8],即到當前PC+8的存儲器取值,運行第一條指令時,PC其實已是8了(流水線決定的)。
 
那麼8+8等於0x10,因此r0等於e1a00000,此指令的做用就是讀取test地址處存放的值。因爲此處放了一條nop,即獲得nop的機器碼。
 
二、第二條adr r0,test被編譯成add r0, pc, #4
這顯然是依賴程序執行到此處的PC值。ADR是小範圍地址讀取僞指令,會將基於PC 相對偏移的地址值讀取到寄存器中,此指令在4地址,PC是4+8=0xc再加4,因而r0=0x10。

 
從結果上來看,test自身的值(標號值),被讀到了r0,這個值是以PC爲參考的,也就是test對應的指令(第二個nop)當前的地址。r0=(標號test的地址與此指令的距離差)+(此指令的地址)=((0x10-0x4=12)+(4))=16=0x10。
 
假如在0x30000000以上運行,r0=((12)+(0x30000004))= 0x30000010。
 
三、ldr r0,=test被編譯成兩個字,一個指令,一個文字池。執行到這裏PC=8, 8+8+4=0x14,因此在14地址取值,編譯器在14地址處放了0x00000010,0x00000010是test的值,假如在Makefile指定鏈接地址是0x30000000,那麼編譯器放在這裏的就是0x30000010,可見,這個值是編譯時肯定的。
 
最後一行andeq r0, r0, r0, lsl r0大概是編譯器的機械動做,把一個數字翻譯成了指令。
 
總結
ADR是小範圍的地址讀取僞指令,它將基於PC 相對偏移的地址值讀取到寄存器中。而ldr獲取的是內存的值,像指針同樣間接尋址。
相關文章
相關標籤/搜索