这个海湾合作委员会错误“…重新定位截断适合…”是什么意思?

我正在编写主机加速器系统的主机端。 主机在Ubuntu Linux下的PC上运行,并通过USB连接与embedded式硬件通信。 通信是通过将内存块复制到embedded式硬件的内存中进行的。

在电路板的内存中有一个内存区域,我用它作为邮箱,在这里我写入和读取数据。 邮箱被定义为一个结构,我使用相同的定义在我的主机空间分配一个镜像邮箱。

过去,我成功地使用了这种技术,现在我将主机Eclipse项目复制到当前项目的工作空间中,并更改了相应的名称。 奇怪的是,当build立主机项目,我现在得到以下消息:

构build目标:fft2d_host
调用:GCC C链接器
gcc -L / opt / adapteva / esdk / tools / host / x86_64 / lib -o“fft2d_host”./src/fft2d_host.o -le_host -lrt

./src/fft2d_host.o:在函数`main'中:

fft2d_host.c :(。text + 0x280):重定位被截断为适合:R_X86_64_PC32,符号“Mailbox”在./src/fft2d_host.o中的COMMON部分中定义

这个错误意味着什么,为什么它不会build立在当前的项目上,而对于较老的项目来说,它是可以的。

您正尝试以这样的方式链接您的项目,即相对寻址scheme的目标远远超出所选相对寻址模式的32位偏移所支持的范围。 这可能是因为当前的项目比较大,因为它以不同的顺序链接目标文件,或者因为有一个不必要的扩展的映射scheme。

这个问题是一个很好的例子,为什么在一个错误信息的generics部分上进行networkingsearch往往是有成效的 – 你会发现像这样的东西:

http://www.technovelty.org/code/c/relocation-truncated.html

哪些提供了一些治愈性的build议。

生成错误的最小示例

main.S :将地址移入%eax (32位):

 _start: mov $_start, %eax 

linker.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' 

请记住:

  • 如果没有指定其他部分,则将所有内容放在.text
  • 如果ENTRY ld使用.text作为默认入口点。 因此_start.text第一个字节。

如何解决这个问题:改用这个linker.ld ,并从头开始减1:

 SECTIONS { . = 0xFFFFFFFF; .text : { *(*) } } 

笔记:

  • 在这个例子中,我们不能使用.global _start使_start全局,否则它仍然失败。 我认为这是因为全局符号具有alignment约束( 0xFFFFFFF0作品)。 TODO ELF标准中记载的地方在哪里?

  • .text段也具有p_align == 2M的alignment约束。 但是我们的链接器足够聪明,可以将段放在0xFFE00000 ,填充零直到0xFFFFFFFF并设置e_entry == 0xFFFFFFFF 。 这工作,但生成一个超大的可执行文件。

testingUbuntu 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

还有一些需要符号扩展而不是零扩展的重定位types,例如R_X86_64_32S 。 另见: https : //stackoverflow.com/a/33289761/895245

请记得按顺序处理错误消息。 在我的情况下,上面这个错误是“未定义的参考”,我直观地跳过了更有趣的“重定位截断”的错误。 事实上,我的问题是一个旧的库,导致“未定义的参考”消息。 一旦我解决了这个问题,“搬迁被截断”也就消失了。

我在构build需要大量堆栈空间(超过2 GiB)的程序时遇到了这个问题。 解决scheme是添加标志-mcmodel=medium ,这是由GCC和英特尔编译器支持。

在Cygwin -mcmodel=medium已经是默认的,并没有帮助。 向我join-Wl,--image-base -Wl,0x10000000到GCC链接器确实修正了错误。

通常,这个错误意味着你的程序太大 ,而且通常太大,因为它包含一个或多个非常大的数据对象。 例如,

 char large_array[1ul << 31]; int other_global; int main(void) { return other_global; } 

将在x86-64 / Linux上产生“重新定位被截断以适合”错误,如果以默认模式编译并且没有优化。 (如果你打开优化,至less在理论上可能会发现large_array被使用和/或other_global从不被写入,从而生成不会触发问题的代码。)

发生什么事情是,默认情况下,GCC在这个架构上使用它的“小代码模型”,其中所有程序的代码和静态分配的数据必须适合地址空间的最低2GB。 (精确的上限大约是2GB – 2MB,因为任何程序地址空间的最低2MB是永久不可用的。如果您正在编译共享库或位置无关的可执行文件,则所有的代码和数据都必须符合两个千兆字节,但是它们不再被钉在地址空间的底部) large_array消耗所有的空间,所以other_global被分配一个超出限制的地址,并且为main生成的代码无法到达它。 你从链接器中得到一个神秘的错误,而不是编译器的一个有用的“ large_array太大”的错误,因为在更复杂的情况下,编译器不能知道other_global是不可及的,所以它甚至不尝试为简单的情况。

大多数情况下,得到这个错误的正确答案是重构你的程序,使其不需要巨大的静态数组和/或千兆字节的机器代码。 但是,如果您由于某种原因必须拥有这些代码 ,则可以使用“中等”或“大”代码模型来提高限制,代价是代码生成效率稍低。 这些代码模型是x86-64特定的; 大多数其他体系结构也存在类似的情况,但“模型”和相关限制的具体设置会有所不同。 (例如,在32位体系结构中,您可能会有一个“小”模式,其中代码和数据的总量被限制为2 24个字节)。

Interesting Posts