Jee*_*enu 7 assembly relocation
我注意到ARM 64位程序集的GNU asm 重定位语法.什么是喜欢那件#:abs_g0_nc:和:pg_hi21:?他们在哪里解释?他们是否有一种模式,或者他们是否在旅途中弥补?我在哪里可以了解更多?
Mar*_*oom 12
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;
Run Code Online (Sandbox Code Playgroud)
每个重定位条目的范围是为加载器(静态或动态)提供四条信息:
要修补的指令的虚拟地址或偏移量.
这是由r_offset.
访问的符号的运行时地址.
这是由较高的部分给出的r_info.
一个名为addend的自定义值此
值最终作为表达式中的操作数,用于计算将写入以修补指令的值.
RELA条目具有此值r_addend,REL条目从重定位站点提取它.
重定位类型
这决定了用于计算修补指令的值的表达式类型.这是编码在下半部分r_info.
在重定位阶段,加载程序遍历所有重定位条目并写入每个指定的位置r_offset,使用下部选择的公式r_info计算要从加数(r_addend对于RELA)和符号地址(可获得)存储的值从上部r_info).
实际上,写入部分已被简化,与其他架构相反,其中指令的直接字段通常与用于编码操作的字段完全分开,在ARM中,立即值与其他编码信息混合.
所以装载机应该知道什么样的指令是试图重新定位,如果是在所有的指令1,但不是让它拆卸搬迁的网站,它是根据指令集的重定位类型的汇编.
每个重定位符号只能重定位一个或两个编码等效的指令.
在特定情况下,重定位本身甚至会改变指令的类型.
根据所选的重定位类型,在重定位期间计算的值计算被隐式扩展为64位,有符号或无符号.
作为ARM具有固定指令大小的RISC架构,将全宽(即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是要重定位的符号的运行时地址.
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 | |
------------+-----------------+-----------+------+-----------+----------
Run Code Online (Sandbox Code Playgroud)
在表中显示了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
Run Code Online (Sandbox Code Playgroud)
通常使用一个检查操作符,即设置最高部分的操作符.
这就是为什么movz只检查版本重定位,而非检查版本重定位movk(部分设置寄存器).
G3重新定位,因为它本质上没有检查,因为没有值可以超过64位.
签名版本结束,_s他们总是检查.
没有G3版本,因为如果使用64位值,则在值本身中指定了符号.
它们总是仅用于设置最高部分,因为符号仅在那里相关.
它们总是在签名值中检查溢出,使值意义更小.
这些重定位将指令的类型更改为movn或movz基于值的符号,这有效符号扩展了值.
也可以进行团体搬迁
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 | | | |
Run Code Online (Sandbox Code Playgroud)
的: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
------------+-----------------+-----------+------+-----------+----------
Run Code Online (Sandbox Code Playgroud)
我找不到官方文件.
上表是根据GAS测试用例和ARM文档重建的,该文档解释了符合AArch64标准的ELF可用的重定位类型.
这些表没有显示ARM文档中存在的所有重定位,因为大多数都是互补版本,由汇编程序自动获取.
带示例的部分会很棒,但我没有ARM GAS.
在将来,我可以扩展这个答案,包括汇编列表和重定位转储的示例.