AArch64重定位前缀

12
我注意到ARM 64位汇编中有GNU汇编重定位语法。像#:abs_g0_nc::pg_hi21:这样的部分是什么?它们在哪里有解释?它们是否遵循某种模式或是即兴创作的?我应该去哪里了解更多信息?

3
它们在第4.6节这里进行了描述。有相当少的重定位类型,如果没有其他人愿意的话,我可能会稍后将该链接转化为答案。 - Margaret Bloom
Arm 64位架构(AArch64)的更新版本ELF在这里:https://github.com/ARM-software/abi-aa/blob/master/aaelf64/aaelf64.rst。 - scrutari
1个回答

30

介绍

ELF64 定义了两种重定位记录类型,分别称为 RELRELA

typedef struct
{
    Elf64_Addr r_offset;    /* Address of reference */
    Elf64_Xword r_info;     /* Symbol index and type of relocation */
} Elf64_Rel;

typedef struct
{
    Elf64_Addr r_offset;    /* Address of reference */
    Elf64_Xword r_info;     /* Symbol index and type of relocation */
    Elf64_Sxword r_addend;  /* Constant part of expression */
} Elf64_Rela;
每个重定位表项的作用是向装载器(静态或动态)提供四个信息:
- 要修补指令的虚拟地址或偏移量,这由 `r_offset` 给出。 - 访问的符号的运行时地址,这由 `r_info` 的高位给出。 - 一个自定义值称为 addend。该值最终作为表达式中的操作数,用于计算将写入要修补的指令的值。对于 RELA 条目,此值在 `r_addend` 中;对于 REL 条目,则从重定位位置提取它。 - 重定位类型,确定用于计算修补指令的值的表达式类型。这编码在 `r_info` 的低位中。
在重定位阶段,装载器遍历所有重定位表项,并使用由 `r_info` 的低位选择的公式写入到每个 `r_offset` 指定的位置,以计算要存储的值,其中包括 addend(对于 RELA 是 `r_addend`),以及符号地址(可从 `r_info` 的高位获得)。
实际上,与其他体系结构不同,在 ARM 中,指令的立即数字段通常与用于编码操作的字节完全分离不同,立即值与其他编码信息混合。因此,装载器应该知道它正在尝试重定位哪种类型的指令,如果它确实是指令的话。但是,与其让装载器反汇编重定位位置,不如由汇编器根据指令设置重定位类型。
每个重定位符号只能重定位一个或两个等效编码的指令。在特定情况下,重定位本身甚至会更改指令类型。
在重定位过程中计算的值在隐式地扩展为 64 位,带符号或无符号,具体取决于所选择的重定位类型。
AArch64 重定位需要解决一个问题:寄存器加载完整宽度(即 64 位)立即数不是微不足道的问题。每个重定位类型在组中用于计算 64 位值的 16 位部分,因此一个组中只能有四个重定位类型(从 G0 到 G3)。

这种将数据切片成16位的方式与movk(保持移动)、movz(清零移动)和movn(逻辑取反移动)相匹配。
其他指令,如bbladrpadr等,则有一个特别适合它们的重定位类型。

每当对于一条引用符号的给定指令只有一个明确的可能的重定位类型时,汇编器就可以生成相应的条目,而程序员无需显式指定它。

组重定位不适合此类别,它们存在是为了让程序员有一些灵活性,因此通常会被显式说明。 在一个组中,重定位类型可以指定汇编器是否必须执行溢出检查。
用于加载值低16位的G0重定位(除非明确禁止)会检查该值是否能够适合16位(有符号或无符号,取决于所使用的特定类型)。 如果加载31-16位,则G1也是如此检查该值是否适合32位。
因此,G3始终是非检查的,因为每个值都适合64位。

最后,重定位可以用于将整数值加载到寄存器中。 实际上,符号的地址只是一个任意的整数常量。
请注意,r_addend宽度为64位。


1如果r_offset指向数据段中的位置,则计算出的值写入所指示位置的64位字中。

重定位操作符

首先,一些参考资料:

  • ARM文档描述了针对ELF64格式的重定位类型,在此处,第4.6节。

  • 一个包含GAS可用所有重定位操作符的测试AArch64汇编文件,在此处 在此处

约定

按照ARM文档的惯例,我们有:

