R_X86_64_32S和R_X86_64_64迁移意味着什么?

时间:2023-02-05 14:44:00

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; recompile with -fPIC

在创建共享对象时不能使用重新定位R_X86 _64_ 32S;编译与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.

我在google上搜索了这个错误,这是可能的原因——如果有人能说出R_X86_64_ 32的真正含义,那就太好了。

5 个解决方案

#1


29  

The R_X86_64_32S and R_X86_64_64 are names of relocation types, for code compiled for the amd64 architecture. You can look all of them up in the amd64 ABI. According to it, R_X86_64_64 is broken down to:

R_X86_64_32S和R_X86_64_64是重新定位类型的名称,用于为amd64体系结构编译的代码。你可以在amd64 ABI中查找所有这些。根据它,R_X86_64_ 64被分解为:

  • R_X86_64 - all names are prefixed with this
  • R_X86_64 -所有的名称都是前缀。
  • 64 - Direct 64 bit relocation
  • 64 -直接64位移位。

and R_X86_64_32S to:

和R_X86_64_32S:

  • R_X86_64 - prefix
  • R_X86_64 -前缀
  • 32S - truncate value to 32 bits and sign-extend
  • 32S -截断值为32位和符号扩展。

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_ 32,链接器然后验证生成的值符号-扩展到原来的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.

现在,在解决您的问题时,错误消息本身就说明了问题。

#2


16  

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. That standard only specifies the field, but not it's arch dependant values.

它们都是重新定位条目的ELF32_R_TYPE字段的所有可能值,该字段在System V ABI 4.1(1997)中指定,它指定了ELF格式的体系结构中立部分。该标准只指定字段,但不指定它的arch依赖值。

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 and R_X86_64_32S relocations truncate the computed value to 32-bits. 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_ 32位的重新定位将计算值截断为32位。链接器必须验证所生成的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:这是addend,它是重新定位条目的一个字段。

The address of the relocation is calculated as:

搬迁地址计算为:

A + S

Where:

地点:

  • A: the addend, here 0xC
  • A: addend,这里是0xC。
  • S: the value of the symbol before relocation, here 00 00 00 00 == 0
  • S:在搬迁之前的符号的值,这里00 00 == 0。

Therefore, after relocation, the new address will be 0xC == 12 bytes into the .text section.

因此,在重新定位之后,新地址将是0xC == 12字节。

This is exactly what we expect, since s comes after a .long (4 bytes) and a .quad (8 bytes).

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

R_X86_64_64 is analogous, but simpler, since here there is no need to truncate the address of s. This is indicated by the standard through word64 instead of word32 on the Field column.

R_X86_64_ 64是类似的,但更简单,因为这里不需要截去s的地址,这是由标准通过word64而不是字段列上的word32来表示的。

R_X86_64_32S vs R_X86_64_32

R_X86_64_32S vs 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, i.e. the truncated bytes must be zero:

    32:如果重置后的截断值不为零扩展旧值,即截断字节必须为零:

    E.g.: 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 80 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80,因为FF FF不为零。

  • 32S: complains if the truncated after relocation value does not sign extend the old value.

    32S:如果移位后的截断值没有扩展旧值,就会抱怨。

    E.g.: 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 80 00到80 00是可以的,因为最后一点80 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?

为内存访问而使用32的基本原理,但是直接使用32的基本原理:当一个汇编器使用象R_X86 _64_ 32S这样的扩展名,而不是像R_X86_64_ 32那样的零扩展时,什么时候比较好呢?

#3


2  

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)中,共享对象是由位置独立代码编译的,代码可以从内存中的任何位置运行,如果不给出这个标志,所生成的代码是位置依赖的,所以不能使用这个共享对象。

#4


2  

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). 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开关放在命令行前面(就像其他地方的答案一样)。对我来说,唯一解决问题的方法就是改变静态库来共享。我怀疑关于-fPIC的错误消息可能是由于一些原因导致的,但从根本上来说,您希望看到的是您的库是如何构建的,以及对正在以不同方式构建的库的怀疑。

