结合U-boot源码中的重定位reloccate分析ARM汇编指令中ldr和adr指令区别

1、这两条指令经常用标号作为操作数,如adr r0,label和ldr r0,label。adr指令是位置相关码,把基于pc相对偏移的地址值读取到寄存器r0中,因此寄存器r0=标号label与该指令的距离差+该指令当前实际运行的地址,ldr指令中把label当成一个指针,把指针所指向的值读入到r0中。

举例:U-boot中有一段重定位代码,意在将Nor或者Nand中启动的代码拷贝到SDRAM中去执行,


.globl _start
         b       reset
         pc, _undefined_instruction
         pc, _software_interrupt
         pc, _prefetch_abort
         pc, _data_abort
         pc, _not_used
         pc, _irq
         pc, _fiq
_TEXT_BASE: /* *_TEXT_BASE=TEXT_BASE */
        .word TEXT_BASE
.globl _armboot_start
_armboot_start: /* *_armboot_start=_start */
        .word _start
relocate:/* relocate U-Boot to RAM     */
        adr r0, _start /* r0 <- current position of code,读取_start标号的地址,即程序实际开始运行的地址到r0,如果u-boot是烧写到nor或者nand中运行的话,那么程序从0地址开始运行,r0=0,如果u-boot是通过下载器下载到内存中运行的话,那么程序从内存TEXT_BASE(代码的链接地址,在board/$(BOARDDIR)/config.mk中指定,smdk2410开发板中TEXT_BASE=0x33f80000)开始运行,r0=0x33f80000。  */
        ldr r1, _TEXT_BASE /* test if we run from flash or RAM。读取_TEXT_BASE标号(指针)所指向的值到r1中,即r1=*_TEXT_BASE,由于第10行指定了_TEXT_BASE标号(指针)处存放的值为TEXT_BASE,因此r1=0x33f80000 */
        cmp r0, r1                  /* don't reloc during debug 。比较r0是否等于r1,如果相等,说明代码是从SDRAM中开始运行的,一般来说是通过下载器下载到内存中运行,那么就不需要进行clear_bas操作。反之则说明是从flash启动。     */
        beq clear_bss
        ldr r2, _armboot_start    /*  读取_armboot_start标号(指针)所指向的值到r2中,即r2=*_armboot_start,而由第13行可知*_armboot_start=_start ,再根据第2行可知_start=(代码开始运行的地址,0x00000000),,因此r2=(代码开始运行的地址,0x00000000).</span>*/
        ldr r3, _bss_start     /* 读取*_bss_start的值到r3中,而_bss_start在连接脚本(board/$(BOARDDIR)/U-Boot.lds)中指定,它代表着bss段的开始地址,它之前的数据为代码段和数据段,因此r3=(bss段之前的所有段的结束地址). </span>*/
        sub r2, r3, r2      /*  r2=r3-r2得到bss之前的所有段的长度 */
        add r2, r0, r2  /* r2=r2+r0,由于r0=(程序实际开始运行的地址),因此r2=(bss段开始的实际地址。后续代码是将r0开始的数据拷贝到r1开始的地址,直到r0==r2*/
copy_loop:
        ldmia r0!, {r3-r10}/* copy from source address [r0]    */
        stmia r1!, {r3-r10} /* copy to   target address [r1]    */
        cmp r0, r2 /* until source end addreee [r2]    */
        ble copy_loop、
loop:
        ldmia r0!, {r3-r10}/* copy from source address [r0]    */
        stmia r1!, {r3-r10} /* copy to   target address [r1]    */
        cmpr0, r2 /* until source end addreee [r2]    */
        ble copy_loop


2、注意还有ldr伪指令,其一般形式为ldr r0,=label,那么r0=(label应该位于的地址,而非实际地址),这里所谓应该位于的地址是跟链接地址有关的,属于位置无关码,如果链接地址为_TEXT_BASE,那么r0=(label相对于程序开头的偏移+_TEXT_BASE),这个值与程序是在SDRAM中运行还是在flash运行是无关的。

举例:U-boot中有一段初始化SDRAM的代码,lowlevel_init函数,代码如下:

lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
      ldr     r0, =SMRDATA
      ldr r1, _TEXT_BASE
      sub r0, r0, r1
      ldr r1, =BWSCON/* Bus Width Status Controller */
      add     r2, r0, #13*4
0:
      ldr     r3, [r0], #4
      str     r3, [r1], #4
      cmp     r2, r0<span style="white-space:pre">	</span>
      bne     0b
/* everything is fine now */
      mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA:   //13个寄存器的值
      .work ....

上面的代码中ldr  r0, =SMRDATA,将SMRDATA标号应该位于的地址赋给r0,r0=(SMRDATA相对于程序开头的偏移量+TEXT_BASE),然后通过sub r0,r0,r1指令使得r0=(SMRDATA相对于程序开头的偏移量),即r0保存了13个寄存器值在flash上的开始地址,最后把SMRDATA处的数据拷贝到BWSCON开始的内存处,进行SDRAM的初始化。这里为什么不用adr r0,SMRDATA,让r0=(SMRDATA标号后指令运行的实际地址)呢?原因在于SMRDATA后面保存的是数据,它们位于专门的数据段,而adr只能支持小范围的跳转,要跳转到数据段,需要ldr这种可大范围跳转的指令。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值