ARM汇编中ADRP和ADRL指令的语义是什么?

Hol*_*ock 9 assembly arm opcode

ADRP

PC相对偏移量的4KB页面地址.

ADRL

将PC相关地址加载到寄存器中.它类似于ADR指令.ADRL可以加载比ADR更广泛的地址,因为它生成两个数据处理指令.

特别,

ADRL汇编为两条指令,ADRP后跟ADD.如果汇编程序无法在两条指令中构造地址,则会生成重定位.然后链接器生成正确的偏移量.ADRL生成与位置无关的代码,因为地址是相对于PC计算的.

做什么ADRPADRL说明做什么?更重要的是,如何与ADRP后面的ADD结构与PC相关的地址?

Cir*_*lli 8

ADR

根本问题是所有ARMv7 / ARMv8指令的长度均为4个字节。

这简化了很多事情,但是有一个不幸的含义:您无法在单个指令中对完整地址(4/8字节)进行编码,因为我们需要一些位来对指令本身进行编码。

这就是ADR指令的来源。即使我们不能存储完整的地址,也可以通过PC的相对地址来引用其中的一些地址(适合编码的地址),这对于许多应用程序来说通常就足够了。

ADR指令使用21位立即数作为偏移量,这允许+ -1MiB跳转(20位+ 1表示符号)。

这里的原理类似于ldr =伪指令:为什么在ARM汇编中在MOV上使用LDR(反之亦然)?

有时可以使用PC上的ADD和SUB来实现ADR,如ARMv7 DDI 0406C.d手册 D9.4“ ARM指令中PC的明确使用”所述:

某些形式的ADR指令可以表示为ADD或SUB形式,而PC为Rn。那些形式的ADD和SUB是允许的,不建议弃用。

TODO什么时候无法实现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实际上在那里很重要,并且具有单独的编码:如何在arm asm上编写PC相对地址?

代码示例:

    adr r0, label
    ldr r1, =label
label:
    /* r0 == r1 */
Run Code Online (Sandbox Code Playgroud)

在具有可运行断言的GitHub上

ADRP

ADRP与ADR类似,但是它会将12位低位清零,并相对于当前PC页而不是字节移动页。

这样,我们可以进一步跳(+ -4GiB),其代价是需要在ADRP之后进行额外的ADD来设置较低的12位。ARMv8手册说:

ADR指令将一个带符号的21位立即数添加到获取该指令的程序计数器的值,然后将结果写入通用寄存器。这允许计算当前PC的±1MB范围内的任何字节地址。

ADRP指令将带符号的21位立即数左移12位,将其添加到程序计数器的值中,并将低12位清除为零,然后将结果写入通用寄存器。这允许在4KB对齐的存储器区域中计算地址。与ADD(立即)指令或具有12位立即数偏移量的Load / Store指令结合使用,可以计算或访问当前PC±4GB范围内的任何地址。

ADRP仅存在于ARMv8中,而不存在于ARMv7中。

ARMv8 DDI 0487C.a手册说,页面只是一个助记符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:
Run Code Online (Sandbox Code Playgroud)

在具有可运行断言的GitHub上

ADRL

ADRL不是实际的指令,只是伪指令。

因此,在v7手册中没有提到它,而在v8手册中的“阅读PC的说明”中只提到了一个,但是我在手册中找不到任何解释它的地方,所以也许只是文档错误?

因此,我将重点介绍GNU AS实现,该实现在https://sourceware.org/binutils/docs-2.31/as/ARM-Opcodes.html#ARM-Opcodes(在ARM特定功能下)中进行了记录:

adrl <register> <label>
Run Code Online (Sandbox Code Playgroud)

该指令将标签的地址加载到指定的寄存器中。该指令将根据标签所在的位置求出一条或两条与PC相关的ADD或SUB指令。如果不需要第二条指令,则会在其位置生成一条NOP指令,因此该指令始终为8个字节长。

因此,它似乎能够扩展到多个ADD / SUB,大概是允许从PC更大的跳跃。

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)
Run Code Online (Sandbox Code Playgroud)

TODO:长地址示例。最大长度是多少?是ADD / ADR的2倍或更多?

尝试在aarch64上使用它失败,因为根据GNU GAS手册,它是ARMv7的特定功能。GNU GAS上的错误消息是2.29.1:

Error: unknown mnemonic `adrl' -- `adrl r6,.Llabel' 
Run Code Online (Sandbox Code Playgroud)

Linux内核还定义了一个名为宏adr_lhttps://patchwork.kernel.org/patch/9883301/ TODO理解的理由。

备择方案

当PC偏移量太长而无法编码为指令时,一种主要的替代方法是使用movk / movw / movt,请参见:ARMv6汇编中= label(等号)和[label](括号)之间有什么区别? ?

  • “例如,如果以下 ADRP 指令放置在内存中的位置 0x4050: \ `adrp x0, #1` \ 那么执行该指令后,x0 现在包含值 0x8000(+ 1 * 4k 并将前 12 位清零) )”。-- 一位后面跟着 12 位零是 1000h,所以我相信这个例子应该产生 5000h,而不是 8000h。 (3认同)

归档时间:

查看次数:

5158 次

最近记录:

6 年,3 月 前