要使这一切有意义,您必须首先:
标准
R_X86_64_64、R_X86_64_32 和 R_X86_64_32S 均由 System V AMD ABI 定义,其中包含 ELF 文件格式的 AMD64 细节。
它们都是重定位条目的ELF32_R_TYPE 字段的所有可能值,在System V ABI 4.1 (1997) 中指定,它指定ELF 格式的体系结构中立部分。该标准仅指定字段,但不指定依赖于拱门的值。
在 4.4.1 “重定位类型”下,我们看到了汇总表:
Name Field Calculation
------------ ------ -----------
R_X86_64_64 word64 A + S
R_X86_64_32 word32 A + S
R_X86_64_32S word32 A + S
我们稍后会解释这个表。
注意事项:
R_X86_64_32 和 R_X86_64_32S 重定位将计算值截断为 32 位。链接器必须验证生成的 R_X86_64_32 (R_X86_64_32S) 重定位值零扩展(符号扩展)到原始 64 位值。
R_X86_64_64 和 R_X86_64_32 示例
我们先来看看R_X86_64_64和R_X86_64_32:
.section .text
/* Both a and b contain the address of s. */
a: .long s
b: .quad s
s:
然后:
as --64 -o main.o main.S
objdump -dzr main.o
包含:
0000000000000000 <a>:
0: 00 00 add %al,(%rax)
0: R_X86_64_32 .text+0xc
2: 00 00 add %al,(%rax)
0000000000000004 <b>:
4: 00 00 add %al,(%rax)
4: R_X86_64_64 .text+0xc
6: 00 00 add %al,(%rax)
8: 00 00 add %al,(%rax)
a: 00 00 add %al,(%rax)
在 Ubuntu 14.04、Binutils 2.24 上测试。
暂时忽略反汇编(这是没有意义的,因为这是数据),只查看标签、字节和重定位。
第一次搬迁:
0: R_X86_64_32 .text+0xc
这意味着:
-
0:作用于字节 0(标签 a)
-
R_X86_64_:AMD64系统V ABI的所有重定位类型使用的前缀
-
32:标签s的64位地址被截断为32位地址,因为我们只指定了.long(4字节)
-
.text:我们在.text 部分
-
0xc:这是addend,是重定位入口的字段
重定位的地址计算为:
A + S
地点:
-
A:加数,这里是0xC
-
S:重定位前的符号值,这里是00 00 00 00 == 0
因此,重定位后,新地址将是 0xC == 12 个字节进入.text 部分。
这正是我们所期望的,因为s 位于.long(4 字节)和.quad(8 字节)之后。
R_X86_64_64 类似,但更简单,因为这里不需要截断s 的地址。这由标准通过word64 而非Field 列上的word32 表示。
R_X86_64_32S 与 R_X86_64_32
R_X86_64_32S 与 R_X86_64_32 之间的区别在于链接器何时会抱怨“重定位被截断以适应”:
-
32:如果重定位后截断值不为零扩展旧值,则抱怨,即截断字节必须为零:
例如:FF FF FF FF 80 00 00 00 到 80 00 00 00 会生成投诉,因为 FF FF FF FF 不为零。
-
32S:如果重定位后截断的值不是sign extend旧值,则抱怨。
例如:FF FF FF FF 80 00 00 00 到 80 00 00 00 很好,因为 80 00 00 00 的最后一位和截断的位都是 1。
另见:What does this GCC error "... relocation truncated to fit..." mean?
R_X86_64_32S 可以通过以下方式生成:
.section .text
.global _start
_start:
mov s, %eax
s:
然后:
as --64 -o main.o main.S
objdump -dzr main.o
给予:
0000000000000000 <_start>:
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
3: R_X86_64_32S .text+0x7
现在我们可以观察到使用链接描述文件截断以适应32S 的“重定位”:
SECTIONS
{
. = 0xFFFFFFFF80000000;
.text :
{
*(*)
}
}
现在:
ld -Tlink.ld a.o
没问题,因为:0xFFFFFFFF80000000 被截断为 80000000,这是一个符号扩展。
但如果我们将链接器脚本更改为:
. = 0xFFFF0FFF80000000;
它现在生成错误,因为 0 使它不再是符号扩展。
使用32S 进行内存访问但32 用于立即数的理由:When is it better for an assembler to use sign extended relocation like R_X86_64_32S instead of zero extension like R_X86_64_32?
R_X86_64_32S 和 PIE(位置无关的可执行文件
R_X86_64_32S 不能用于位置无关的可执行文件,例如使用gcc -pie 完成,否则链接失败:
relocation R_X86_64_32S against `.text' can not be used when making a PIE object; recompile with -fPIC
l
我提供了一个最小的例子来解释它:What is the -fPIE option for position-independent executables in gcc and ld?