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