60

我正在对主机加速器系统的主机端进行编程。主机在 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): relocation truncated to fit: R_X86_64_PC32 对 ./src/fft2d_host.o 中 COMMON 部分中定义的符号“邮箱”

这个错误是什么意思,为什么它不会在当前项目上构建,而在旧项目上是可以的?

4

7 回答 7

55

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

这个问题是一个完美的例子,说明为什么对错误消息的通用部分进行网络搜索通常很有成效——你会发现这样的事情:

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

这提供了一些治疗建议。

于 2012-05-07T17:16:26.260 回答
28

产生错误的最小示例

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

电源

_start:
    mov $_start, %eax

链接器.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用作默认入口点 if 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.

并且有一些类型的重定位需要符号扩展而不是零扩展,如此处所示,例如R_X86_64_32S。另请参阅:https ://stackoverflow.com/a/33289761/895245

R_AARCH64_PREL32

提问:创建 aarch64 裸机程序时如何防止“main.o:(.eh_frame+0x1c): relocation truncated to fit: R_AARCH64_PREL32 against `.text'”?

于 2015-09-17T20:36:43.710 回答
14

在 Cygwin-mcmodel=medium上已经是默认设置并且没有帮助。对我来说,添加-Wl,--image-base -Wl,0x10000000到 GCC 链接器确实修复了错误。

于 2016-06-21T03:48:38.023 回答
11

请记住按顺序处理错误消息。在我的例子中,这个错误上面的错误是“未定义的引用”,我在视觉上跳过了它到更有趣的“重定位截断”错误。事实上,我的问题是一个导致“未定义引用”消息的旧库。一旦我解决了这个问题,“重定位被截断”也消失了。

于 2014-12-30T16:45:28.343 回答
11

我在构建需要大量堆栈空间(超过 2 GiB)的程序时遇到了这个问题。解决方案是添加-mcmodel=mediumGCC 和 Intel 编译器都支持的标志。

于 2016-01-31T07:03:04.143 回答
10

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

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

如果在默认模式下编译并且没有优化,将在 x86-64/Linux 上产生“relocation truncated to fit”错误。(如果您打开优化,至少在理论上,它可以找出large_array未使用和/或other_global从未编写过的代码,从而生成不会触发问题的代码。)

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

大多数时候,对出现此错误的正确响应是重构您的程序,以便它不需要巨大的静态数组和/或千兆字节的机器代码。但是,如果您出于某种原因确实必须拥有它们,您可以使用“中型”或“大型”代码模型来提升限制,但代价是代码生成效率稍低。这些代码模型是特定于 x86-64 的;大多数其他架构也存在类似的情况,但确切的“模型”集和相关限制会有所不同。(例如,在 32 位架构上,您可能有一个“小型”模型,其中代码和数据的总量被限制在 2 24字节。)

于 2017-11-07T21:55:22.047 回答
0

我遇到了完全相同的问题。在没有-fexceptions构建标志的情况下编译后,文件编译没有问题

于 2020-11-03T10:17:34.943 回答