#5


1  

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://*.com/a/42388145/5459638

我已经详细介绍了如何在另一个线程https://*.com/a/42388145/5459638中处理它。

#1


29  

The R_X86_64_32S and R_X86_64_64 are names of relocation types, for code compiled for the amd64 architecture. You can look all of them up in the amd64 ABI. According to it, R_X86_64_64 is broken down to:

R_X86_64_32S和R_X86_64_64是重新定位类型的名称,用于为amd64体系结构编译的代码。你可以在amd64 ABI中查找所有这些。根据它,R_X86_64_ 64被分解为:

  • R_X86_64 - all names are prefixed with this
  • R_X86_64 -所有的名称都是前缀。
  • 64 - Direct 64 bit relocation
  • 64 -直接64位移位。

and R_X86_64_32S to:

和R_X86_64_32S:

  • R_X86_64 - prefix
  • R_X86_64 -前缀
  • 32S - truncate value to 32 bits and sign-extend
  • 32S -截断值为32位和符号扩展。

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_ 32,链接器然后验证生成的值符号-扩展到原来的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.

现在,在解决您的问题时,错误消息本身就说明了问题。

#2


16  

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. That standard only specifies the field, but not it's arch dependant values.

它们都是重新定位条目的ELF32_R_TYPE字段的所有可能值,该字段在System V ABI 4.1(1997)中指定,它指定了ELF格式的体系结构中立部分。该标准只指定字段,但不指定它的arch依赖值。

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 and R_X86_64_32S relocations truncate the computed value to 32-bits. 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_ 32位的重新定位将计算值截断为32位。链接器必须验证所生成的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:这是addend,它是重新定位条目的一个字段。

The address of the relocation is calculated as:

搬迁地址计算为:

A + S

Where:

地点:

  • A: the addend, here 0xC
  • A: addend,这里是0xC。
  • S: the value of the symbol before relocation, here 00 00 00 00 == 0
  • S:在搬迁之前的符号的值,这里00 00 == 0。

Therefore, after relocation, the new address will be 0xC == 12 bytes into the .text section.

因此,在重新定位之后,新地址将是0xC == 12字节。

This is exactly what we expect, since s comes after a .long (4 bytes) and a .quad (8 bytes).

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

R_X86_64_64 is analogous, but simpler, since here there is no need to truncate the address of s. This is indicated by the standard through word64 instead of word32 on the Field column.

R_X86_64_ 64是类似的,但更简单,因为这里不需要截去s的地址,这是由标准通过word64而不是字段列上的word32来表示的。

R_X86_64_32S vs R_X86_64_32

R_X86_64_32S vs 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, i.e. the truncated bytes must be zero:

    32:如果重置后的截断值不为零扩展旧值,即截断字节必须为零:

    E.g.: 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 80 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80 00 00 - 80,因为FF FF不为零。

  • 32S: complains if the truncated after relocation value does not sign extend the old value.

    32S:如果移位后的截断值没有扩展旧值,就会抱怨。

    E.g.: 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 80 00到80 00是可以的,因为最后一点80 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?

为内存访问而使用32的基本原理,但是直接使用32的基本原理:当一个汇编器使用象R_X86 _64_ 32S这样的扩展名,而不是像R_X86_64_ 32那样的零扩展时,什么时候比较好呢?

#3


2  

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)中,共享对象是由位置独立代码编译的,代码可以从内存中的任何位置运行,如果不给出这个标志,所生成的代码是位置依赖的,所以不能使用这个共享对象。

#4


2  

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). 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开关放在命令行前面(就像其他地方的答案一样)。对我来说,唯一解决问题的方法就是改变静态库来共享。我怀疑关于-fPIC的错误消息可能是由于一些原因导致的,但从根本上来说,您希望看到的是您的库是如何构建的,以及对正在以不同方式构建的库的怀疑。

#5


1  

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://*.com/a/42388145/5459638

我已经详细介绍了如何在另一个线程https://*.com/a/42388145/5459638中处理它。