我正在编程主机 - 加速器系统的主机端。主机在Ubuntu Linux下的PC上运行,并通过USB连接与嵌入式硬件通信。通过将内存块复制到嵌入式硬件的内存中来执行通信。
在主板的内存中有一个内存区域,我将其用作邮箱,用于写入和读取数据。邮箱定义为结构,我使用相同的定义在主机空间中分配镜像邮箱。
我过去成功使用过这种技术,所以现在我将主机Eclipse项目复制到我当前项目的工作区,并进行了相应的名称更改。奇怪的是,在构建主机项目时,我现在收到以下消息:
建筑目标:fft2d_host
调用:GCC C Linker
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针对./src/fft2d_host.o中的COMMON部分中定义的符号`Mailbox'
这个错误意味着什么,以及为什么它不会建立在当前项目上,而旧版项目是否正常?
答案 0 :(得分:45)
您试图以这样的方式链接您的项目:相对寻址方案的目标比所选相对寻址模式的32位位移所支持的更远。这可能是因为当前项目较大,因为它以不同的顺序链接目标文件,或者因为存在不必要的扩展映射方案。
这个问题是一个很好的例子,说明为什么在错误信息的通用部分进行网络搜索通常很有效率 - 你会发现这样的事情:
http://www.technovelty.org/code/c/relocation-truncated.html
这提供了一些治疗性建议。
答案 1 :(得分:19)
生成错误的最小示例
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'
请记住:
as
会将所有内容都放在.text
上ld
使用.text
作为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
。
有些类型的重定位需要 sign 扩展而不是零扩展,如下所示,例如R_X86_64_32S
。另见:https://stackoverflow.com/a/33289761/895245
答案 2 :(得分:10)
请记住按顺序处理错误消息。在我的情况下,上面的错误是&#34;未定义的引用&#34;,我在视觉上跳过了更有趣的&#34;重定位截断&#34;错误。事实上,我的问题是一个旧的库导致&#34;未定义的引用&#34;信息。一旦我解决了这个问题,&#34;重定位被截断了#34;也离开了。
答案 3 :(得分:9)
我在构建需要大量堆栈空间(超过2 GiB)的程序时遇到了这个问题。解决方案是添加标志-mcmodel=medium
,GCC和英特尔编译器都支持该标志。
答案 4 :(得分:8)
On Cygwin -mcmodel=medium
已经默认无效。对我来说,将-Wl,--image-base -Wl,0x10000000
添加到GCC链接器确实修复了错误。
答案 5 :(得分:6)
通常,此错误意味着您的程序太大,并且通常它太大,因为它包含一个或多个非常大的数据对象。例如,
char large_array[1ul << 31];
int other_global;
int main(void) { return other_global; }
将产生一个&#34;重定位被截断以适应&#34; x86-64 / Linux上的错误,如果在默认模式下编译而没有优化。 (如果启用优化,至少在理论上,它可以确定large_array
未使用和/或永远不会写other_global
,因此生成的代码不会触发问题。)
正在发生的事情是,默认情况下,GCC使用其小代码模型&#34;在这种体系结构中,所有程序的代码和静态分配的数据必须适合地址空间的最低2GB。 (精确的上限类似于2GB - 2MB,因为任何程序的地址空间中最低的2MB永久不可用。如果您正在编译共享库或与位置无关的可执行文件,则所有代码和数据必须仍然适合2千兆字节,但它们不再固定在地址空间的底部。)large_array
自己消耗所有空间,因此other_global
被分配了一个超出限制的地址,为main
生成的代码无法访问它。你从链接器中得到一个神秘的错误,而不是一个有用的&#34; large_array
太大&#34;来自编译器的错误,因为在更复杂的情况下,编译器不能知道other_global
是不可及的,因此它甚至不会尝试简单的情况。
大多数情况下,获得此错误的正确答案是重构您的程序,以便它不需要巨大的静态数组和/或千兆字节的机器代码。但是,如果由于某种原因你真的必须使用它们,你可以使用"medium" or "large" code models来提升限制,代价是效率稍低的代码生成。这些代码模型特定于x86-64;大多数其他架构都存在类似的东西,但确切的一组&#34;模型&#34;相关限制将有所不同。 (例如,在32位体系结构中,您可能有一个&#34;小型#34;模型,其中代码和数据的总量限制为2 24 字节。)
答案 6 :(得分:0)
我遇到了完全相同的问题。在没有-fexceptions
生成标志的情况下进行编译后,文件编译没有问题