【问题标题】:AArch64 relocation prefixesAArch64 重定位前缀
【发布时间】:2016-07-25 14:30:00
【问题描述】:

我注意到一个 GNU asm relocation syntax 用于 ARM 64 位汇编。像#:abs_g0_nc::pg_hi21: 这样的部分是什么?他们在哪里解释?他们有模式还是他们在旅途中组成?在哪里可以了解更多信息?

【问题讨论】:

标签: assembly relocation


【解决方案1】:

简介

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_offset指定的位置,使用r_info的下半部分选择的公式从addendr_addend for RELA)和符号地址(可从r_info 的上半部分获得)。

实际上写部分已经被简化了,与其他体系结构相反,指令的立即字段通常与用于编码操作的字段完全分开,在 ARM 中,立即值与其他编码信息混合在一起。 所以加载器应该知道试图重定位的指令是什么类型的,如果它是一条指令1,而不是让它反汇编重定位的位置,而是设置重定位类型的汇编器按照说明。

每个重定位符号只能重定位一个或两个编码等效的指令。
在特定情况下,重定位本身甚至会改变指令的类型。

在重定位期间计算的值被隐式扩展到 64 位,根据选择的重定位类型有符号或无符号。

AArch64 重定位

作为 ARM 一个具有固定指令大小的 RISC 架构,将全宽(即 64 位)立即加载到寄存器中并非易事,因为没有指令可以具有全宽立即字段。

AArch64 中的重定位也必须解决这个问题,这实际上是一个双重问题:首先,找到程序员打算使用的真实值(这是问题的纯重定位部分);其次,找到一种方法将其放入寄存器,因为没有指令具有 64 位立即数字段。

第二个问题是通过使用group relocation来解决的,group中的每个relocation type都用于计算64 bits value的16 bits,因此一个relocation type只能有4个relocation type组(范围从 G0G3)。

这种切片为 16 位适合 movk(移动保持)、movz(移动归零)和 movn(移动逻辑否定)。
其他指令,如bbladrpadr 等,都有专门适合它们的重定位类型。

只要给定指令引用符号的重定位类型只有一种,因此是明确的,汇编程序可以生成相应的条目,而程序员无需显式指定它。

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

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


1 如果r_offset 指向数据段中的某个站点,则计算值将作为 64 位字写入指定位置。

重定位运算符

首先,一些参考资料:

  • 描述 ELF64 格式的重定位类型的 ARM 文档是 here,第 4.6 节

  • 一个测试 AArch64 程序集文件,大概包含 GAS 可用的所有重定位运算符是 here here

约定

遵循我们的 ARM 文档约定:

S 是被重定位符号的运行时地址。
A 是 搬迁加数。
P是搬迁地点地址 (源自r_offset)。
X 是重定位的结果 操作,在应用任何掩码或位选择操作之前。
Page(expr) 是表达式 expr 的页地址,定义为 expr & ~0xFFF,即expr,低 12 位清零。 GOTGlobal Offset Table.
GDAT(S+A) 的地址表示 GOT 中用于地址 S+A 的 64 位条目。这 条目将在运行时通过重定位重定位 R_AARCH64_GLOB_DAT(S+A).
G(expr)是GOT的地址 表达式 expr.
Delta(S) 的条目解析差异 S的静态链接地址和 S。如果S 是空符号(ELF 符号索引 0),则解析为 P的静态链接地址与执行的区别 P的地址。
Indirect(expr)代表调用结果 expr 作为函数。
[msb:lsb] 是位掩码操作 表示值中位的选择,包括边界。

运营商

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

|X|≤2^16 类型的表达式旨在 -2^16 ≤ X ,注意右边的严格不等式。
这是一种对符号的滥用,被格式化表格的约束所调用。

集团搬迁

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 位值,则在值本身中指定符号 if sully。
它们始终仅用于设置最高部分,因为该标志仅在此处相关。
他们总是检查有符号值中的溢出,使值的意义减少。
这些重定位根据值的符号将指令的类型更改为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 中的大部分内容都是深奥的,遗憾的是,并非所有这些都被彻底记录下来。我的职业生涯太晚了,现在不能做出改变!
  • @pogojotz 谢谢。我找到了另一个副本。
  • 非常感谢。我希望你有比现在更多的选票,你的回答很棒。
猜你喜欢
  • 1970-01-01
  • 2019-05-13
  • 2020-05-26
  • 2018-07-23
  • 2018-07-07
  • 2021-04-04
  • 2011-02-07
  • 2017-09-01
  • 1970-01-01
相关资源
最近更新 更多