这个 GCC 错误“... relocation truncated to fit...”是啥意思?

Posted

技术标签:

【中文标题】这个 GCC 错误“... relocation truncated to fit...”是啥意思?【英文标题】:What does this GCC error "... relocation truncated to fit..." mean?这个 GCC 错误“... relocation truncated to fit...”是什么意思? 【发布时间】:2012-05-16 04:46:39 【问题描述】:

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

在板的内存中有一个内存区域,我将其用作邮箱,我可以在其中写入和读取数据。邮箱被定义为一个结构,我使用相同的定义在我的宿主空间中分配一个镜像邮箱。

我过去成功地使用了这种技术,所以现在我将宿主 Eclipse 项目复制到我当前项目的工作区,并进行了适当的名称更改。奇怪的是,在构建宿主项目时,我现在收到以下消息:

构建目标: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 对 ./src/fft2d_host.o 中 COMMON 部分中定义的符号“邮箱”

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

【问题讨论】:

【参考方案1】:

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

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

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

这提供了一些治疗建议。

【讨论】:

这里有一个建议:你可能会不小心在没有-fPIC 的情况下构建 64 位对象。这让我绊倒了一段时间。 对错误消息的通用部分的网络搜索将我带到了这里!【参考方案2】:

生成错误的最小示例

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

main.S

_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 上 如果ENTRYld 使用.text 作为默认入口点。因此_start.text 的第一个字节。

如何解决:改用 linker.ld,并从开头减去 1:

SECTIONS

    . = 0xFFFFFFFF;
    .text :
    
        *(*)
    

注意事项:

在这个例子中,我们不能用.global _start 使_start 全局化,否则它仍然会失败。我认为这是因为全局符号具有对齐约束(0xFFFFFFF0 有效)。 TODO 在 ELF 标准中记录在哪里?

.text 段也有一个对齐约束p_align == 2M。但是我们的链接器足够聪明,可以将段放在0xFFE00000,用零填充直到0xFFFFFFFF,然后设置e_entry == 0xFFFFFFFF。这可行,但会生成一个过大的可执行文件。

在 Ubuntu 14.04 AMD64、Binutils 2.24 上测试。

说明

首先你必须通过一个最小的例子来理解什么是重定位:https://***.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://***.com/a/33289761/895245

R_AARCH64_PREL32

提问于:How to prevent "main.o:(.eh_frame+0x1c): relocation truncated to fit: R_AARCH64_PREL32 against `.text'" when creating an aarch64 baremetal program?

【讨论】:

如果您正在编写程序集,那么获取 objdump 提供的信息的另一种方法是使用程序集列表文件。在您的汇编命令中包含 -al=(file) ,如下所示:as ... test.s -al=test.lst 另外,请考虑相对地址的来源。在某些情况下,它不是指令的地址,它可能是下一条指令的地址。由于这个原因,在 6502 程序集中,BEQ $ 编码为 F0 FE。【参考方案3】:

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

【讨论】:

@garyp 我不知道 midenok 是怎么想出来的,但我看到 0x1000 0000 是 32 位 DLL 的 MS 默认基地址,而 0x1 8000 0000 是 64 位的默认基地址位 DLL:MS linker /BASE option docs【参考方案4】:

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

【讨论】:

我确认这一点。您还必须不使用 -fPIC 将其编译为库:software.intel.com/en-us/forums/… 如果这不是很明显,如果不需要,请不要使用-mcmodel=medium,因为它在处理大型 ( -mlarge-data-threshold 默认为 64kiB) 静态/全局数组。首先寻找其他原因,例如试试-fPIC。不清楚为什么超过 2GB 的 stack 会与默认的 -mcmodel=small 不兼容,因为全局符号不引用堆栈内存,并且堆栈已经超出了正常的低 2GiB (@ 987654330@) 可执行文件。见gcc.gnu.org/onlinedocs/gcc/x86-Options.html。 你在 Cygwin 上遇到过这个吗? 不记得了,但可能不记得了。【参考方案5】:

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

【讨论】:

我也是:问题是过时的 .o 文件。标头引用了不存在的功能。重新编译,没关系,我猜链接器在这些情况下决定位置“非常大”而不是“不存在”:) 遗憾的是,一个未解析的符号经常会产生这种“有趣”但信息量不足的信息。处理工具链失败的启发式方法是,如果您在修复所有未定义符号之后收到此消息,那么您将遇到真正的截断问题。在大师级别,您可能能够将一些截断错误与特定的未定义符号相关联,并成功治愈那些与未定义符号相关的错误。【参考方案6】:

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

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_array is too large”错误,因为在更复杂的情况下,编译器无法知道other_global 将是遥不可及的,所以它不会甚至不要尝试简单的情况。

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

【讨论】:

谢谢。如问题中所述,这不是 x86 代码。这是很久以前的事了,但无论如何,我不确定代码模型大小与嵌入式处理器有何关系?这不是 x86 特定的限制/属性吗? @ysap 每个 CPU 架构都有限制like - 通常由适合某些机器指令的立即操作数的位数设置。我写了这个答案是因为一个更新的问题作为这个问题的副本被关闭了,我认为现有的答案不能很好地解决那个人的问题,我只使用 x86-64 作为一个方便的例子。 (不过,问题中的错误消息包含术语 R_X86_64_PC32,这听起来确实像是您正在为 x86-64 编译代码。也许您真正的问题是您的 makefile 没有调用它应该具有的交叉编译器。) 我肯定是在交叉编译,但是,嘿,即使那家公司也不复存在了……谢谢您的意见。【参考方案7】:

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

【讨论】:

以上是关于这个 GCC 错误“... relocation truncated to fit...”是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

eclipse编译动态链接库文件报错 relocation R_X86_64_32 against `.data' can not be used when making a shared o

relocation R_X86_64_32 against `.rodata‘ can not be used when making a shared object; recompile with

POJ-2923 Relocation---01背包+状态压缩

C/C++链接静态库报错:dangerous relocation: unsupported relocation(-fPIC)

C/C++链接静态库报错:dangerous relocation: unsupported relocation(-fPIC)

Relocation POJ - 2923(01背包+状压dp)