PC相对偏移量的4KB页面地址。
将PC相关地址加载到寄存器中。它类似于ADR 指令。 ADRL可以加载比ADR更广泛的地址,因为 它生成两个数据处理指令。
具体地,
ADRL汇编为两条指令,ADRP后跟ADD。如果 汇编程序无法在两条指令中构造地址 生成搬迁。然后链接器生成正确的偏移量。 ADRL生成与位置无关的代码,因为地址是 相对于PC计算。
ADRP
和ADRL
说明有何作用?更重要的是,ADRP
后跟ADD
如何构建与PC相关的地址?
答案 0 :(得分:5)
ADR
根本问题是所有ARMv7 / ARMv8指令的长度均为4个字节。
这简化了很多事情,但是有一个不幸的含义:由于我们需要一些位来对指令本身进行编码,因此您无法在单个指令中对完整地址(4/8字节)进行编码。
这是ADR指令进入的地方。即使我们无法存储完整的地址,我们也可以通过PC的相对地址来引用其中的一些地址(适合编码的地址),这通常足以满足许多应用程序的需要。
ADR指令使用21位立即数作为偏移量,这允许+ -1MiB跳转(20位+ 1表示符号)。
这里的原理类似于ldr =
伪指令:Why use LDR over MOV (or vice versa) in ARM assembly?
有时ARMv7 DDI 0406C.d manual D9.4“ ARM指令中PC的明确使用”中所述,使用PC的ADD和SUB可以实现ADR:
某些形式的ADR指令可以表示为ADD或SUB形式,而PC为Rn。允许使用那些形式的ADD和SUB,并且 不推荐使用。
何时使用ADD
无法做到? GNU GAS建议ADR只是伪运算,它总是组合为ADD或SUB:https://sourceware.org/binutils/docs-2.31/as/ARM-Opcodes.html#ARM-Opcodes
该指令将标签的地址加载到指定的寄存器中。该指令将根据标签所在的位置求值为PC相对ADD或SUB指令。如果标签超出范围,或者未在与ADR指令相同的文件(和部分)中定义标签,则将生成错误。该指令将不使用文字池。
请注意,在ARMv8上,不能像通用寄存器那样在所有指令中使用PC,因此ADR实际上在那里很重要,并且具有单独的编码:Howto write PC relative adressing on arm asm?
代码示例:
adr r0, label
ldr r1, =label
label:
/* r0 == r1 */
On GitHub with runnable assertion。
ADRP
ADRP与ADR类似,但它会将12位低位清零,并相对于当前PC页面而不是字节相对于页面进行移位。
这样,我们可以跳得更远(+ -4GiB),其代价是需要在ADRP之后设置额外的ADD来设置较低的12位。 ARMv8手册说:
ADR指令将一个带符号的21位立即数添加到获取该指令的程序计数器的值,然后将结果写入通用寄存器。这样就可以计算当前PC的±1MB之内的任何字节地址。
ADRP指令将带符号的21位立即数左移12位,将其添加到程序计数器的值中,并将低12位清除为零,然后将结果写入通用寄存器。这允许在4KB对齐的存储器区域中计算地址。结合使用ADD(立即)指令或具有12位立即数偏移量的加载/存储指令,可以计算或访问当前PC±4GB范围内的任何地址。
ADRP仅存在于ARMv8中,而不存在于ARMv7中。
ARMv8 DDI 0487C.a manual表示Page只是4KB的助记符,并不反映实际的页面大小,可以将其配置为其他大小。 C3.3.5“相对于PC的地址计算”:
ADRP描述中使用的术语页面是4KB内存区域的简写,与虚拟内存无关。 记忆翻译颗粒大小。
代码示例:
adrp x0, label
adr x1, label
/* Clear the lower 12 bits. */
bic x1, x1, #0xFF
bic x1, x1, #0xF00
/* x0 == x1 */
label:
On GitHub with runnable assertion。
ADRL
ADRL不是实际的指令,只是伪指令。
因此,在v7手册中没有提到它,而在v8手册中“读取PC的指令”中只提到了一个,但是我在解释它的手册中找不到任何地方,所以也许只是文档错误?
因此,我将重点介绍GNU AS实现,该实现在https://sourceware.org/binutils/docs-2.31/as/ARM-Opcodes.html#ARM-Opcodes的ARM特定功能下进行了记录:
adrl <register> <label>
该指令将标签的地址加载到指定的寄存器中。该指令将根据标签所在的位置求出一条或两条与PC相关的ADD或SUB指令。如果不需要第二条指令,则会在其位置生成一条NOP指令,因此该指令始终为8个字节长。
因此,似乎可以达到与ADR相同的结果,但是使用不同的指令? Objdump确认了GNU手册的内容:
adr r0, label
10478: e28f0008 add r0, pc, #8
adrl r2, label
10480: e28f2000 add r2, pc, #0
10484: e1a00000 nop ; (mov r0, r0)
TODO:ADRL与ADR相比有什么优势?
尝试使用它在aarch64上失败,因为根据GNU GAS手册,它是ARMv7的特定功能。
替代
当PC偏移量太长而无法编码为指令时,一种主要替代方法是使用movk / movw / movt,请参见:What is the difference between =label (equals sign) and [label] (brackets) in ARMv6 assembly?