ここでリンク レジスタの値をテストする理由を説明できる人はいますか?
のbl check_position
値をPC+4
リンク レジスタに配置し、制御をcheck_position
PC 相対に転送します。ARM での bl これまでのところ、すべてが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
pseudo code
bl
mov lr,pc ; pc is actually two instruction ahead.
add pc,pc,#branch_offset-8
重要なのは、 の更新を含め、BL
に基づいてすべてを行うことです。このトリックを使用する代わりに、 を使用できますが、が 8 バイト先になります。もう 1 つの方法は、 を使用することです。これにより、アセンブラーにすべてのアドレス計算を実行させることができます。PC
lr
mov R0,PC
PC
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
どちらの場合も、コードはより単純で、1 ワード小さく、レジスタを上書きしないlr
ため、他の目的に使用できます。
Ref1: gnu-assembler マニュアルのArm op-codesと.ltorgを参照してください。
Ref2:これはまさに Linuxhead.S
が ARM に対して行っていることです。
編集: ARM ARM を確認しましたが、PC は明らかに現在の命令+8
であり、コードがこのようになった理由を示しています。adr
バージョンの方が直接的で読みやすいと思いますが、adr
疑似操作はそれほど頻繁に使用されないため、人々は慣れていない可能性があります。