这个海湾合作委员会错误“…重新定位截断适合…”是什么意思?
我正在编写主机加速器系统的主机端。 主机在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个字节)。