Relocation truncated to fit?

2018-08-06  本文已影响258人  罗蓁蓁

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参数。

上一篇 下一篇

猜你喜欢

热点阅读