问题描述
|
当我尝试在64位FreeBSD中编译C应用程序时出现以下错误:
创建共享库时,不能使用重定位R_X86_64_32S;用-fPIC重新编译
什么是“ 0”重定位,什么是“ 1”重定位?
我一直在搜索该错误,并且可能是由该原因引起的-如果有人能说出R_X86_64_32S的真正含义,那就太好了。
解决方法
R_X86_64_32S
和R_X86_64_64
是重定位类型的名称,用于为amd64架构编译的代码。您可以在amd64 ABI中查找所有这些文件。
根据它,ѭ1被分解为:
R_X86_64-所有名称都带有前缀
64-直接64位重定位
和R_X86_64_32S
到:
R_X86_64-前缀
32S-将值截断为32位并进行符号扩展
在两种情况下,这基本上表示\“此重定位所指向的符号的值加上任何加数\”。然后对于链接器“ 0”,验证生成的值是否符号扩展为原始的64位值。
现在,在可执行文件中,为代码和数据段指定了指定的虚拟基址。可执行代码不共享,每个可执行文件都有其自己的新地址空间。这意味着编译器确切知道数据段将在哪里,并可以直接引用它。另一方面,库只能知道其数据段与基地址之间有指定的偏移量。该基地址的值只能在运行时知道。因此,所有库必须使用无论位置如何都可以执行的代码(称为位置无关代码(简称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细节。
它们都是重定位条目的“ 10”字段的所有可能值,在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_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)
在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
:这是加数,是重定位条目的字段
重定位的地址计算如下:
A + S
哪里:
A
:加号,here31ѭ
S
:重定位前符号的值,here33ѭ
因此,在重定位之后,新的地址将在“ 26”节中为0xC == 12个字节。
这正是我们的期望,因为s
紧随.long
(4字节)和.quad
(8字节)之后。
R_X86_64_64
与之类似,但是更简单,因为这里不需要截断s
的地址。这是通过through40ѭ而不是Field
列中的word32
表示的。
R_X86_64_32S和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
现在我们可以观察到带有链接描述文件的\“ relocation \”被截断以适合32S
:
SECTIONS
{
. = 0xFFFFFFFF80000000;
.text :
{
*(*)
}
}
现在:
ld -Tlink.ld a.o
很好,因为:0xFFFFFFFF80000000
被截断为into61ѭ,这是一个符号扩展。
但是,如果我们将链接描述文件更改为:
. = 0xFFFF0FFF80000000;
现在它会产生错误,因为ѭ20不再使它成为符号扩展。
使用ѭ49进行内存访问而用ѭ23进行即时访问的理由:什么时候汇编程序最好使用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
升
我在以下位置提供了一个解释它的最小示例:gcc和ld中与位置无关的可执行文件的-fPIE选项是什么?
,这意味着编译共享对象时无需使用-fPIC
标志:
gcc -shared foo.c -o libfoo.so # Wrong
你需要打电话
gcc -shared -fPIC foo.c -o libfoo.so # Right
在ELF平台(Linux)下,共享对象使用位置无关的代码进行编译-可以在内存中任何位置运行的代码,如果未指定此标志,则生成的代码与位置有关,因此无法使用此共享宾语。
,我遇到了这个问题,发现这个答案并没有帮助我。我试图将静态库与共享库链接在一起。我还研究了如何将-fPIC开关放在命令行的前面(如其他答案中所建议)。
对我来说,唯一解决此问题的方法是将静态库更改为共享库。我怀疑有关-fPIC的错误消息可能由于多种原因而发生,但从根本上讲,您要查看的是如何构建库,并对以不同方式构建的库感到怀疑。
,在我的情况下,出现了这个问题,因为要编译的程序希望在远程目录中找到共享库,而只有相应的静态库才出错。
实际上,此重定位错误是一种变相的文件找不到错误。
我已经在另一个线程中详细介绍了如何应对它https://stackoverflow.com/a/42388145/5459638