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