2011-05-23 99 views
33

得到了下面的错误,当我试图编译在64位的FreeBSD C应用:创建共享对象时R_X86_64_32S和R_X86_64_64重定位是什么意思?

搬迁R_X86_64_32S不能使用;与-fPIC

什么是R_X86_64_32S搬迁,什么是R_X86_64_64重新编译?

我已经搜索了关于错误的信息,这可能是因为 - 如果有人能够说出R_X86_64_32S的真正含义,那将会很棒。

+1

相关:Linux发行版最近已经开始启用**与位置无关的可执行文件**默认情况下,海湾合作委员会。如果试图将任何非PIC代码链接到可执行文件中,包括使用诸如'mov $ symbol,%edi'而不是'lea symbol(%rip),%rdi'之类的asm,你将会得到这个错误。请参阅[在x86-64 Linux中不再允许32位绝对地址?](https://stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64- linux)获取使用'gcc -no-pie -fno-pie'制作传统的依赖于位置的可执行文件的信息(如果这是你想要的)。 – 2017-11-05 07:45:03

回答

28

R_X86_64_32SR_X86_64_64是重定位类型的名称,用于为amd64体系结构编译的代码。您可以在amd64 ABI中查看所有这些。 根据它,R_X86_64_64被分解为:

  • R_X86_64 - 直接64位搬迁

R_X86_64_32S到 - 所有的名字都与此

  • 64前缀:

    • R_X86_64 - 前缀
    • 32S - 截断值为32位并签名扩展

    这两种情况下的基本意思是“此重定位指向的符号的值加上任何加数”。对于R_X86_64_32S,链接器会验证生成的值是否符号扩展到原始的64位值。

    现在,在一个可执行文件中,代码段和数据段被赋予一个指定的虚拟基地址。可执行代码不共享,每个可执行文件都有自己的新地址空间。这意味着编译器确切知道数据部分的位置,并且可以直接引用它。另一方面,图书馆只能知道他们的数据部分将在基地址的指定偏移处;该基地址的值只能在运行时才知道。因此,所有的库都必须使用可以执行的代码来生成,而不管它被放到内存中的位置,称为位置无关代码(简称PIC)。

    现在谈到解决您的问题时,错误信息是不言自明的。

  • 2

    这意味着,编译一个共享对象,而无需使用-fPIC标志,你应该:

    gcc -shared foo.c -o libfoo.so # Wrong 
    

    你需要调用

    gcc -shared -fPIC foo.c -o libfoo.so # Right 
    

    在ELF平台(Linux)的共享对象与位置无关编译代码 - 可以从内存中的任何位置运行的代码,如果未给出该标志,则生成的代码与位置有关,因此无法使用此共享对象。

    +0

    我有同样的问题,但即使添加了-fPIC,我仍然得到错误:'gcc -std = c99 -Wall -pedantic -shared -fopenmp -fPIC -static test.c -o libtest.so'。任何想法为什么?谢谢! – 2014-12-10 00:38:59

    2

    我遇到了这个问题,发现这个答案没有帮助我。我试图将静态库与共享库链接在一起。我也调查过在命令行上放置-fPIC开关(正如其他地方的建议)。 解决问题的唯一方法就是将静态库更改为共享。我怀疑有关-fPIC的错误信息可能会由于许多原因而发生,但基本上你要看的是你的库的构建方式,以及对以不同方式构建的库感到怀疑。

    +0

    它是有道理的,一个静态库将被建立而不用'-fPIC',因此会使用一些绝对的重定位。因此您不能将该代码链接到可重定位的共享对象。 – 2017-11-05 07:40:24

    15

    对于任何这是有道理的,你必须首先:

    标准

    R_X86_64_64R_X86_64_32R_X86_64_32S均由System V AMD ABI定义,其中包含ELF文件格式的AMD64细节。

    它们都是重定位条目的ELF32_R_TYPE字段的所有可能值,在System V ABI 4.1 (1997)中指定,它指定ELF格式的架构中立部分。该标准只规定了字段,但不是它与拱相关的值。

    在4.4.1“重定位类型”,我们看到的汇总表:

    Name   Field Calculation 
    ------------ ------ ----------- 
    R_X86_64_64 word64 A + S 
    R_X86_64_32 word32 A + S 
    R_X86_64_32S word32 A + S 
    

    我们稍后会解释这个表。

    和NOTE:

    R_X86_64_32R_X86_64_32S重定位截断计算值到32位。链接器必须验证R_X86_64_32(R_X86_64_32S)重定位的生成值为零扩展(符号扩展)为原始64位值。 R_X86_64_64和R_X86_64_32的

    让我们先来看看为R_X86_64_64R_X86_64_32

    .section .text 
        /* Both a and b contain the address of s. */ 
        a: .long s 
        b: .quad s 
        s: 
    

    然后:

    as --64 -o main.o main.S 
    objdump -dzr main.o 
    

    包含:

    0000000000000000 <a>: 
        0: 00 00     add %al,(%rax) 
             0: R_X86_64_32 .text+0xc 
        2: 00 00     add %al,(%rax) 
    
    0000000000000004 <b>: 
        4: 00 00     add %al,(%rax) 
             4: R_X86_64_64 .text+0xc 
        6: 00 00     add %al,(%rax) 
        8: 00 00     add %al,(%rax) 
        a: 00 00     add %al,(%rax) 
    

    测试Ubuntu 14.04,Binutils 2.24。

    现在忽略反汇编(这是没有意义的,因为这是数据),并且只查看标签,字节和重定位。

    第一重定位:

    0: R_X86_64_32 .text+0xc 
    

    这意味着:

    • 0:作用于字节0(标签a
    • R_X86_64_:由所有重定位类型的AMD64系统Ⅴ的用于前缀ABI
    • 32:标签s的64位地址是trunca泰德到一个32位的地址,因为我们仅指定了.long(4字节)
    • .text:我们对.text
    • 0xc:这是加数,这是重新定位条目的字段

    搬迁的地址的计算公式为:

    A + S 
    

    其中:

    • A:加数,这里0xC
    • S:重定位之前的符号的值,这里00 00 00 00 == 0

    因此,重定位之后,新的地址将是位于0xC == 12个字节进入.text部分。

    这正是我们所期望的,因为s而来的.long(4个字节)后和.quad(8个字节)。

    R_X86_64_64是类似的,但更简单,因为这里不需要截取s的地址。这通过word64而不是word32列在Field列标准中指示。

    R_X86_64_32S VS R_X86_64_32

    VS R_X86_64_32R_X86_64_32S之间的区别是,当链接器会抱怨 “与搬迁截断以适应”:

    • 32:如果截断后搬迁值不抱怨不是零扩展旧值,即截断的字节必须为零:

      例如:FF FF FF FF 80 00 00 0080 00 00 00生成投诉,因为FF FF FF FF不为零。

    • 32S:抱怨如果截断后重定位值不是sign extend旧值。

      E.g.:FF FF FF FF 80 00 00 0080 00 00 00是好的,因为80 00 00 00的最后一位和截断位数均为1

    参见:

    .section .text 
    .global _start 
    _start: 
        mov s, %eax 
        s: 
    

    然后:What does this GCC error "... relocation truncated to fit..." mean?

    R_X86_64_32S可以生成:

    as --64 -o main.o main.S 
    objdump -dzr main.o 
    

    给出:

    0000000000000000 <_start>: 
        0: 8b 04 25 00 00 00 00 mov 0x0,%eax 
             3: R_X86_64_32S .text+0x7 
    

    现在,我们可以观察到 “搬迁” 截断,以适应32S与链接脚本:

    SECTIONS 
    { 
        . = 0xFFFFFFFF80000000; 
        .text : 
        { 
         *(*) 
        } 
    } 
    

    现在:

    ld -Tlink.ld a.o 
    

    是好的,这是因为:0xFFFFFFFF80000000被截断为80000000,这是一个符号扩展。

    但是,如果我们改变链接脚本:

    . = 0xFFFF0FFF80000000; 
    

    现在产生的错误,因为这0使得它不会是一个符号扩展了。

    为什么要使用32S内存访问,但32为立即数:When is it better for an assembler to use sign extended relocation like R_X86_64_32S instead of zero extension like R_X86_64_32?

    1

    在我的情况下,问题的出现是因为要编译有望程序查找共享库在远程目录,而只有对应的静态库在那里在一个错误。

    实际上,这个重定位错误是一个伪装文件未发现的错误。

    我已经详细讲述了我与它在这个其他线程应对https://stackoverflow.com/a/42388145/5459638