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

ysa*_*sap 50 c eclipse embedded gcc memory-management

我正在编程主机 - 加速器系统的主机端.主机在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部分中定义的符号`Mailbox'

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

Chr*_*ton 46

您试图以这样一种方式链接您的项目:相对寻址方案的目标远远超过所选相对寻址模式的32位位移所支持的目标.这可能是因为当前项目较大,因为它以不同的顺序链接目标文件,或者因为存在不必要的扩展映射方案.

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

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

这提供了一些治疗性建议.

  • 对错误消息的一般部分的网络搜索将我带到了这里! (10认同)
  • 这是一个建议:您可能在没有`-fPIC`的情况下意外构建了64位对象.这让我吵了一阵. (2认同)

Cir*_*四事件 20

生成错误的最小示例

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

_start:
    mov $_start, %eax
Run Code Online (Sandbox Code Playgroud)

linker.ld:

SECTIONS
{
    /* This says where `.text` will go in the executable. */
    . = 0x100000000;
    .text :
    {
        *(*)
    }
}
Run Code Online (Sandbox Code Playgroud)

在x86-64上编译:

as -o main.o main.S
ld -o main.out -T linker.ld main.o
Run Code Online (Sandbox Code Playgroud)

结果ld:

(.text+0x1): relocation truncated to fit: R_X86_64_32 against `.text'
Run Code Online (Sandbox Code Playgroud)

请记住:

  • as.text如果没有指定其他部分,则将所有内容放在其中
  • ld使用.text作为默认入口点if ENTRY.因此_start是第一个字节.text.

如何修复它:改用它linker.ld,然后从开头减去1:

SECTIONS
{
    . = 0xFFFFFFFF;
    .text :
    {
        *(*)
    }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我们不能_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
Run Code Online (Sandbox Code Playgroud)

如果我们在英特尔手册中查看指令的编码方式,我们会看到:

  • 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


小智 10

请记住按顺序处理错误消息.在我的情况下,上面的错误是"未定义的引用",我在视觉上跳过了更有趣的"重定位截断"错误.实际上,我的问题是一个旧库导致了"未定义的引用"消息.一旦我解决了这个问题,"重定位被截断"也会消失.


Ruf*_*ind 10

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

  • 我证实了这一点.您还必须使用`-fPIC`将其编译为库:https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/话题/ 268374#评论-1590873 (2认同)
  • 如果情况不太明显,请**不要使用`-mcmodel = medium`,因为在处理大型(-mlarge-data-threshold`时,它会使asm效率降低。默认为64kiB)静态/全局数组。首先寻找其他原因,例如尝试`-fPIC`。尚不清楚为什么超过2GB的* stack *与默认的-mcmodel = small不兼容,因为全局符号不引用堆栈内存,并且对于正常情况(-mcmodel)而言,堆栈已经在低2GiB之外。 = small`)可执行文件。参见https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html。 (2认同)

mid*_*nok 9

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


zwo*_*wol 9

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

char large_array[1ul << 31];
int other_global;
int main(void) { return other_global; }
Run Code Online (Sandbox Code Playgroud)

如果在默认模式下编译并且没有优化,则会在x86-64/Linux上产生"重定位被截断以适应"错误.(如果启用优化,至少在理论上,它可以确定large_array未使用和/或other_global永远不会写入,从而生成不会触发问题的代码.)

发生了什么,默认情况下,GCC在这种架构上使用其"小代码模型",其中所有程序的代码和静态分配的数据必须适合地址空间的最低2GB.(精确的上限是一样的东西2GB - 2MB,因为任何程序的地址空间的非常最低2MB是永久无法使用.如果你正在编译共享库或位置无关的可执行文件,所有的代码和数据的大小仍必须符合两成.千兆字节,但他们没有被钉在地址空间的底部了.) large_array消耗所有由本身的空间,所以other_global被分配上述限制的地址和生成的代码main不能达到它.你从链接器得到一个神秘的错误,而不是large_array编译器有用的" 太大"的错误,因为在更复杂的情况下,编译器无法知道这other_global将是遥不可及的,所以它甚至没有尝试简单的案例.

大多数情况下,获得此错误的正确响应是重构您的程序,以便它不需要巨大的静态数组和/或千兆字节的机器代码.但是,如果由于某种原因你真的必须使用它们,你可以使用"中"或"大"代码模型来提升限制,代价是效率稍低的代码生成.这些代码模型特定于x86-64; 对于大多数其他架构存在类似的东西,但确切的"模型"集和相关限制将有所不同.(例如,在32位体系结构中,您可能有一个"小"模型,其中代码和数据的总量限制为2 24个字节.)