S是被重定位符号的运行时地址。
A是重定位的加数。
P是重定位位置的地址(从r_offset导出)。
X是重定位操作的结果,在应用任何屏蔽或位选择操作之前。
Page(expr)是表达式expr的页地址,定义为expr & ~0xFFF,即将expr的低12位清除。 GOT全局偏移表的地址。
GDAT(S+A)表示地址S+A在GOT中的64位条目。该条目将在运行时使用重定位R_AARCH64_GLOB_DAT(S+A)进行重定位。
G(expr)是表达式expr的GOT条目地址
Delta(S)解析为S的静态链接地址与S的执行地址之间的差异。如果S是空符号(ELF符号索引0),则解析为P的静态链接地址与P的执行地址之间的差异。
Indirect(expr)表示将expr作为函数调用的结果。
[msb:lsb]是一种位掩码操作,表示选择值中的位,边界是包括的。

操作符

为了简洁起见,重定位名称缺少前缀R_AARCH64_

形如|X|≤2^16的表达式意为-2^16 ≤ X < 2^16,请注意右侧的严格不等号。
这是一种符号滥用,由于表格格式的限制而产生。

组重定位

Operator    | Relocation name | Operation | Inst | Immediate | Check
------------+-----------------+-----------+------+-----------+----------
:abs_g0:    | MOVW_UABS_G0    | S + A     | movz | X[15:0]   | 0≤X≤2^16
------------+-----------------+-----------+------+-----------+----------
:abs_g0_nc: | MOVW_UABS_G0_NC | S + A     | movk | X[15:0]   | 
------------+-----------------+-----------+------+-----------+----------
:abs_g1:    | MOVW_UABS_G1    | S + A     | movz | X[31:16]  | 0≤X≤2^32
------------+-----------------+-----------+------+-----------+----------
:abs_g1_nc: | MOVW_UABS_G1_NC | S + A     | movk | X[31:16]  | 
------------+-----------------+-----------+------+-----------+----------
:abs_g2:    | MOVW_UABS_G2    | S + A     | movz | X[47:32]  | 0≤X≤2^48
------------+-----------------+-----------+------+-----------+----------
:abs_g2_nc: | MOVW_UABS_G2_NC | S + A     | movk | X[47:32]  | 
------------+-----------------+-----------+------+-----------+----------
:abs_g3:    | MOVW_UABS_G3    | S + A     | movk | X[64:48]  | 
            |                 |           | movz |           |
------------+-----------------+-----------+------+-----------+----------
:abs_g0_s:  | MOVW_SABS_G0    | S + A     | movz | X[15:0]   | |X|≤2^16
            |                 |           | movn |           |
------------+-----------------+-----------+------+-----------+----------
:abs_g1_s:  | MOVW_SABS_G1    | S + A     | movz | X[31:16]  | |X|≤2^32
            |                 |           | movn |           |
------------+-----------------+-----------+------+-----------+----------
:abs_g2_s:  | MOVW_SABS_G2    | S + A     | movz | X[47:32]  | |X|≤2^48
            |                 |           | movn |           |
------------+-----------------+-----------+------+-----------+----------

表格中展示了ABS版本,汇编器可以根据符号引用和输出格式的类型选择PREL(PC相对)或GOTOFF(GOT相对)版本。

这些重定位运算符的一个典型用法是

Unsigned 64 bits                      Signed 64 bits   
movz    x1,#:abs_g3:u64               movz  x1,#:abs_g3_s:u64
movk    x1,#:abs_g2_nc:u64            movk  x1,#:abs_g2_nc:u64
movk    x1,#:abs_g1_nc:u64            movk  x1,#:abs_g1_nc:u64
movk    x1,#:abs_g0_nc:u64            movk  x1,#:abs_g0_nc:u64
通常使用检查操作符中的一个,即设置最高位的操作符。因此,检查版本只重新定位movz,而非检查版本会重新定位movk(部分设置寄存器)。G3将两者都重新定位,因为它本质上是非检查的,因为没有值可以超过64位。
带有_s后缀的有符号版本始终进行检查。没有G3版本,因为如果使用64位值,则在值本身中完全指定了符号。它们总是仅用于设置最高部分,因为符号仅在那里相关。这些重新定位根据值的符号将指令类型更改为movnmovz,从而有效地对值进行符号扩展。
还提供了组重新定位。
PC相对,19、21、33位地址
Operator    | Relocation name | Operation | Inst | Immediate | Check
------------+-----------------+-----------+------+-----------+----------
[implicit]  | LD_PREL_LO19    | S + A - P | ldr  | X[20:2]   | |X|≤2^20
------------+-----------------+-----------+------+-----------+----------
[implicit]  | LD_PREL_LO21    | S + A - P | adr  | X[20:0]   | |X|≤2^20
------------+-----------------+-----------+------+-----------+----------
[implicit]  | LD_PREL_LO21    | S + A - P | adr  | X[20:0]   | |X|≤2^20
------------+-----------------+-----------+------+-----------+----------
:pg_hi21:   | ADR_PREL_PG     | Page(S+A) | adrp | X[31:12]  | |X|≤2^32
            | _HI21           | - Page(P) |      |           |
