1. 程式人生 > >對比理解adr,ldr指令

對比理解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   1、先分析第一條指令ldr r0,test被編譯成ldr
r0, [pc, #8],即到當前PC+8的儲存器取值,執行第一條指令時,PC其實已經是8了(流水線決定的)。
  那麼8+8等於0x10,所以r0等於e1a00000,此指令的作用就是讀取test地址處存放的值。由於此處放了一條nop,即得到nop的機器碼。   2、第二條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。   3、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獲取的是記憶體的值,像指標一樣間接定址。