Assembly 装配中的重新定位

Assembly 装配中的重新定位,assembly,arm,bare-metal,Assembly,Arm,Bare Metal,我有一个用汇编语言编写的裸机手臂的启动代码,我试图理解它是如何工作的。二进制文件写入一些外部闪存中,并在启动时在RAM中复制自身的一部分。尽管我读了这篇文章,但在这种背景下,我仍然没有完全理解搬迁的概念。RAM映射到低地址窗口,闪存映射到高地址窗口。有人能解释一下为什么我们在这里测试链接寄存器的值吗 /* Test if we are running from an address, we are not linked at */ bl check_position check_

我有一个用汇编语言编写的裸机手臂的启动代码,我试图理解它是如何工作的。二进制文件写入一些外部闪存中,并在启动时在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 

我猜应用程序是从ram运行的,在调试应用程序时,作者可能正在使用某种引导加载程序和/或jtag将测试应用程序直接加载到ram中,因此没有理由复制和运行(这可能会导致崩溃)

您这样做的另一个原因是为了避免无限循环。例如,如果您想从闪存启动(通常必须),但要从ram执行,最简单的方法就是将整个闪存或整个闪存块复制到ram,然后分支到ram的开头。当你这样做的时候,意味着你再次点击“将应用程序复制到ram和分支”循环,为了避免第二次出现(这可能会使你崩溃),你有某种我是否从flash运行这个循环的测试

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

/* 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 
bl check_位置
将把
PC+4
的值放在链接寄存器中,并将控制权转移到
check_位置
也与PC相关。到目前为止,一切都是相对的

ldr r1,=check_position
文本池中获取一个值

  ldr r1,[pc, #offset]
...
  offset:
    .long check_position   # absolute address from assemble/link. 
因此,
R0
包含PC相对版本,
R1
包含绝对组装版本。这里,对它们进行了比较。您还可以使用算术来计算差异,如果不为零,则进行分支;或者可能将代码复制到其绝对目的地。参考2如果代码在链接地址运行,则
R0
R1
是相同的。这是
bl
的一些
伪代码

 mov lr,pc               ; pc is actually two instruction ahead.
 add pc,pc,#branch_offset-8
关键是
BL
基于
PC
执行所有操作,包括更新
lr
。我们可以使用
movr0,PC
,而不是使用这个技巧,除非
PC
前面有8个字节。另一种选择是使用adrr0,检查位置,这将使汇编程序为我们做所有的地址计算

 /* 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 
参考1:参见gnu汇编程序手册中的和
Ref2:这正是Linux
head.S
为ARM所做的


编辑:我检查了ARM,PC显然是当前指令
+8
,这说明了为什么代码是这样的。我认为
adr
版本更直观易读,但是
adr
伪op没有经常使用,因此人们可能不熟悉它。

感谢您提供了两个非常好的答案!如果可以的话,我会接受这两种方法,因为其中一种解释了代码的目标(JTAG程序加载器假设是正确的),第二种解释了它是如何工作的。对于ARMV6+您可以使用
movw r1,#:lower16:check_position
movt r1,#:upper 16:check_position
而不是
ldr r1,=check_position。它可能会跑得稍微快一点。