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)
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)
ADRL
ADRL不是实际的指令,只是伪指令。
因此,在v7手册中没有提到它,而在v8手册中的“阅读PC的说明”中只提到了一个,但是我在手册中找不到任何解释它的地方,所以也许只是文档错误?
因此,我将重点介绍GNU AS实现,该实现在https://sourceware.org/binutils/docs-2.31/as/ARM-Opcodes.html#ARM-Opcodes(在ARM特定功能下)中进行了记录:
Run Code Online (Sandbox Code Playgroud)adrl <register> <label>该指令将标签的地址加载到指定的寄存器中。该指令将根据标签所在的位置求出一条或两条与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_l在https://patchwork.kernel.org/patch/9883301/ TODO理解的理由。
备择方案
当PC偏移量太长而无法编码为指令时,一种主要的替代方法是使用movk / movw / movt,请参见:ARMv6汇编中= label(等号)和[label](括号)之间有什么区别? ?
| 归档时间: |
|
| 查看次数: |
5158 次 |
| 最近记录: |