R_X86_64_32S 和 R_X86_64_64 重定位是啥意思?

Posted

技术标签:

【中文标题】R_X86_64_32S 和 R_X86_64_64 重定位是啥意思?【英文标题】:What do R_X86_64_32S and R_X86_64_64 relocation mean?R_X86_64_32S 和 R_X86_64_64 重定位是什么意思? 【发布时间】:2011-08-30 21:58:18 【问题描述】:

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

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

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

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

【问题讨论】:

相关:Linux 发行版最近开始为 gcc 默认启用 与位置无关的可执行文件。如果尝试将任何非 PIC 代码链接到可执行文件,包括使用 mov $symbol, %edi 而不是 lea symbol(%rip), %rdi 的 asm,您将收到此错误。有关使用 gcc -no-pie -fno-pie 制作传统位置相关可执行文件的信息,请参阅 32-bit absolute addresses no longer allowed in x86-64 Linux?。 @PeterCordes 我认为您的评论应该有自己的答案 @ManuelSelva:我评论中的链接是我对该主题的回答。这不完全是这个问题的答案(关于什么是重定位)。这里现有的答案很好。 【参考方案1】:

R_X86_64_32SR_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)。

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

【讨论】:

【参考方案2】:

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

查看一个最小的重定位示例:https://***.com/a/30507725/895245 了解ELF文件的基本结构:https://***.com/a/30648229/895245

标准

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

我们稍后会解释这个表。

注意事项:

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(标签 aR_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

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

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

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

R_X86_64_32S 与 R_X86_64_32

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

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

现在我们可以观察到使用链接描述文件截断以适应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

l

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

【讨论】:

mov s, %eax 是带符号的 32 位绝对重定位的一个稍微令人困惑的示例,因为除非意外,否则您通常不会写它。很容易忽略您遗漏了 (%rip) 的事实,并且它是一个负载而不是 mov-immediate($s,它将是零扩展绝对值,或使用mov $s, %rax 签名)。更好的是mov s(%rdi), %eax(或LEA),或sub $s, %rdi(或cmp $s, %rdi)。在一种寻址模式中使用带有寄存器偏移的静态地址是 32 位绝对地址的主要用例之一。 相关:32-bit absolute addresses no longer allowed in x86-64 Linux?:使用-pie -fpie 作为默认配置 gcc 现在在 Linux 发行版上很常见,所以这会咬人制作可执行文件以及故意制作共享库。【参考方案3】:

这意味着编译共享对象时没有使用-fPIC 标志,因为你应该这样做:

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

你需要打电话

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

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

【讨论】:

我遇到了同样的问题,但即使添加了 -fPIC,我仍然会遇到错误:gcc -std=c99 -Wall -pedantic -shared -fopenmp -fPIC -static test.c -o libtest.so。任何想法为什么?谢谢! 这个例子清楚地展示了如何使用 gcc -fPIC 标志来修复“x86-64-32s”和“r-x86-64-64”相关的错误。 @CiprianTomoiagă 迟到了,但在我的情况下......我的依赖链中隐藏了一个静态库,它不是用 -fPIC 编译的。所以我得到了这个错误,即使最外面的库是在指令中用 -fPIC 编译的。我猜没有奇迹。修复方法是返回链条并找到在没有 -fPIC 的情况下编译的静态库并更改它。【参考方案4】:

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

【讨论】:

在没有-fPIC 的情况下构建静态库是有道理的,因此会使用一些绝对重定位。因此,您无法将该代码链接到可重定位的共享对象中。【参考方案5】:

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

其实这个重定位错误是变相的file-not-found错误。

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

【讨论】:

【参考方案6】:

上面的答案演示了这些重定位是什么,我发现使用 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    

【讨论】:

以上是关于R_X86_64_32S 和 R_X86_64_64 重定位是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

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

内联程序集返回:在创建共享对象时,不能使用针对未定义符号的重定位R_X86_64_32S [重复]

为什么在创建特定于x86_64的共享对象时“无法使用R_X86_64_32”的CMake解决方案?

relocation R_X86_64_32S against `.data‘ can not be used when making a PIE object; recompile with -fP

重定位 R_X86_64_PC32 对符号 _ZTISt13runtime_error@@GLIBCXX_3.4 在制作共享对象时不能使用;使用 -fPIC 重新编译

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