这个GCC错误“......重定位被截断以适应......”是什么意思?

时间:2012-05-07 17:08:03

标签: c eclipse memory-management gcc embedded

我正在编程主机 - 加速器系统的主机端。主机在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'

这个错误意味着什么,以及为什么它不会建立在当前项目上,而旧版项目是否正常?

7 个答案:

答案 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生成标志的情况下进行编译后,文件编译没有问题