汇编中的重定位

6

我有一份裸机ARM引导程序的汇编代码,正在努力理解它的工作原理。二进制代码写在外部闪存中,在开机时将其部分内容复制到RAM中。尽管我已经阅读了维基百科条目,但我仍然没有完全理解此上下文中重定位的概念。在这里,RAM映射到低地址窗口,而闪存则映射到高地址窗口。有人可以解释一下为什么我们要在这里测试链接寄存器的值吗?

/* Test if we are running from an address, we are not linked at */
       bl check_position
 check_position:
        mov     r0, lr                  
        ldr     r1, =check_position
        cmp     r0, r1                  /* ; don't relocate during debug */
        beq     relocated_entry 

谢谢您提供的两个优秀答案!如果可以的话,我会都接受的,因为一个解释了代码的目标(JTAG程序加载器假设是正确的),而第二个则解释了它的工作原理。 - Étienne
2个回答

5
我的猜测是该应用程序从RAM运行,在调试该应用程序时,该作者可能使用某种引导加载程序或JTAG将测试应用程序直接加载到RAM中,因此没有必要复制和运行(这可能会导致崩溃)。
另一个这样做的原因是为了避免无限循环。例如,如果您想从闪存启动(通常必须如此),但又想从RAM执行,则最简单的方法就是将整个闪存或一些闪存块复制到RAM中,并跳转到RAM的起始处。当您这样做时,意味着您再次进入“将应用程序复制到RAM并跳转”的循环以避免第二次发生(这可能会使您崩溃)。因此,您需要某种“我是否从闪存运行此循环”测试。

4

有人能解释一下为什么我们要在这里测试链接寄存器的值吗?

bl check_position指令将PC+4的值存入链接寄存器,并将控制转移到check_position,也是相对于PC的。bl at ARM 到目前为止,所有的都是相对于PC的。

ldr r1,=check_position字面池中获取一个值。Ref1 实际的代码看起来像是这样的:

  ldr r1,[pc, #offset]
...
  offset:
    .long check_position   # absolute address from assemble/link. 

因此,R0 包含一个相对于 PC 的版本,而 R1 包含已组装的绝对版本。在这里,它们进行了比较。您也可以使用算术运算来计算差异,然后如果非零,则执行 分支; 或者可能将代码复制到其绝对位置。Ref2 如果代码在链接地址处 运行,则 R0R1 相同。这是用于 bl 的一些 伪代码

 mov lr,pc               ; pc is actually two instruction ahead.
 add pc,pc,#branch_offset-8

关键是BL基于PC执行所有操作,包括更新lr。除了使用这个“技巧”,我们还可以使用mov R0,PC,但PC向前8个字节。另一个选择是使用adr R0,check_position,这将让汇编器为我们执行所有地址计算。
 /* Test if we are running from an address, we are not linked at */
 check_position:
    adr    r0, check_position
    ldr    r1, =check_position
    cmp    r0, r1                  /* ; don't relocate during debug */
    beq    relocated_entry 

一个ARMv6版本可能是这样的,
 /* Test if we are running from an address, we are not linked at */
 check_position:
    adr    r0, check_position
    movw   r1, #:lower16:check_position
    movt   r1, #:upper16:check_position
    cmp    r0, r1                  /* ; don't relocate during debug */
    beq    relocated_entry 

在这两种情况下,代码更加简单明了,少用一个单词,不会覆盖lr寄存器,因此可以用于其他目的。
参考文献1:请参阅gnu汇编器手册中的Arm op-codes.ltorg
参考文献2:这正是Linux head.S在ARM上所做的事情。
编辑:我查看了ARM ARM,PC显然是当前指令+8,这说明为什么代码是这样的。我认为adr版本更直接、易读,但adr伪操作符并没有经常使用,所以人们可能不熟悉它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接