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_32S
和R_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_64
, R_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“重定位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_32
和R_X86_64_32S
重定位将计算值截断为32位。 链接器必须validationR_X86_64_32(R_X86_64_32S)重定位的生成值为零扩展(符号扩展)为原始64位值。
R_X86_64_64和R_X86_64_32的示例
我们先来看看R_X86_64_64
和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)
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_32S
与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 00
到80 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