R_X86_64_32S和R_X86_64_64重定位是什么意思?

时间:2011-05-23 06:23:24

标签: c compiler-construction linker elf relocation

当我尝试在64位FreeBSD中编译C应用程序时出现以下错误:

  

重定位R_X86_64_32S在制作共享对象时不能使用;用-fPIC重新编译

什么是R_X86_64_32S重定位,什么是R_X86_64_64

我已经搜索了错误,这可能是原因 - 如果有人能说出R_X86_64_32S的真正含义,那就太好了。

6 个答案:

答案 0 :(得分:34)

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

  • R_X86_64 - 所有名称都以此
  • 为前缀
  • 64 - 直接64位重定位

R_X86_64_32S

  • R_X86_64 - 前缀
  • 32S - 将值截断为32位并进行符号扩展
在这两种情况下,

基本上都是指“此重定位指向的符号的值加上任何加数”。对于R_X86_64_32S,链接器然后验证生成的值符号扩展到原始的64位值。

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

现在,在解决您的问题时,错误消息不言而喻。

答案 1 :(得分:21)

对于任何有意义的事情,你必须先:

<强>标准

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&#34;重定位类型&#34;我们看到摘要表:

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

我们稍后会解释这个表。

并注意:

  

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系统V ABI的所有重定位类型使用的前缀
  • 32:标签s的64位地址被截断为32位地址,因为我们只指定了.long(4个字节)
  • .text:我们在.text部分
  • 0xc:这是 addend ,它是重定位条目的字段

重定位的地址计算如下:

A + S

其中:

  • A:加数,此处0xC
  • S:重定位前符号的值,此处为00 00 00 00 == 0

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

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

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

R_X86_64_32S vs R_X86_64_32

R_X86_64_32SR_X86_64_32之间的区别在于链接器会抱怨&#34;重定位被截断以适应&#34;:

  • 32:抱怨如果重定位后的截断值不会使旧值扩展为零,即截断的字节必须为零:

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

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

    例如:FF FF FF FF 80 00 00 0080 00 00 00没问题,因为80 00 00 00的最后一位和截断的位都是1.

另请参阅:What does this GCC error "... relocation truncated to fit..." mean?

R_X86_64_32S可以通过以下方式生成:

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

然后:

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

现在我们可以观察&#34;搬迁&#34;使用链接描述文件截断以适合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?

R_X86_64_32S和PIE(与职位无关的可执行文件

R_X86_64_32S不能用于位置无关的可执行文件,例如完成gcc -pie,否则链接失败:

relocation R_X86_64_32S against `.text' can not be used when making a PIE object; recompile with -fPIC

1

我提供了一个最小的例子来解释它:What is the -fPIE option for position-independent executables in gcc and ld?

答案 2 :(得分:2)

这意味着编译共享对象而不使用-fPIC标志:

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

您需要致电

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

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

答案 3 :(得分:2)

我遇到了这个问题,发现这个答案对我没有帮助。我试图将静态库与共享库链接起来。我还调查了在命令行中更早地放置-fPIC开关(如其他地方的答案中所建议的那样)。 解决这个问题的唯一办法就是将静态库更改为共享。我怀疑有关-fPIC的错误消息可能由于多种原因而发生,但从根本上说,您要查看的是如何构建库,并怀疑以不同方式构建的库。

答案 4 :(得分:1)

在我的情况下,问题出现了,因为要编译的程序需要在远程目录中查找共享库,而只有相应的静态库存在错误。

实际上,这个重定位错误是伪装的文件未找到错误。

我详细介绍了我在另一个帖子https://stackoverflow.com/a/42388145/5459638

中如何处理它

答案 5 :(得分:1)

上面的答案演示了这些重定位是什么,我发现使用 GCC -mcmodel=large 标志构建 x86_64 对象可以防止 R_X86_64_32S,因为编译器对这个模型中的重定位地址没有任何假设。

在以下情况下:

extern int myarr[];

int test(int i)
{
  return myarr[i];
}

gcc -O2 -fno-pie -c test_array.c构建并用objdump -drz test_array.o反汇编,我们有:

 0: 48 63 ff                movslq %edi,%rdi
 3: 8b 04 bd 00 00 00 00    mov    0x0(,%rdi,4),%eax
        6: R_X86_64_32S myarr
 a: c3                      ret    

使用 -mcmodel=large,即 gcc -mcmodel=large -O2 -fno-pie -c test_array.c,我们有:

 0: 48 b8 00 00 00 00 00    movabs $0x0,%rax
 7: 00 00 00 
        2: R_X86_64_64  myarr
 a: 48 63 ff                movslq %edi,%rdi
 d: 8b 04 b8                mov    (%rax,%rdi,4),%eax
10: c3                      ret