我注意到用于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的较高部分给出。
一个自定义值,称为addend
该值最终用作表达式中的操作数,该表达式用于计算将被写入以修补指令的值。
RELA条目在r_addend中具有此值,REL条目从重定位站点中提取该值。
搬迁类型
这确定用于计算值以修补指令的表达式类型。它被编码在r_info的下部。


搬迁

在重定位阶段,加载程序会使用所有由r_offset下部选择的公式,遍历所有重定位条目并写入每个r_info指定的位置,以计算要从加数(r_addend用于RELA)和符号地址(可从r_info的上部获取)。

实际上,与其他架构相反,在其他架构中,指令的立即数字段通常与用于对操作进行编码的字节完全分开,从而简化了写入部分。在ARM中,即时值与其他编码信息混合在一起。
因此,加载器应该知道要尝试重定位的指令类型(如果完全是一条指令)1,而是由汇编程序根据指令设置重定位类型,而不是让它分解重定位位置。

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

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

AArch64搬迁

作为ARM一种具有固定指令大小的RISC体系结构,立即将全宽度(即64位)加载到寄存器中是很重要的,因为没有指令可以具有全宽度立即数字段。

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

第二个问题通过使用组重定位解决,组中的每个重定位类型都用于计算64位值的16位部分,因此,组中只能有四个重定位类型(范围从G0到G3)。

此切片分为16位,以适应movk(移动保持),movz(移动归零)和movn(在逻辑上进行求反)。
其他说明,例如bbladrpadr等,具有特别适合他们的重定位类型。

只要给定指令的引用符号只有一种(因此毫不含糊)可能的重定位类型,汇编器就可以生成相应的条目,而无需程序员明确指定它。

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

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



1如果r_offset指向数据节中的某个站点,则计算出的值将以64位字的形式写入所指示的位置。

搬迁公司

首先,一些参考:


描述ELF64格式的重定位类型的ARM文档为here,第4.6节
可能包含GAS可用的所有重定位运算符的AArch64测试程序集文件为here here


约定

遵循ARM文档约定,我们有:


S是要重定位的符号的运行时地址。
A
用于重定位的加数。
P是搬迁地点的地址
(源自r_offset)。
X是重定位的结果
在进行任何屏蔽或位选择操作之前
Page(expr)是表达式expr的页面地址,定义为
expr & ~0xFFF,即expr,低12位被清除。
GOT
Global 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位值,则必须在值本身中明确指定符号。
它们始终仅用于设置最高部分,因为符号仅在此处相关。
它们始终在检查,因为有符号值的溢出会使该值的含义变小。
这些重定位根据值的符号将指令的类型更改为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。
将来,我可能会扩展此答案,以包括程序集列表和重定位转储的示例。

关于assembly - AArch64重定位前缀,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38570495/

10-10 13:50