天天看點

ARM ldr與adr指令對比

adr指令與ldr指令,都是加載,而他們的差別在于:      adr指令加載符号時加載運作位址(相對位置),編譯器編譯後改成add指令或sub指令      ldr指令加載符号時加載連結位址(絕對位置),編譯器編譯後仍是ldr指令(原來的ldr是僞指令)。

實際測試以下程式: .global _start _start:

    adr r0, _start     ldr r0, _start     ldr r0, =_start

    b .              @結尾死循環

反彙編結果如下: 00000000 <_start>:    0:    e24f0008     sub    r0, pc, #8    4:    e51f000c     ldr    r0, [pc, #-12]    ; 0 <_start>    8:    e59f0000     ldr    r0, [pc, #0]    ; 10 <_start+0x10>    c:    eafffffe     b    c <_start+0xc>   10:    00000000     andeq    r0, r0, r0 首先看 adr r0, _start    0:    e24f0008     sub    r0, pc, #8 由于arm有兩級流水線,是以目前pc指令指向0x8,是以r0就是0x0。 ldr r0, _start    4:    e51f000c     ldr    r0, [pc, #-12]    ; 0 <_start> 可以看出r0的值也指向0x0. ldr r0, =_start    8:    e59f0000     ldr    r0, [pc, #0]    ; 10 <_start+0x10>

此時注意到r0的值指向了0x10,而0x10位址記憶體放的正是00000000, _start标号。

從上面的實際測試看出, adr r0, _start     ;這條指令中adr被轉化為了add或sub指令,是根據pc目前指向的位置來擷取_start标号的位置,是基于pc的偏移量,是以取得的是相對位置 ldr r0, _start     ;這條指令也是根據pc目前指向位置來擷取_start标号的位置,與adr不同的是使用了ldr指令 ldr r0, _start     ;這條指令是用文字池的方式來得到一個位址,而位址裡放的内容才是_start标号的位址,取得的是絕對位置

查閱其他資料時得知,adr主要是用作短加載,因轉化為add/sub的形式,是以其尋址空間較小。adr的尋址空間隻有前後4kb,而且必須在同一個代碼段中,隻能用于短加載。ldr沒有這些要求,ldr指令取得的是絕對位址,可用來長加載或長跳轉。

綜上:ldr和adr的差別主要就是位址的位置無關性的差别。

補充:指令adrl adrl //轉化成兩個ADD,尋址空間是8KB