产生错误的最小示例
main.S
将地址移入%eax
(32 位)。
电源
_start:
mov $_start, %eax
链接器.ld
SECTIONS
{
/* This says where `.text` will go in the executable. */
. = 0x100000000;
.text :
{
*(*)
}
}
在 x86-64 上编译:
as -o main.o main.S
ld -o main.out -T linker.ld main.o
结果ld
:
(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text'
请记住:
as
.text
如果没有指定其他部分,则将所有内容放在
ld
将.text
用作默认入口点 if ENTRY
。因此_start
是 的第一个字节.text
。
如何修复它:改用它linker.ld
,并从一开始减去 1:
SECTIONS
{
. = 0xFFFFFFFF;
.text :
{
*(*)
}
}
笔记:
_start
在这个例子中,我们不能用全局化.global _start
,否则它仍然会失败。我认为这是因为全局符号具有对齐约束(0xFFFFFFF0
有效)。TODO 在 ELF 标准中记录在哪里?
该.text
段也有一个对齐约束p_align == 2M
。但是我们的链接器足够聪明,可以将段放置在 处0xFFE00000
,用零填充直到0xFFFFFFFF
并设置e_entry == 0xFFFFFFFF
。这可行,但会生成一个过大的可执行文件。
在 Ubuntu 14.04 AMD64、Binutils 2.24 上测试。
解释
首先,您必须通过一个最小的示例了解什么是重定位:https ://stackoverflow.com/a/30507725/895245
接下来,看看objdump -Sr main.o
:
0000000000000000 <_start>:
0: b8 00 00 00 00 mov $0x0,%eax
1: R_X86_64_32 .text
如果我们查看英特尔手册中指令的编码方式,我们会看到:
b8
说这是mov
一个%eax
0
是要移动到 的立即值%eax
。然后重定位将修改它以包含 的地址_start
。
移动到 32 位寄存器时,立即数也必须是 32 位的。
但是在这里,重定位必须修改那些 32 位以_start
在链接发生后将地址放入其中。
0x100000000
不适合 32 位,但0xFFFFFFFF
可以。因此错误。
此错误只会发生在产生截断的重定位上,例如R_X86_64_32
(8 字节到 4 字节),但绝不会发生在R_X86_64_64
.
并且有一些类型的重定位需要符号扩展而不是零扩展,如此处所示,例如R_X86_64_32S
。另请参阅:https ://stackoverflow.com/a/33289761/895245
R_AARCH64_PREL32
提问:创建 aarch64 裸机程序时如何防止“main.o:(.eh_frame+0x1c): relocation truncated to fit: R_AARCH64_PREL32 against `.text'”?