arm汇编指令:ldr和adr的区别

在分析uboot源码时,遇到adr、ldr指令,却分不清这2者的区别,网上很多解释含糊不清,现对adr与ldr指令做解释。

首先给出一段汇编代码:

ldr     r0, _start
        adr     r0, _start
        ldr     r0, =_start
        nop
        mov     pc, lr
_start:
        nop

设置代码的起始地址为 0x0c008000,用 ADS1.2编译后的反汇编如下所示:

0c008000 <_start-0x14>:
c008000:       e59f000c        ldr     r0, [pc, #12]   ; c008014 <_start>
c008004:       e28f0008        add     r0, pc, #8      ; 0x8
c008008:       e59f0008        ldr     r0, [pc, #8]    ; c008018 <_start+0x4>
c00800c:       e1a00000        nop                     (mov r0,r0)
c008010:       e1a0f00e        mov     pc, lr

0c008014 <_start>:
c008014:       e1a00000        nop                     (mov r0,r0)
c008018:       0c008014        stceq   0, cr8, [r0], -#80

分析: 
1、ldr     r0, _start;
这里ldr是一个指令,把内存地址 _start(0c008014)对应的值(可能是数据或指令对应的机器码)读入。执行这个后,r0 = 0xe1a00000。

2、adr     r0, _start;小范围的地址读取伪指令
adr是一个伪指令,通过指令“add  r0, pc, #8 ”把_start标号对应的地址加载到 r0,因为地址是相对程序的,因此adr产生依赖于位置的代码。这里这段代码在 0x0c008000 运行,那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,那么r0 = 0x00000014。

通过这条伪指令就可以得知当前程序在内存的什么位置运行。这是一条伪指令,但是功能却如此强大,需要相应的编译器功能才能实现。

3、ldr     r0, =_start;大范围的地址读取
这里ldr是一个伪指令,用于加载32位的立即数或一个地址值到指定寄存器。这条指令取得的是标号_start 的绝对地址。这个绝对地址是在编译器编译完后进行 link(链接)的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,一条是指令,另一条是 _start 的数据(因为在编译阶段不能确定 _start 的值(要等到link时才确定),而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需要多出一个空间存放 _start标号对应的地址数据,这里就是 0x0c008014,r0=0x0c008014)。

参考:

https://www.cnblogs.com/amanlikethis/p/3419710.html 

https://www.cnblogs.com/jack739x/articles/2060762.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章