很多人在寫簡單的裸機**或分析uboot時,常常遇到adr ldr指令。卻分不清這2者的區別,今天就來談談adr與ldr指令。
先寫啟動**test_adr.s:
.text
.globl _start
_start:
ldr r0, test
adr r0, test
ldr r0,
=test
noptest:
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。
所謂「當前」—是以執行時的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獲取的是記憶體的值,像指標一樣間接定址。
ldr和adr的區別
同學們在學習arm指令時,多數都會對adr和ldr這兩個命令產生疑惑,那他們究竟有什麼區別呢?其實這兩個都是偽指令 adr是小範圍的位址讀取偽指令,ldr是大範圍的讀取位址偽指令。可實際上adr是將基於pc相對偏移的位址值或基於暫存器相對位址值讀取的為指令,而ldr用於載入32為立即數或乙個位址到指...
ARM彙編中ldr與adr的區別
ldr與adr的區別 nop編譯的時候設定 ro 為 0x0c008000 0c008000 start 0x14 c008000 e59f000c ldr r0,pc,12 c008014 start c008004 e28f0008 add r0,pc,8 0x8 c008008 e59f000...
ARM彙編中ldr與adr的區別
ldr與adr的區別 ldr r0,start adr r0,start ldr r0,start nopmov pc,lr start nop編譯的時候設定 ro 為 0x0c008000 0c008000 start 0x14 c008000 e59f000c ldr r0,pc,12 c008...