Assembly ARM组装中ADRP和ADRL指令的语义是什么?

Assembly ARM组装中ADRP和ADRL指令的语义是什么?,assembly,arm,arm64,instructions,position-independent-code,Assembly,Arm,Arm64,Instructions,Position Independent Code,PC相对偏移量处4KB页面的地址 将PC相对地址加载到寄存器中。它类似于ADR 指示ADRL可以加载比ADR更大范围的地址,因为 它生成两条数据处理指令 具体来说, ADRL汇编成两条指令,一条ADRP后跟ADD。如果 汇编程序不能在两条指令中构造地址,它 生成重新定位。然后链接器生成正确的偏移量。 ADRL产生与位置无关的代码,因为地址是 相对于PC计算 ADRP和ADRL指令做什么?更重要的是,和ADRP后接ADD如何构建PC相对地址?ADR ADR是一个简单的PC相对地址计算:您给它一个

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

将PC相对地址加载到寄存器中。它类似于ADR 指示ADRL可以加载比ADR更大范围的地址,因为 它生成两条数据处理指令

具体来说,

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

ADRP
ADRL
指令做什么?更重要的是,和
ADRP
后接
ADD
如何构建PC相对地址?

ADR

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


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

adr x0, #1
adrp x0, #0x1000
然后,在执行此指令后,
x0
现在包含值0x4001

我们可以试着做以下事情:

mov x0, #0x4001
但PC相对寻址有以下优点:

  • 所有ARMv7/ARMv8指令的长度均为4字节。这在很大程度上与x86不同,x86的指令宽度是可变的

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

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

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

  • 它允许独立于位置的代码,这是避免共享库在内存中冲突的基础,但也有助于启用主文本段,另请参见:

  • 生成的代码更小

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

在ARmv7/aarch32中,ADR有时可以通过D9.4“在ARM指令中明确使用PC”中记录的PC的ADD和SUB来实现:

ADR指令的某些形式可以表示为ADD或SUB形式,PC为Rn。允许这些形式的附加和附加,以及 不反对

TODO什么时候不能用
添加
?GNU GAS表明ADR只是一个伪op,总是组合成ADD或SUB:

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

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

ADRP

ADRP与ADR相似,但它:

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

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

adr x0, #1
adrp x0, #0x1000
然后,在执行此指令后,
x0
现在包含值0x5000(+0x1000,并将前12位归零)

但是请注意,上面的语法只是教育性的,因为GNUGAS似乎不接受文字整型常量作为参数,而只接受符号。(或者,它将0x1000视为一个符号,而链接失败,这是沿着这些线进行的,现在没有时间完全理解它)

由于较低的12位被调零,为了计算完整地址,ADRP通常与ADD+
:lo12:
重定位一起使用,如:

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

请注意,
:lo12:
只是将
myvariable
的较低12位提取为立即数,链接器生成的最终指令只是一条
add x0,x0,#
,另请参见:and

与ADR相比,ADRP的优点是我们可以跳得更远(+-4GiB),代价是需要在ADRP之后进行额外的加法来设置较低的12位。ARMv8手册说明:

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

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

ADRP的另一个限制是,与ADR不同,如果将代码加载到内存中的位置相对于原始链接器偏移量(例如,由于ASLR)不偏移4K的倍数,则ADRP将中断。例如,如果您稍微向上移动一点,目标地址可能会落在下一页上,而PC位置会停留在旧页上,从而使ADRP指向错误的页。但是,仍然考虑依赖ADRP的可执行文件
Error: unknown mnemonic `adrl' -- `adrl r6,.Llabel'