R_X86_64_32S和R_X86_64_64重定位是什么意思?

Raj*_*Raj 49 c compiler-construction linker elf relocation

当我尝试在64位FreeBSD中编译C应用程序时出现以下错误:

在制作共享对象时不能使用重定位R_X86_64_32S; 用-fPIC重新编译

什么是R_X86_64_32S搬迁,什么是R_X86_64_64

我已经用Google搜索了错误,这可能是原因 - 如果有人能说出R_X86_64_32S的真正意义,那就太好了.

Mic*_*kis 35

R_X86_64_32SR_X86_64_64是重定位类型的名称,编为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链接器,然后验证生成的值是否扩展为原始的64位值.

现在,在可执行文件中,代码和数据段被赋予指定的虚拟基地址.可执行代码不是共享的,每个可执行文件都有自己的新地址空间.这意味着编译器确切地知道数据部分的位置,并且可以直接引用它.另一方面,库只能知道它们的数据部分将与基址相距指定的偏移量; 该基址的值只能在运行时知道.因此,所有库都必须使用可以执行的代码生成,无论它放在何处放入内存,称为位置无关代码(简称PIC).

现在,当涉及到解决您的问题时,错误消息会说明问题.


Cir*_*四事件 25

要使其中任何一个有意义,您必须首先:

标准

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"重定位类型"下,我们看到摘要表:

Name          Field   Calculation
------------  ------  -----------
R_X86_64_64   word64  A + S
R_X86_64_32   word32  A + S
R_X86_64_32S  word32  A + S
Run Code Online (Sandbox Code Playgroud)

我们稍后会解释这个表.

并注意:

R_X86_64_32R_X86_64_32S重定位截断计算值到32位.链接器必须验证R_X86_64_32(R_X86_64_32S)重定位的生成值是否为零扩展(符号扩展)为原始的64位值.

R_X86_64_64和R_X86_64_32的示例

让我们先来看看R_X86_64_64R_X86_64_32:

.section .text
    /* Both a and b contain the address of s. */
    a: .long s
    b: .quad s
    s:
Run Code Online (Sandbox Code Playgroud)

然后:

as --64 -o main.o main.S
objdump -dzr main.o
Run Code Online (Sandbox Code Playgroud)

包含:

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)
Run Code Online (Sandbox Code Playgroud)

在Ubuntu 14.04上测试,Binutils 2.24.

暂时忽略反汇编(这是没有意义的,因为这是数据),并且只查看标签,字节和重定位.

第一次搬迁:

0: R_X86_64_32  .text+0xc
Run Code Online (Sandbox Code Playgroud)

意思是:

  • 0:作用于字节0(标签a)
  • R_X86_64_:AMD64系统V ABI的所有重定位类型使用的前缀
  • 32:标签的64位地址s被截断为32位地址,因为我们只指定了一个.long(4个字节)
  • .text:我们在这个.text部分
  • 0xc:这是加数,它是重定位条目的一个字段

重定位的地址计算如下:

A + S
Run Code Online (Sandbox Code Playgroud)

哪里:

  • A:加数,在这里 0xC
  • S:重定位前符号的值,这里 00 00 00 00 == 0

因此,重定位后,新地址将为0xC == 12个字节进入该.text节.

这正是我们所期望的,因为s它来自.long(4个字节)和.quad(8个字节)之后.

R_X86_64_64是类似的,但更简单,因为这里不需要截断地址s.这是通过标准通过所指示word64,而不是word32Field列中.

R_X86_64_32S vs R_X86_64_32

R_X86_64_32Svs 之间的区别在于R_X86_64_32链接器会抱怨"将重定位截断为适合":

  • 32:抱怨如果重定位后的截断值不是零扩展旧值,即截断的字节必须为零:

    例如:因为不是零FF FF FF FF 80 00 00 0080 00 00 00产生投诉FF FF FF FF.

  • 32S:抱怨如果重定位后的截断值没有符号扩展旧值.

    例如:FF FF FF FF 80 00 00 0080 00 00 00优良的,因为最后一位80 00 00 00和截断位数均为1.

另请参阅:此GCC错误"...重定位被截断以适应......"是什么意思?

R_X86_64_32S 可以生成:

.section .text
.global _start
_start:
    mov s, %eax
    s:
Run Code Online (Sandbox Code Playgroud)

然后:

as --64 -o main.o main.S
objdump -dzr main.o
Run Code Online (Sandbox Code Playgroud)

得到:

0000000000000000 <_start>:
   0:   8b 04 25 00 00 00 00    mov    0x0,%eax
                        3: R_X86_64_32S .text+0x7
Run Code Online (Sandbox Code Playgroud)

现在我们可以观察截断的"重定位"以适应32S链接器脚本:

SECTIONS
{
    . = 0xFFFFFFFF80000000;
    .text :
    {
        *(*)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在:

ld -Tlink.ld a.o
Run Code Online (Sandbox Code Playgroud)

很好,因为:0xFFFFFFFF80000000被截断80000000,这是一个符号扩展.

但是,如果我们将链接描述文件更改为:

. = 0xFFFF0FFF80000000;
Run Code Online (Sandbox Code Playgroud)

它现在生成错误,因为这0使它不再是符号扩展.

32S用于内存访问的基本原理,但是32对于immediates:汇编器何时更好地使用符号扩展重定位,如R_X86_64_32S而不是像R_X86_64_32那样的零扩展?


Art*_*yom 5

这意味着编译共享对象时不使用-fPIC标志,因为您应该:

 gcc -shared foo.c -o libfoo.so # Wrong
Run Code Online (Sandbox Code Playgroud)

你需要打电话

 gcc -shared -fPIC foo.c -o libfoo.so # Right
Run Code Online (Sandbox Code Playgroud)

在ELF平台(Linux)下,共享对象是用位置无关代码编译的 - 可以从内存中任何位置运行的代码,如果没有给出此标志,则生成的代码是位置相关的,因此不可能使用此共享目的。

  • 我有完全相同的问题,但即使添加了 -fPIC,我仍然收到错误:`gcc -std=c99 -Wall -pedantic -shared -fopenmp -fPIC -static test.c -o libtest.so`。有什么想法吗?谢谢! (3认同)
  • @CiprianTomoiagă 迟到了,但就我而言......我的依赖链中埋藏着一个静态库,它不是用 -fPIC 编译的。所以我得到了这个错误,即使最外层的库是在指令中使用 -fPIC 编译的。我想没有奇迹。修复方法是返回链并找到未使用 -fPIC 编译的静态库并更改它。 (2认同)

归档时间:

查看次数:

29337 次

最近记录:

6 年,4 月 前