ARM指令ldr和adr的區別

2021-10-11 22:21:53 字數 2651 閱讀 4551

很多人在寫簡單的裸機**或分析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...