Relocation truncated to fit?
如果你在x86-64上编码的时间足够长,你最终会遇到如下错误:
(.text+0x3): relocation truncated to fit: R_X86_64_32S against symbol `array' defined in foo section in ./pcrel8.o
这里有一个小例子可以帮助你弄清楚你做错了什么。
请考虑以下代码:
$ cat foo.s
.globl foovar
.section foo, "aw",@progbits
.type foovar, @object
.size foovar, 4
foovar:
.long 0
.text
.globl _start
.type function, @function
_start:
movq $foovar, %rax
如果不清楚,她也像源码文件:
int foovar = 0;
void function(void) {
int *bar = &foovar;
}
让我们构建该代码,看看它是什么样的:
gcc -c foo.s
$ objdump --disassemble-all ./foo.o
./foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: 48 c7 c0 00 00 00 00 mov $0x0,%rax
Disassembly of section foo:
0000000000000000 <foovar>:
0: 00 00 add %al,(%rax)
...
我们可以看到mov指令只为链接器分配了4个字节(00 00 00 00)以放入foovar的地址。如果我们检查重定位:
$ readelf --relocs ./foo.o
Relocation section '.rela.text' at offset 0x3a0 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000003 00050000000b R_X86_64_32S 0000000000000000 foovar + 0
该R_X86_64_32S
搬迁的确是只有32位的搬迁。现在我们可以发现这个错误。请考虑以下链接器脚本,它将foo部分放在距离代码大约5千兆字节的位置。
$ cat test.lds
SECTIONS
{
. = 10000;
.text : { *(.text) }
. = 5368709120;
.data : { *(.foo) }
}
这意味着我们无法在重定位分配的空间内放置foovar的地址。当我们尝试时:
ld -Ttest.lds ./foo.o
./foo.o: In function `_start':
(.text+0x3): relocation truncated to fit: R_X86_64_32S against symbol `foovar' defined in foo section in ./foo.o
这意味着foovar的完整64位地址(现在位于5千兆字节以上)无法在为其分配的32位空间内表示。
出于代码优化的目的,mov指令的默认即时大小 是32位值。这是有道理的,因为在大多数情况下,程序可以愉快地存在于32位地址空间中,并且人们不会做一些事情,比如让他们的数据远离他们的代码,它需要的不仅仅是一个32位的地址来表示它。因此,默认使用32位immediate会大大减少代码大小,因为您不必为每个mov腾出64位立即空间。
所以,如果你想真正的移动完整的64位立即到寄存器中,你想要的movabs指令。尝试使用上面的代码 - 使用movabs你应该得到一个R_X86_64_64
重定位和64位的空间来修补地址。
如果你看到这个并且你不是手工编码,你可能想要查看gcc的-mcmodel参数。
网友评论