我试图在64位FreeBSD上编译C应用程序时,遇到了以下错误:
重定位R_X86_64_32S无法在制作共享对象时使用;请使用-fPIC重新编译
R_X86_64_32S
重定位是什么?R_X86_64_64
呢?
我已经通过谷歌搜索得到了该错误的可能原因,但如果有人能告诉我R_X86_64_32S的真实含义,那就太好了。
我试图在64位FreeBSD上编译C应用程序时,遇到了以下错误:
重定位R_X86_64_32S无法在制作共享对象时使用;请使用-fPIC重新编译
R_X86_64_32S
重定位是什么?R_X86_64_64
呢?
我已经通过谷歌搜索得到了该错误的可能原因,但如果有人能告诉我R_X86_64_32S的真实含义,那就太好了。
R_X86_64_32S
和R_X86_64_64
是针对amd64架构编译代码的重定位类型名称。您可以在amd64 ABI上查找所有这些名称。
根据ABI,R_X86_64_64
可分为:
R_X86_64_32S
可分为:
在两种情况下基本上意味着“指向此重定位的符号的值加上任何附加量”。对于R_X86_64_32S
,链接器会验证生成的值是否符号扩展为原始64位值。
现在,在可执行文件中,代码段和数据段被分配了指定的虚拟基地址。可执行代码不是共享的,每个可执行文件都有自己新的地址空间。这意味着编译器完全知道数据段将在哪里,并且可以直接引用它。另一方面,库只能知道其数据段将位于距基地址指定偏移的地方;该基地址的值只能在运行时知道。因此,所有库都必须使用可以在内存中的任何位置执行的代码来生成,称为位置独立代码(简称PIC)。
现在,当解决您的问题时,错误消息会说明问题所在。
为了理解这些内容,您必须首先:
标准
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格式的体系结构中立部分。该标准仅规定了该字段,但未规定其特定于体系结构的值。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 System V ABI的所有重定位类型使用的前缀32
:标签s
的64位地址被截断为32位地址,因为我们只指定了一个.long
(4个字节).text
:我们在.text
部分0xc
:这是重定位条目的一个字段,称为“加数”重定位的地址计算如下:
A + S
在哪里:
A
:被加数,在此处为 0xC
S
:重定位之前符号的值,在此处为 00 00 00 00 == 0
因此,重定位后,新地址将是 0xC == 12
字节进入 .text
部分。
这正是我们所期望的,因为 s
在一个 .long
(4 字节)和一个 .quad
(8 字节)之后。
R_X86_64_64
是类似的,但更简单,因为这里没有必要截断 s
的地址。这通过在 Field
列上使用 word64
而不是 word32
来指示标准。
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 报错 "... 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选项?$s
,这将是零扩展绝对地址,或者使用 mov $s,%rax
进行有符号操作)。更好的方法是 mov s(%rdi), %eax
(或 LEA),或 sub $s, %rdi
(或 cmp $s, %rdi
)。在一个寻址模式中使用带有寄存器偏移量的静态地址是32位绝对地址的主要用例之一。 - Peter Cordes-pie -fpie
作为默认选项现在很常见,因此这将影响那些制作可执行文件以及有意制作共享库的人。 - Peter Cordes-fPIC
标志: gcc -shared foo.c -o libfoo.so # Wrong
您需要调用
gcc -shared -fPIC foo.c -o libfoo.so # Right
gcc -std=c99 -Wall -pedantic -shared -fopenmp -fPIC -static test.c -o libtest.so
。 有什么想法吗?谢谢! - Ciprian Tomoiagă-fPIC
编译是有道理的,因此会使用一些绝对重定位。因此,您无法将该代码链接到可重定位共享对象中。 - Peter Cordesextern 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
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
mov $symbol,%edi
而不是lea symbol(%rip),%rdi
的汇编代码,都会出现此错误。如果想要传统的位置相关可执行文件,请参阅在x86-64 Linux中不再允许32位绝对地址?,了解如何使用gcc -no-pie -fno-pie
。 - Peter Cordes