[英]What do R_X86_64_32S and R_X86_64_64 relocation mean?
Got the following error when I tried to compile a C application in 64-bit FreeBSD:当我尝试在 64 位 FreeBSD 中编译 C 应用程序时出现以下错误:
relocation R_X86_64_32S can not be used when making a shared object;
relocation R_X86_64_32S 在制作共享对象时不能使用; recompile with -fPIC
用 -fPIC 重新编译
What is R_X86_64_32S
relocation and what is R_X86_64_64
?什么是
R_X86_64_32S
重定位,什么是R_X86_64_64
?
I've googled about the error, and it's possible causes - It'd be great if anyone could tell what R_X86_64_32S really means.我在谷歌上搜索了这个错误,这可能是原因 - 如果有人能说出 R_X86_64_32S 的真正含义,那就太好了。
The R_X86_64_32S
and R_X86_64_64
are names of relocation types, for code compiled for the amd64 architecture. R_X86_64_32S
和R_X86_64_64
是重定位类型的名称,用于为 amd64 架构编译的代码。 You can look all of them up in the amd64 ABI .您可以在amd64 ABI 中查找所有这些。 According to it,
R_X86_64_64
is broken down to:根据它,
R_X86_64_64
被分解为:
and R_X86_64_32S
to:和
R_X86_64_32S
到:
which basically means "the value of the symbol pointed to by this relocation, plus any addend", in both cases.在这两种情况下,这基本上意味着“此重定位指向的符号的值,加上任何加数”。 For
R_X86_64_32S
the linker then verifies that the generated value sign-extends to the original 64-bit value.对于
R_X86_64_32S
,链接器然后验证生成的值是否符号扩展为原始 64 位值。
Now, in an executable file, the code and data segments are given a specified virtual base address.现在,在一个可执行文件中,代码和数据段被赋予了一个指定的虚拟基地址。 The executable code is not shared, and each executable gets its own fresh address space.
可执行代码不共享,每个可执行文件都有自己的新地址空间。 This means that the compiler knows exactly where the data section will be, and can reference it directly.
这意味着编译器确切地知道数据段的位置,并且可以直接引用它。 Libraries, on the other hand, can only know that their data section will be at a specified offset from the base address;
另一方面,库只能知道它们的数据部分将位于距基地址的指定偏移处; the value of that base address can only be known at runtime.
该基地址的值只能在运行时知道。 Hence, all libraries must be produced with code that can execute no matter where it is put into memory, known as position independent code (or PIC for short).
因此,所有库都必须使用无论放入内存何处都可以执行的代码生成,称为位置无关代码(或简称 PIC)。
Now when it comes to resolving your problem, the error message speaks for itself.现在,在解决您的问题时,错误消息不言自明。
For any of this to make sense, you must first:要使其中任何一项有意义,您必须首先:
Standards标准
R_X86_64_64
, R_X86_64_32
and R_X86_64_32S
are all defined by the System V AMD ABI , which contains the AMD64 specifics of the ELF file format. R_X86_64_64
、 R_X86_64_32
和R_X86_64_32S
都是由System V AMD ABI定义的,其中包含 ELF 文件格式的 AMD64 细节。
They are all possible values for the ELF32_R_TYPE
field of a relocation entry, specified in the System V ABI 4.1 (1997) which specifies the architecture neutral parts of the ELF format.它们都是重定位条目的
ELF32_R_TYPE
字段的所有可能值,在System V ABI 4.1 (1997)中指定,它指定了 ELF 格式的体系结构中立部分。 That standard only specifies the field, but not it's arch dependant values.该标准仅指定字段,而不是它的依赖于拱的值。
Under 4.4.1 "Relocation Types" we see the summary table:在 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
We will explain this table later.我们稍后会解释这个表。
And the note:和注意事项:
The
R_X86_64_32
andR_X86_64_32S
relocations truncate the computed value to 32-bits.R_X86_64_32
和R_X86_64_32S
重定位将计算值截断为 32 位。 The linker must verify that the generated value for the R_X86_64_32 (R_X86_64_32S) relocation zero-extends (sign-extends) to the original 64-bit value.链接器必须验证为 R_X86_64_32 (R_X86_64_32S) 重定位生成的值零扩展(符号扩展)到原始 64 位值。
Example of R_X86_64_64 and R_X86_64_32 R_X86_64_64 和 R_X86_64_32 示例
Let's first look into R_X86_64_64
and 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:
Then:然后:
as --64 -o main.o main.S
objdump -dzr main.o
Contains:包含:
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)
Tested on Ubuntu 14.04, Binutils 2.24.在 Ubuntu 14.04、Binutils 2.24 上测试。
Ignore the disassembly for now (which is meaningless since this is data), and look only to the labels, bytes and relocations.暂时忽略反汇编(这是没有意义的,因为这是数据),只查看标签、字节和重定位。
The first relocation:第一次搬迁:
0: R_X86_64_32 .text+0xc
Which means:意思是:
0
: acts on byte 0 (label a
) 0
:作用于字节 0(标签a
)R_X86_64_
: prefix used by all relocation types of the AMD64 system V ABI R_X86_64_
: AMD64 系统 V ABI 的所有重定位类型使用的前缀32
: the 64-bit address of the label s
is truncated to a 32 bit address because we only specified a .long
(4 bytes) 32
:标签s
的 64 位地址被截断为 32 位地址,因为我们只指定了一个.long
(4 个字节).text
: we are on the .text
section .text
:我们在.text
部分0xc
: this is the addend , which is a field of the relocation entry 0xc
:这是加数,它是重定位条目的一个字段The address of the relocation is calculated as:重定位地址计算如下:
A + S
Where:在哪里:
A
: the addend, here 0xC
A
: 加数,这里是0xC
S
: the value of the symbol before relocation, here 00 00 00 00 == 0
S
: 重定位前符号的值,这里00 00 00 00 == 0
Therefore, after relocation, the new address will be 0xC == 12 bytes into the .text
section.因此,重定位后,新地址将是
.text
部分的 0xC == 12 个字节。
This is exactly what we expect, since s
comes after a .long
(4 bytes) and a .quad
(8 bytes).这正是我们所期望的,因为
s
在.long
(4 字节)和.quad
(8 字节)之后。
R_X86_64_64
is analogous, but simpler, since here there is no need to truncate the address of s
. R_X86_64_64
类似,但更简单,因为这里不需要截断s
的地址。 This is indicated by the standard through word64
instead of word32
on the Field
column.这由标准通过
Field
列上的word64
而不是word32
指示。
R_X86_64_32S vs R_X86_64_32 R_X86_64_32S 对比 R_X86_64_32
The difference between R_X86_64_32S
vs R_X86_64_32
is when the linker will complain "with relocation truncated to fit": R_X86_64_32S
与R_X86_64_32
之间的区别在于链接器何时会抱怨“重定位被截断以适合”:
32
: complains if the truncated after relocation value does not zero extend the old value, ie the truncated bytes must be zero: 32
:如果重定位后截断的值不为零扩展旧值,即被截断的字节必须为零,则抱怨:
Eg: FF FF FF FF 80 00 00 00
to 80 00 00 00
generates a complaint because FF FF FF FF
is not zero.例如:
FF FF FF FF 80 00 00 00
到80 00 00 00
产生投诉,因为FF FF FF FF
不为零。
32S
: complains if the truncated after relocation value does not sign extend the old value. 32S
:如果重定位后截断的值没有对旧值进行符号扩展,则会报错。
Eg: FF FF FF FF 80 00 00 00
to 80 00 00 00
is fine, because the last bit of 80 00 00 00
and the truncated bits are all 1.例如:
FF FF FF FF 80 00 00 00
到80 00 00 00
就可以了,因为80 00 00 00
的最后一位和被截断的位都是 1。
See also: What does this GCC error "... relocation truncated to fit..." mean?另请参阅: 这个 GCC 错误“...重定位被截断以适合...”是什么意思?
R_X86_64_32S
can be generated with: R_X86_64_32S
可以通过以下方式生成:
.section .text
.global _start
_start:
mov s, %eax
s:
Then:然后:
as --64 -o main.o main.S
objdump -dzr main.o
Gives:给出:
0000000000000000 <_start>:
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
3: R_X86_64_32S .text+0x7
Now we can observe the "relocation" truncated to fit on 32S
with a linker script:现在我们可以使用链接描述文件观察被截断以适合
32S
的“重定位”:
SECTIONS
{
. = 0xFFFFFFFF80000000;
.text :
{
*(*)
}
}
Now:现在:
ld -Tlink.ld a.o
Is fine, because: 0xFFFFFFFF80000000
gets truncated into 80000000
, which is a sign extension.很好,因为:
0xFFFFFFFF80000000
被截断为80000000
,这是一个符号扩展。
But if we change the linker script to:但是如果我们将链接描述文件更改为:
. = 0xFFFF0FFF80000000;
It now generates the error, because that 0
made it not be a sign extension anymore.它现在生成错误,因为
0
使它不再是符号扩展。
Rationale for using 32S
for memory access but 32
for immediates: 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?使用
32S
进行内存访问但使用32
进行立即数访问的基本原理: 汇编程序何时使用 R_X86_64_32S 之类的符号扩展重定位而不是 R_X86_64_32 之类的零扩展更好?
R_X86_64_32S and PIE (position independent executables R_X86_64_32S 和 PIE(位置无关的可执行文件
R_X86_64_32S cannot be used in position independent executables, eg done with gcc -pie
, otherwise link fails with: 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升
I have provided a minimal example explaining it at: What is the -fPIE option for position-independent executables in gcc and ld?我提供了一个最小的例子来解释它: gcc 和 ld 中位置无关的可执行文件的 -fPIE 选项是什么?
That means that compiled a shared object without using -fPIC
flag as you should:这意味着在不使用
-fPIC
标志的情况下编译共享对象,您应该这样做:
gcc -shared foo.c -o libfoo.so # Wrong
You need to call你需要打电话
gcc -shared -fPIC foo.c -o libfoo.so # Right
Under ELF platform (Linux) shared objects are compiled with position independent code - code that can run from any location in memory, if this flag is not given, the code that is generated is position dependent, so it is not possible to use this shared object.在 ELF 平台 (Linux) 下,共享对象是使用位置无关代码编译的 - 代码可以从内存中的任何位置运行,如果没有给出此标志,则生成的代码是位置相关的,因此无法使用此共享目的。
I ran into this problem and found this answer didn't help me.我遇到了这个问题,发现这个答案对我没有帮助。 I was trying to link a static library together with a shared library.
我试图将静态库与共享库链接在一起。 I also investigated putting the -fPIC switch earlier on the command line (as advised in answers elsewhere).
我还研究了将 -fPIC 开关更早地放在命令行上(如其他地方的答案中所建议的那样)。 The only thing that fixed the problem, for me, was changing the static library to shared.
对我来说,解决问题的唯一方法是将静态库更改为共享。 I suspect the error message about -fPIC can happen due to a number of causes but fundamentally what you want to look at is how your libraries are being built, and be suspicious of libraries that are being built in different ways.
我怀疑有关 -fPIC 的错误消息可能由于多种原因而发生,但从根本上说,您想要查看的是您的库是如何构建的,并对以不同方式构建的库持怀疑态度。
In my case the issue arose because the program to compile expected to find shared libraries in a remote directory, while only the corresponding static libraries were there in a mistake.在我的情况下,问题出现是因为要编译的程序希望在远程目录中找到共享库,而只有相应的静态库出错了。
Actually, this relocation error was a file-not-found error in disguise.实际上,这个重定位错误是变相的文件未找到错误。
I have detailed how I coped with it in this other thread https://stackoverflow.com/a/42388145/5459638我在另一个线程中详细说明了我是如何处理它的https://stackoverflow.com/a/42388145/5459638
The above answer demonstrates what these relocations are, and I found building x86_64 objects with GCC -mcmodel=large flag can prevent R_X86_64_32S because the compiler has no assumption on the relocated address in this model.上面的答案演示了这些重定位是什么,我发现使用 GCC -mcmodel=large 标志构建 x86_64 对象可以防止 R_X86_64_32S,因为编译器对这个模型中的重定位地址没有任何假设。
In the following case:在以下情况下:
extern int myarr[];
int test(int i)
{
return myarr[i];
}
Built with gcc -O2 -fno-pie -c test_array.c
and disassemble with objdump -drz test_array.o
, we have:用
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
With -mcmodel=large, ie gcc -mcmodel=large -O2 -fno-pie -c test_array.c
, we have:使用 -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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.