ARM汇编中的ADRP和ADRL指令的语义是什么? [英] What are the semantics of ADRP and ADRL instructions in ARM assembly?

查看:554
本文介绍了ARM汇编中的ADRP和ADRL指令的语义是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

ADRP

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

Address of 4KB page at a PC-relative offset.

ADRL

将PC相对地址加载到寄存器中.它类似于ADR 操作说明.与ADR相比,ADRL可以加载更多的地址范围,因为 它会生成两条数据处理指令.

Load a PC-relative address into a register. It is similar to the ADR instruction. ADRL can load a wider range of addresses than ADR because it generates two data processing instructions.

具体地说,

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

ADRL assembles to two instructions, an ADRP followed by ADD. If the assembler cannot construct the address in two instructions, it generates a relocation. The linker then generates the correct offsets. ADRL produces position-independent code, because the address is calculated relative to PC.

ADRPADRL指令有什么作用?更重要的是,ADRP和后跟ADRP的PC相对地址如何构成?

What do ADRP and ADRL instructions do? More importantly, how does and ADRP followed by an ADD construct a PC-relative address?

推荐答案

ADR

ADR是一个简单的PC相对地址计算:您给它一个立即偏移量,并将相对于当前PC的地址存储在寄存器中.

ADR is a simple PC-relative address calculation: you give it an immediate offset, and it stores in the register the address relative to the current PC.

例如,如果以下ADR指令位于内存中的0x4000位置:

For example, if the following ADR instruction is placed at position 0x4000 in memory:

adr x0, #1

然后在执行该指令后,x0现在包含值0x4001. 在可运行的GitHub上断言.

then after this instruction is executed x0 now contains the value 0x4001. On GitHub with runnable assertion.

我们可以尝试这样做:

mov x0, #0x4001

但是相对于PC的寻址具有以下优点:

but PC-relative addressing has the following advantages:

  • 所有ARMv7/ARMv8指令的长度均为4个字节.与指令宽度可变的x86形成鲜明对比.

  • all ARMv7 / ARMv8 instructions are 4 bytes long. This in in large contrast to x86 where instruction widths are variable.

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

This simplifies a lot of things, but it has one unfortunate implication: you cannot encode full addresses (4 / 8 bytes) in a single instruction, since we need some bits to encode the instruction itself.

即使我们无法存储完整的地址,我们也可以通过PC的相对地址来引用其中的一些地址(适合编码的地址),这对于许多应用程序来说通常就足够了,因为我们经常只跳转到附近代码位置.

Even though we cannot store full addresses, we can refer to some of them (those that fit into the encoding) by the relative address to the PC, which is often enough for many applications, since we are often only jumping to nearby code locations.

这里的原理类似于ldr =伪指令的存在原理:

The rationale here is analogous to that of the existence of the ldr = pseudo instruction: Why use LDR over MOV (or vice versa) in ARM assembly?

允许使用位置无关的代码,这是避免共享库在内存中发生冲突的基础,而且对于主要文本段启用在gcc和ld中与位置无关的可执行文件的-fPIE选项是什么?

it allows for position independent code, which is fundamental to avoid shared libraries from clashing in memory, and but is also useful for the main text segment to enable ASLR, see also: What is the -fPIE option for position-independent executables in gcc and ld?

生成的代码较小

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

The ADR instruction uses a 21-bit immediate for the offset, which allows for +-1MiB jumps (20-bits + 1 for the sign).

在ARmv7/aarch32中,有时可以使用PC上的ADD和SUB实现ADR,如

In ARmv7/aarch32, ADR can sometimes be achieved with ADD and SUB with PC as documented on the ARMv7 DDI 0406C.d manual D9.4 "Explicit use of the PC in ARM instructions":

某些形式的ADR指令可以表示为ADD或SUB形式,而PC为Rn.允许使用那些形式的ADD和SUB,并且 不推荐使用.

Some forms of the ADR instruction can be expressed as forms of ADD or SUB, with the PC as Rn. Those forms of ADD and SUB are permitted, and not deprecated.

TODO什么时候不能用ADD实现? GNU GAS建议ADR只是伪操作,它总是组合为ADD或SUB: https://sourceware.org/binutils/docs-2.31/as/ARM-Opcodes.html#ARM-Opcodes

TODO when can it not be achieved with ADD? GNU GAS suggests that ADR is just a pseudo-op that always assembles into ADD or SUB: https://sourceware.org/binutils/docs-2.31/as/ARM-Opcodes.html#ARM-Opcodes

该指令会将标签的地址加载到指定的寄存器中.该指令将根据标签所在的位置求值为PC的ADD或SUB相对指令.如果标签超出范围,或者未在与ADR指令相同的文件(和部分)中定义标签,则将生成错误.该指令将不使用文字池.

This instruction will load the address of label into the indicated register. The instruction will evaluate to a PC relative ADD or SUB instruction depending upon where the label is located. If the label is out of range, or if it is not defined in the same file (and section) as the ADR instruction, then an error will be generated. This instruction will not make use of the literal pool.

但是,在ARMv8 aarch64中,PC不能像通用寄存器那样在每条指令中使用,因此ADR实际上在那里很重要,并且具有单独的编码:

In ARMv8 aarch64 however, the PC cannot be used in every instruction like a general purpose register, therefore ADR is actually important there and has a separate encoding: Howto write PC relative adressing on arm asm?

ADRP

ADRP与ADR类似,但它:

ADRP is similar to ADR, but it:

  • 相对于当前页面而不是字节移动页面(4KiB,ADRP中的P代表页面)
  • 将12个低位清零

例如,如果以下ADRP指令位于内存中的0x4050位置:

