60

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

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

什么是R_X86_64_32S搬迁,什么是搬迁R_X86_64_64

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

4

6 回答 6

40

R_X86_64_32S和是重定位类型的R_X86_64_64名称,用于为 amd64 架构编译的代码。您可以在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)。

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

于 2011-05-23T07:03:54.303 回答
31

为了使这一切有意义,您必须首先:

标准

R_X86_64_64R_X86_64_32并且R_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

我们稍后会解释这个表。

并且注意:

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

R_X86_64_64 和 R_X86_64_32 的示例

让我们先看看R_X86_64_64and R_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:标签的 64 位地址s被截断为 32 位地址,因为我们只指定了一个.long(4 字节)
  • .text: 我们在这个.text部分
  • 0xc: 这是加数,是重定位项的一个字段

重定位地址计算如下:

A + S

在哪里:

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

因此,重定位后,新地址将是 0xC == 12 字节进入该.text段。

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

R_X86_64_64类似,但更简单,因为这里不需要截断s. 这由标准通过word64而不是word32Field列上指示。

R_X86_64_32S 与 R_X86_64_32

R_X86_64_32Svs之间的区别在于R_X86_64_32链接器何时会抱怨“重定位被截断以适应”:

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

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

  • 32S:如果重定位后截断的值没有符号扩展旧值,则会抱怨。

    例如:FF FF FF FF 80 00 00 00to80 00 00 00很好,因为最后一位80 00 00 00和被截断的位都是 1。

另请参阅:这个 GCC 错误“... relocation truncated to fit...”是什么意思?

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

现在我们可以观察到被截断以适应32S链接描述文件的“重定位”:

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

现在:

ld -Tlink.ld a.o

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

但是,如果我们将链接描述文件更改为:

. = 0xFFFF0FFF80000000;

它现在生成错误,因为这0使它不再是符号扩展。

32S用于内存访问但32用于立即数的基本原理:汇编器何时使用像 R_X86_64_32S 这样的符号扩展重定位而不是像 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

l

我提供了一个最小的示例来解释它:什么是 gcc 和 ld 中与位置无关的可执行文件的 -fPIE 选项?

于 2015-10-22T20:07:27.150 回答
5

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

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

你需要打电话

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

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

于 2011-05-23T06:28:13.753 回答
3

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

于 2015-08-04T20:53:03.477 回答
2

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

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

我已经详细说明了我如何在另一个线程中处理它https://stackoverflow.com/a/42388145/5459638

于 2017-02-22T10:00:38.577 回答
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, ie 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    
于 2021-06-16T03:52:46.637 回答