AArch64 重定位前缀
AArch64 relocation prefixes
我注意到一个用于 ARM 64 位汇编的 GNU asm relocation syntax。 #:abs_g0_nc:
和 :pg_hi21:
这样的片段是什么?他们在哪里解释?他们是否有模式,或者他们是在旅途中弥补的?我在哪里可以了解更多信息?
简介
ELF64 定义了两种类型的重定位条目,称为 REL 和 RELA:
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
.
的高位给出的
一个名为加数的自定义值
该值最终作为表达式中的操作数,用于计算将写入以修补指令的值。
RELA条目在r_addend
中有这个值,REL条目从重定位站点中提取它。
搬迁类型
这决定了用于计算修补指令值的表达式类型。这个编码在r_info
.
的下半部分
搬迁
在重定位阶段,加载程序遍历所有重定位条目并写入每个 r_offset
指定的位置,使用 r_info
下半部分选择的公式计算值从addend(RELA 的r_addend
)和符号地址(可从r_info
的上部获得)存储。
实际上写入部分已经简化,与其他架构相反,在其他架构中,指令的立即字段通常与用于编码操作的字节完全分开,在 ARM 中,立即值与其他编码信息混合.
所以加载器应该知道什么样的指令试图重定位,如果它是一个指令1,而不是让它反汇编重定位的位置,它是设置的汇编程序根据说明的搬迁类型。
每个重定位符号只能重定位一两个编码等效指令。
在特定情况下,重定位本身甚至会更改指令类型。
在重定位期间计算的值被隐式扩展到 64 位,根据选择的重定位类型有符号或无符号。
AArch64 重定位
作为具有固定指令大小的 RISC 架构的 ARM,将全宽度(即 64 位)立即加载到寄存器中是非常重要的,因为没有指令可以具有全宽度立即字段。
AArch64 中的重定位也必须解决这个问题,它实际上是一个双重问题:首先,找到程序员打算使用的实际值(这是问题的纯重定位部分);其次,找到一种方法将其放入寄存器,因为没有指令具有 64 位立即数字段。
第二个问题通过使用组重定位得到解决,组中的每个重定位类型用于计算 64 位值的 16 位部分,因此只能有一组中的四种重定位类型(范围从 G0 到 G3)。
这种分成 16 位的切片符合 movk
(移动保持)、movz
(移动归零)和 movn
(逻辑移动否定)。
其他指令,如 b
、bl
、adrp
、adr
等,具有特别适合它们的重定位类型。
只要引用符号的给定指令只有一个因此明确的可能重定位类型,汇编程序就可以生成相应的条目,而无需程序员显式指定它。
组重定位不属于此类,它们的存在是为了让程序员有一定的灵活性,因此通常会明确说明。
在组中,重定位类型可以指定汇编程序是否必须执行溢出检查。
G0 重定位,用于加载值的低 16 位,除非明确禁止,否则检查该值是否适合 16 位(有符号或无符号,取决于使用的特定类型)。
G1 也是如此,加载位 31-16 检查值是否适合 32 位。
因此 G3 始终不检查,因为每个值都适合 64 位。
最后,重定位可用于将整数值加载到寄存器中。
实际上,一个符号的地址无非是一个任意整数常量。
请注意 r_addend
是 64 位宽。
1 如果 r_offset
指向数据部分中的站点,则计算值将在指示的位置写入 64 位字。
搬迁运营商
首先,一些参考资料:
约定
根据 ARM 文档约定,我们有:
S
is the runtime address of the symbol being relocated.
A
is the
addend for the relocation.
P
is the address of the relocation site
(derived from r_offset
).
X
is the result of a relocation
operation, before any masking or bit-selection operation is applied.
Page(expr)
is the page address of the expression expr, defined as
expr & ~0xFFF
, i.e. expr
with the lower 12 bits cleared.
GOT
is
the address of the Global Offset
Table.
GDAT(S+A)
represents a 64-bit entry in the GOT for address S+A. The
entry will be relocated at run time with relocation
R_AARCH64_GLOB_DAT(S+A).
G(expr)
is the address of the GOT
entry for the expression expr.
Delta(S)
resolves to the difference
between the static link address of S
and the execution address of
S
. If S
is the null symbol (ELF symbol index 0), resolves to the
difference between the static link address of P
and the execution
address of P
.
Indirect(expr)
represents the result of calling
expr
as a function.
[msb:lsb]
is a bit-mask operation
representing the selection of bits in a value, bounds are inclusive.
运算符
为了简洁起见,搬迁名称缺少前缀R_AARCH64_
。
类型 |X|≤2^16 的表达式意为 -2^16 ≤ X < 2^16, 注意右边的严格不等式。
这是对符号的滥用,由格式化 table.
的约束调用
组搬迁
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 | |
------------+-----------------+-----------+------+-----------+----------
在 table 中显示了 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 在值本身中指定。
它们总是只用于设置最高部分,因为符号只在那里相关。
他们总是检查有符号值是否溢出,使值变得更无意义。
这些重定位根据值的符号将指令类型更改为 movn
或 movz
,这有效地对值进行符号扩展。
组搬迁,也可以
PC-relative, 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_NC
,ldrh
使用 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
------------+-----------------+-----------+------+-----------+----------
结语
我找不到官方文档。
上面的 tables 是根据 GAS 测试用例和 ARM 文档重建的,该文档解释了可用于符合 AArch64 的 ELF 的重定位类型。
tables 没有显示 ARM 文档中存在的所有重定位,因为它们中的大多数是补充版本,由汇编程序自动选取。
有示例的部分会很棒,但我没有 ARM GAS。
将来我可能会扩展此答案以包括程序集列表和重定位转储的示例。
我注意到一个用于 ARM 64 位汇编的 GNU asm relocation syntax。 #:abs_g0_nc:
和 :pg_hi21:
这样的片段是什么?他们在哪里解释?他们是否有模式,或者他们是在旅途中弥补的?我在哪里可以了解更多信息?
简介
ELF64 定义了两种类型的重定位条目,称为 REL 和 RELA:
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
. 的高位给出的
一个名为加数的自定义值
该值最终作为表达式中的操作数,用于计算将写入以修补指令的值。
RELA条目在r_addend
中有这个值,REL条目从重定位站点中提取它。搬迁类型 这决定了用于计算修补指令值的表达式类型。这个编码在
r_info
. 的下半部分
搬迁
在重定位阶段,加载程序遍历所有重定位条目并写入每个 r_offset
指定的位置,使用 r_info
下半部分选择的公式计算值从addend(RELA 的r_addend
)和符号地址(可从r_info
的上部获得)存储。
实际上写入部分已经简化,与其他架构相反,在其他架构中,指令的立即字段通常与用于编码操作的字节完全分开,在 ARM 中,立即值与其他编码信息混合.
所以加载器应该知道什么样的指令试图重定位,如果它是一个指令1,而不是让它反汇编重定位的位置,它是设置的汇编程序根据说明的搬迁类型。
每个重定位符号只能重定位一两个编码等效指令。
在特定情况下,重定位本身甚至会更改指令类型。
在重定位期间计算的值被隐式扩展到 64 位,根据选择的重定位类型有符号或无符号。
AArch64 重定位
作为具有固定指令大小的 RISC 架构的 ARM,将全宽度(即 64 位)立即加载到寄存器中是非常重要的,因为没有指令可以具有全宽度立即字段。
AArch64 中的重定位也必须解决这个问题,它实际上是一个双重问题:首先,找到程序员打算使用的实际值(这是问题的纯重定位部分);其次,找到一种方法将其放入寄存器,因为没有指令具有 64 位立即数字段。
第二个问题通过使用组重定位得到解决,组中的每个重定位类型用于计算 64 位值的 16 位部分,因此只能有一组中的四种重定位类型(范围从 G0 到 G3)。
这种分成 16 位的切片符合 movk
(移动保持)、movz
(移动归零)和 movn
(逻辑移动否定)。
其他指令,如 b
、bl
、adrp
、adr
等,具有特别适合它们的重定位类型。
只要引用符号的给定指令只有一个因此明确的可能重定位类型,汇编程序就可以生成相应的条目,而无需程序员显式指定它。
组重定位不属于此类,它们的存在是为了让程序员有一定的灵活性,因此通常会明确说明。
在组中,重定位类型可以指定汇编程序是否必须执行溢出检查。
G0 重定位,用于加载值的低 16 位,除非明确禁止,否则检查该值是否适合 16 位(有符号或无符号,取决于使用的特定类型)。
G1 也是如此,加载位 31-16 检查值是否适合 32 位。
因此 G3 始终不检查,因为每个值都适合 64 位。
最后,重定位可用于将整数值加载到寄存器中。
实际上,一个符号的地址无非是一个任意整数常量。
请注意 r_addend
是 64 位宽。
1 如果 r_offset
指向数据部分中的站点,则计算值将在指示的位置写入 64 位字。
搬迁运营商
首先,一些参考资料:
约定
根据 ARM 文档约定,我们有:
S
is the runtime address of the symbol being relocated.
A
is the addend for the relocation.
P
is the address of the relocation site (derived fromr_offset
).
X
is the result of a relocation operation, before any masking or bit-selection operation is applied.
Page(expr)
is the page address of the expression expr, defined asexpr & ~0xFFF
, i.e.expr
with the lower 12 bits cleared.GOT
is the address of the Global Offset Table.
GDAT(S+A)
represents a 64-bit entry in the GOT for address S+A. The entry will be relocated at run time with relocation R_AARCH64_GLOB_DAT(S+A).
G(expr)
is the address of the GOT entry for the expression expr.
Delta(S)
resolves to the difference between the static link address ofS
and the execution address ofS
. IfS
is the null symbol (ELF symbol index 0), resolves to the difference between the static link address ofP
and the execution address ofP
.
Indirect(expr)
represents the result of callingexpr
as a function.
[msb:lsb]
is a bit-mask operation representing the selection of bits in a value, bounds are inclusive.
运算符
为了简洁起见,搬迁名称缺少前缀R_AARCH64_
。
类型 |X|≤2^16 的表达式意为 -2^16 ≤ X < 2^16, 注意右边的严格不等式。
这是对符号的滥用,由格式化 table.
组搬迁
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 | |
------------+-----------------+-----------+------+-----------+----------
在 table 中显示了 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 在值本身中指定。
它们总是只用于设置最高部分,因为符号只在那里相关。
他们总是检查有符号值是否溢出,使值变得更无意义。
这些重定位根据值的符号将指令类型更改为 movn
或 movz
,这有效地对值进行符号扩展。
组搬迁,也可以
PC-relative, 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_NC
,ldrh
使用 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
------------+-----------------+-----------+------+-----------+----------
结语
我找不到官方文档。
上面的 tables 是根据 GAS 测试用例和 ARM 文档重建的,该文档解释了可用于符合 AArch64 的 ELF 的重定位类型。
tables 没有显示 ARM 文档中存在的所有重定位,因为它们中的大多数是补充版本,由汇编程序自动选取。
有示例的部分会很棒,但我没有 ARM GAS。
将来我可能会扩展此答案以包括程序集列表和重定位转储的示例。