For example, if the following ADRP instruction is placed at position 0x4050 in memory:

adrp x0, #1

然后在执行该指令后x0现在包含值0x5000(+1 * 4k且前12位为零).

then after this instruction is executed x0 now contains the value 0x5000 (+ 1 * 4k and zero out the first 12 bits).

由于低12位被清零,为了计算完整地址,通常将ADRP与ADD + :lo12:重定位一起使用,如下所示:

Since the lower 12 bits are zeroed out, to calculate a full address, ADRP is normally used together with an ADD + :lo12: relocation as in:

adrp x0, myvariable
add x0, x0, :lo12:myvariable

请注意,:lo12:只是将myvariable的低12位提取到立即数,链接器生成的最终指令只是add x0, x0, #<immediate>,另请参见:链接器有什么作用?.

Note that the :lo12: just extracts the lower 12 bits of myvariable to an immediate, the final instruction produced by the linker is just an add x0, x0, #<immediate>, see also: AArch64 relocation prefixes and What do linkers do?.

与ADR相比,ADRP的优势在于,我们可以跳得更远(+ -4GiB),其代价是需要在ADRP之后设置额外的ADD来设置较低的12位. ARMv8手册说:

The advantage of ADRP over ADR is that we can jump much further (+-4GiB), at the cost of needing to do an extra ADD after ADRP to set the lower 12-bits. The ARMv8 manual says:

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

The ADR instruction adds a signed, 21-bit immediate to the value of the program counter that fetched this instruction, and then writes the result to a general-purpose register. This permits the calculation of any byte address within ±1MB of the current PC.

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

The ADRP instruction shifts a signed, 21-bit immediate left by 12 bits, adds it to the value of the program counter with the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address within ±4GB of the current PC.

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

ADRP only exists in ARMv8, not in ARMv7.

ARMv8 DDI 0487C.a手册表示Page是只是4KB的助记符,并不反映实际的页面大小,可以将其配置为其他大小. C3.3.5相对于PC的地址计算":

The ARMv8 DDI 0487C.a manual says that Page is just a mnemonic for 4KB, and does not reflect the actual page size, which is configurable to other sizes. C3.3.5 "PC-relative address calculation":

ADRP描述中使用的术语页面是4KB内存区域的简写,与虚拟内存无关. 记忆翻译颗粒大小.

The term page used in the ADRP description is short-hand for the 4KB memory region, and is not related to the virtual memory translation granule size.

请注意,与ADR不同,如果将代码加载到内存中的位置相对于原始链接程序偏移量的偏移量不为4K的倍数,则ADRP会中断.例如,如果您向上移动一点,目标地址可能会落在下一页上,而PC位置仍旧在旧页上,从而使ADRP指向错误的页面.但是,仍然依赖于ADRP的可执行文件被视为PIE,而诸如动态链接器/ASLR之类的系统只能以4K的倍数重新定位在内存中,相关信息:

Note that unlike ADR, ADRP would break if you were to load the code to memory at a position that is not offset by a multiple of a 4K relative to the original linker offset. For example, if you shift a bit up, the target address could fall on the next page, while the PC location stays on the old one, making ADRP point to the wrong page. However, executables that rely on ADRP are still considered PIE, and systems such as the dynamic linker/ASLR can only relocate in memory by multiples of 4K, related: How is the address of the text section of a PIE executable determined in Linux?

ADRL

ADRL不是实际的指令,而只是一个伪指令",即汇编程序.

ADRL is is not an actual instruction, just a "pseudo-instruction", i.e. an assembler hack.

因此,在v7手册中没有提及,并且在v8手册中的读取PC的说明"中仅提及了一个,但是我在说明它的手册中找不到任何内容,因此也许这只是文档错误?

As such, it is not mentioned in the v7 manual, and there is just one mention on the v8 manual at "Instructions that read the PC", but I can't find anywhere in the manual that explains it, so maybe it is just a documentation mistake?

因此,我将重点介绍在

I will therefore focus on the GNU AS implementation which documents it at https://sourceware.org/binutils/docs-2.31/as/ARM-Opcodes.html#ARM-Opcodes under ARM specific features::

adrl <register> <label>

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

This instruction will load the address of label into the indicated register. The instruction will evaluate to one or two PC relative ADD or SUB instructions depending upon where the label is located. If a second instruction is not needed a NOP instruction will be generated in its place, so that this instruction is always 8 bytes long.

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

Therefore it appears to be able to expand to multiple ADD / SUB, presumably to allow for a larger jump from the PC.

Objdump确认了GNU手册中关于短地址的内容:

Objdump confirms what the GNU manual says for short addresses:

    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:长地址的示例.最大长度是多少?只是ADD/ADR的2倍或更多?

TODO: example of long addresses. What is the maximum length? Just 2x that of ADD / ADR or more?

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

Trying to use it on aarch64 fails, since it is an ARMv7 specific features according to the GNU GAS manual. The error message on GNU GAS is 2.29.1 is:

Error: unknown mnemonic `adrl' -- `adrl r6,.Llabel' 

Linux内核还在 https://patchwork.kernel中定义了一个名为adr_l的宏. .org/patch/9883301/ TODO了解基本原理.

The Linux kernel has also defined a macro called adr_l at https://patchwork.kernel.org/patch/9883301/ TODO understand rationale.

替代

当PC偏移太长而无法编码成指令时,一种主要替代方法是使用movk/movw/movt,请参见:

One main alternative for when the PC offset is too long to encode into the instruction either, is to use movk / movw / movt, see: What is the difference between =label (equals sign) and [label] (brackets) in ARMv6 assembly?

这篇关于ARM汇编中的ADRP和ADRL指令的语义是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