------------+-----------------+-----------+------+-----------+----------
:pg_hi21_nc:| ADR_PREL_PG     | Page(S+A) | adrp | X[31:12]  | 
            | _HI21_NC        | - Page(P) |      |           |
------------+-----------------+-----------+------+-----------+----------
:lo12:      | ADD_ABS_LO12_NC | S + A     | add  | X[11:0]   | 
------------+-----------------+-----------+------+-----------+----------
:lo12:      | LDST8_ABS_LO12  | S + A     | ld   | X[11:0]   | 
            | _NC             |           | st   |           |
------------+-----------------+-----------+------+-----------+----------
:lo12:      | LDST16_ABS_LO12 | S + A     | ld   | X[11:1]   | 
            | _NC             |           | st   |           |
------------+-----------------+-----------+------+-----------+----------
:lo12:      | LDST32_ABS_LO12 | S + A     | ld   | X[11:2]   | 
            | _NC             |           | st   |           |
------------+-----------------+-----------+------+-----------+----------
:lo12:      | LDST64_ABS_LO12 | S + A     | prfm | X[11:3]   | 
            | _NC             |           |      |           |
------------+-----------------+-----------+------+-----------+----------
:lo12:      | LDST128_ABS     | S + A     | ?    | X[11:4]   | 
            | _LO12_NC        |           |      |           |
:lo12:的含义取决于指令处理的数据大小(例如,ldrb使用LDST8_ABS_LO12_NCldrh使用LDST16_ABS_LO12_NC)。这些重定位的GOT相对版本也存在,汇编器将选择正确的版本。控制流重定位。
Operator    | Relocation name | Operation | Inst | Immediate | Check
------------+-----------------+-----------+------+-----------+----------
[implicit]  | TSTBR14         | S + A - P | tbz  | X[15:2]   | |X|2^15
            |                 |           | tbnz |           |  
------------+-----------------+-----------+------+-----------+----------
[implicit]  | CONDBR19        | S + A - P | b.*  | X[20:2]   | |X|2^20
------------+-----------------+-----------+------+-----------+----------
[implicit]  | JUMP26          | S + A - P | b    | X[27:2]   | |X|2^27
------------+-----------------+-----------+------+-----------+----------
[implicit]  | CALL26          | S + A - P | bl   | X[27:2]   | |X|2^27
------------+-----------------+-----------+------+-----------+----------

结语

我找不到官方文档。
上面的表格是从GAS测试用例和ARM文件中重构出来的,用于解释AArch64一致ELF可用的重定位类型。

这些表格并没有展示在ARM文件中出现的所有重定位类型,因为大部分都是互补版本,由汇编器自动挑选。

如果有一个包含示例的章节那就太好了,但是我没有ARM GAS。
将来我可能会扩展这个答案,包括汇编列表和重定位转储的示例。


感谢您的写作。我很感激你涉及一个非常广泛的话题。我认为binutils中的许多内容都是玄学的,遗憾的是并不是所有这些内容都有详细记录。已经晚了,我的职业生涯无法转换! - Jeenu
链接 https://www.slac.stanford.edu/grp/lcls/controls/global/sw/epics/site/cexpsh/Devl/cexp/cexp-CEXP_Release_2_2/binutils-2.23.1/gas/testsuite/gas/aarch64/reloc-insn.s 不再可用。 - pogojotz
@pogojotz 谢谢。我找到了另一个副本。 - Margaret Bloom
非常感谢您。我希望您能获得比现在更多的投票,您的答案非常出色。 - Pavel.Zh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接