美国加州伯克利分校:关于adr与ldr伪指令的疑惑

来源:百度文库 编辑:偶看新闻 时间:2024/05/03 11:40:37

这段时间在学习Uboot,看到start.S中有这么一句:
relocate:
             adr  r0,_start
             ldr   r2,_TEXT_BASE
... ...
      事先把adr与ldr的伪命令用法粘上:

       ADR:
              ADR 可将一个立即值与 pc 值相加,并将结果写入目标寄存器。
   语法
       ADR{cond}{.W} Rd,label
     其中:
      cond       是一个可选的条件代码
      .W           是可选的指令宽度说明符。
      Rd           是要加载的寄存器。
      label       是一个程序相对的表达式。label 必须在当前指令的一定范围之内。
   用法
      ADR 可生成与位置无关的代码,因为地址相对于程序或寄存器。

   LDR (相对 pc)
                  加载寄存器。  地址为 pc 加偏移量。
   语法
      LDR{type}{cond}{.W} Rt, label
            LDRD{cond} Rt, Rt2, label        ; Doubleword
      其中:
      type 可以是下列项之一:
      B           无符号字节(加载时零扩展为 32 位。)
      SB        有符号字节(仅 LDR。 符号扩展为 32 位。)
      H           无符号半字(加载时零扩展为 32 位。)
      SH        有符号半字(仅 LDR。 符号扩展为 32 位。)
      -            如果是字,则省略。
      cond     是一个可选的条件代码 。
      .W          是可选的指令宽度说明符。
      Rt          是要加载或存储的寄存器。
      Rt2        是第二个要加载或存储的寄存器。
      label      是一个程序相对的表达式。
      label 必须在当前指令的一定范围之内。
  
LDR 伪指令
            使用以下项之一加载寄存器:  
            ? 一个 32 位常数值
            ? 一个地址
语法
          LDR{cond}{.W} Rt, =expr
          LDR{cond}{.W} Rt, =label_expr
  其中:
          cond        是一个可选的条件代码
          .W            是可选的指令宽度说明符。
          Rt             是要加载的寄存器。
          expr         取值为一个数值常数:
             ? 如果 expr 的值位于范围内,则汇编器将会生成一个 MOV 或 MVN  指令。
             ? 如果 expr 的值不在 MOV 或 MVN 指令的范围内,则汇编器会将常数放入文字池中,并会生成一个相对于程序的 LDR 指令,该 指令可从文字池中读取此常数。
          label_expr   是地址的程序相对表达式或外部表达式,形式为加上或减去一个数值常数的标签。汇编器将label_expr 的值放入文字池中,并会生成一个相对于程序的 LDR 指令,该指令可从文字池中加载该值。
             ? 如果 label_expr 是一个外部表达式,或未包含在当前代码段内,则汇编器会在对象文件中放入一个链接器重新定位指令。 链接 器将在链接是生成该地址。
           ? 如果 label_expr 是一个局部标签,则汇编器会在对象文件中放入一个链接器重新定位指令,并会为该局部标签生成一个符号。该地址将在链接时生成。 如果局部标签引用了 Thumb 代码,则还会设置该地址的 Thumb 位(位 0)。

 用法
          LDR 伪指令的主要功能有:
             ? 当立即数由于超出了 MOV 和 MVN 指令的范围,而不能被移入寄存器中时,生成文字常数。
             ? 将相对于程序的地址或外部地址载入寄存器中。 无论链接器将包含 LDR 的ELF 代码段置于何处,该地址始终有效。

      我是这么理解的:
   _start是程序标号,_TEXT_BASE是在config.mk定义的变量,TEXT_BASE=0X33F80000,也就是说内存中或者存储设备中TEXT_BASE所对应地址的值为0X33F80000。那么第一句 adr r0,_start意味着,在该程序运行中,把程序标号_start所对应程序语句的运行地址(地址值,非地址中的内容)加载到r0寄存器中。第二句 ldr  r2,_TEXT_BASE的意思是说,把变量TEXT_BASE对应地址中的数值(也就是0X33F80000)加载到r2寄存器中。
    还有一点,为了实现同样的功能,我觉得第一句也能用ldr  r0,=_start实现。
    请大伙指正。

ldr与adr的区别- []
Tag:嵌入式
        ldr     r0, _start
        adr     r0, _start
        ldr     r0, =_start
        nop
        mov     pc, lr
_start:
        nop
        
编译的时候设置 RO 为 0x0c008000

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

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

分析:

ldr     r0, _start

从内存地址 _start 的地方把值读入。执行这个后,r0 = 0xe1a00000

adr     r0, _start

取得 _start 的地址到 r0,但是请看反编译的结果,它是与位置无关的。其实取得的时相对的位置。例如这段代码在 0x0c008000 运行,那么 adr r0, _start 得到 r0 = 0x0c008014;如果在地址 0 运行,就是 0x00000014 了。

ldr     r0, =_start

这个取得标号 _start 的绝对地址。这个绝对地址是在 link 的时候确定的。看上去这只是一个指令,但是它要占用 2 个 32bit 的空间,一条是指令,另一条是 _start 的数据(因为在编译的时候不能确定 _start 的值,而且也不能用 mov 指令来给 r0 赋一个 32bit 的常量,所以需要多出一个空间存放 _start 的真正数据,在这里就是 0x0c008014)。
因此可以看出,这个是绝对的寻址,不管这段代码在什么地方运行,它的结果都是 r0 = 0x0c008014