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

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

在创build共享对象时不能使用R_X86_64_32S; 用-fPIC重新编译

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

我已经search了这个错误,这可能是因为 – 如果有人能说出R_X86_64_32S的真正含义,那将是非常棒的。

R_X86_64_32SR_X86_64_64是重定位types的名称,用于为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 ,链接器将validation生成的值是否扩展到原始的64位值。

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

现在谈到解决您的问题时,错误信息本身就说明了一切。

对于这些有意义的事情,你必须首先:

  • 看到一个最小的迁移示例: https : //stackoverflow.com/a/30507725/895245
  • 了解ELF文件的基本结构: https : //stackoverflow.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“重定位types”下,我们看到了汇总表:

 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位。 链接器必须validationR_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) 

testingUbuntu 14.04,Binutils 2.24。

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

第一次搬迁:

 0: R_X86_64_32 .text+0xc 

意思是:

  • 0 :作用于字节0(标号a
  • R_X86_64_ :AMD64系统的所有重定位typesV ABI使用的前缀
  • 32 :标签s的64位地址被截断为32位地址,因为我们只指定了一个.long (4字节)
  • .text :我们在.text部分
  • 0xc :这是加数 ,这是重定位项的字段

重新安置的地址计算如下:

 A + S 

哪里:

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

因此,在重定位之后,新地址将变为.text段的0xC == 12个字节。

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

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

R_X86_64_32S vs 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 :抱怨如果在重定位值之后截断不符号扩展旧值。

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

另请参阅: 此GCC错误“…重定位被截断以适合…”是什么意思?

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 ao 

没问题,因为: 0xFFFFFFFF80000000被截断为80000000 ,这是一个符号扩展。

但是,如果我们将链接器脚本更改为:

 . = 0xFFFF0FFF80000000; 

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

使用32S进行内存访问的基本原理,但对于立即数使用32理由: 汇编程序何时使用符号扩展重定位(像R_X86_64_32S而不是像R_X86_64_32这样的零扩展)更好?

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

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

你需要打电话

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

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

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

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

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

我已经详细介绍了如何在这个其他线程处理它https://stackoverflow.com/a/42388145/5459638