我有一个用汇编写的裸机ARM的启动代码,我试图了解它是如何工作的。二进制文件写在一些外部Flash中,并在启动时将其自身的一部分复制到RAM中。在这种情况下,我仍然没有完全得到重定位的概念,即使我读过wikipedia entry。 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
答案 0 :(得分:5)
我的猜测是应用程序从ram运行,并且在调试应用程序时,这个作者可能正在使用某种bootloader和/或jtag将测试应用程序直接加载到ram中,因此没有理由复制和运行(这可能会导致崩溃)。
你做这样的事情的另一个原因是避免无限循环。例如,如果您想从闪存(通常必须)启动但是从ram执行,最简单的方法是将整个闪存或整个闪存的一部分复制到ram并且只分支到ram的开头。当你这样做意味着你再次点击“将应用程序复制到ram and branch”循环,以避免它第二次(这可能会让你崩溃),你有一些我从flash运行这个循环或不测试。
答案 1 :(得分:3)
有人可以向我解释为什么我们在这里测试链接寄存器的值吗?
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 如果代码在链接地址正在运行,那么R0
和{ {1}}是一样的。这是R1
的一些pseudo code
。
bl
关键是 mov lr,pc ; pc is actually two instruction ahead.
add pc,pc,#branch_offset-8
根据BL
执行所有操作,包括PC
的更新。我们可以使用lr
,而不是使用此技巧,除了mov R0,PC
前面是8个字节。另一种方法是使用PC
,这将使汇编程序为我们完成所有地址数学运算。
adr R0,check_position
参考1:请参阅gnu-assembler手册中的Arm op-codes和.ltorg
参考2:这正是Linux /* 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
正在为ARM做的事情。
编辑:我检查了ARM ARM,而PC显然是当前的指令head.S
,这说明了为什么代码是这样的。我认为+8
版本更直接,更易读,但adr
伪操作不经常使用,所以人们可能不熟悉它。