【问题标题】:NASM Linux shared object error: Relocation R_X86_64_32S against '.data'NASM Linux 共享对象错误:针对“.data”重定位 R_X86_64_32S
【发布时间】:2020-01-26 02:43:09
【问题描述】:

我正在使用 NASM 编译器并与 ld 链接在 Linux 中编译 NASM 64 位共享对象。它使用以下字符串编译为目标文件:

sudo nasm -felf64 Test_File.asm

我与 ld 链接:

sudo ld -shared Test_File.o -o Test_File.so

我收到以下错误:

Relocation R_X86_64_32S against '.data' can not be used when making a shared object; recompile with -fPIC

ld: final link failed: Nonrepresentable section on output

很遗憾,NASM 编译器没有 -fPIC 选项。

在阅读了许多关于在 Linux 中为 64 位共享库编写与位置无关的代码的资源后,我非常了解这个问题,但我仍然不清楚我需要对位置进行哪些指令更改- 独立于 64 位 NASM。例如,是否所有涉及命名变量的指令都必须是“rel”——例如,movsd xmm0,[rel abc] 而不是 movsd xmm0,[abc]?我知道 R_X86_64_32S 表示 32 位寻址,但我的代码中没有任何 32 位寻址。

此外,32 位和 64 位在如何编写位置无关代码方面存在显着差异,并且一些资源仅集中在 32 位代码上。甚至 NASM 手册中的第 9.2 节编写 NetBSD/FreeBSD/OpenBSD 和 Linux/ELF 共享库也不清楚如何将 64 位代码更改为与位置无关的代码。该部分侧重于 32 位代码(使用全局偏移表),而不是(基于其他研究)用于 64 位代码。

  1. 根据需要,文件以 [BITS 64] 和 [default rel] 开头。

  2. 数据段声明为section .data align=16

  3. .data 部分中的每个变量都定义为 dq,例如 number: dq 0。

  4. 文件顶部包含以下格式的导出:全局 ABC:function。

我怀疑只有数据移动指令会受到影响——数学指令不会。对于 realloc 的外部调用,我添加了 wrt ..plt 特殊符号,但我仍然得到相同的错误。

这是我的问题:

  1. 是否所有的 mov 指令都需要用“rel”关键字重写,例如 mov rax,[rel abc] 而不是 mov rax,[abc]?

  2. 是否需要更改 lea 指令(例如 lea rdi,[rel abc])?

  3. 还有其他需要特殊处理的指令类型吗?

我不会在此处发布整个(非常长的)nasm 代码列表,因为我不是在寻找逐行分析。我只想知道 64 位相对寻址需要重写哪些指令类型(例如 mov、cmp、jmp、lea),以及如何重写。它是否只涉及对数据段中定义的变量的访问(例如,mov rcx,[abc],其中 abc 在数据段中定义为 abc:dq 0)。

总而言之,我的问题是:我需要对 64 位 NASM 的位置无关代码进行哪些更改,因为 NASM 编译器没有 fPIC 选项?我当然不是指逐行,而是需要添加或重写哪些类型的指令。

非常感谢。

【问题讨论】:

    标签: linux linker x86-64 nasm


    【解决方案1】:

    很遗憾,NASM 编译器没有 -fPIC 选项。

    当然不是;这是编译器的代码生成选项。 NASM 是汇编器,而不是编译器;它汇编的指令由源文件设置,而不是命令行选项。 (错误消息假设人们在编译器输出中使用ld,而不是手写asm。)

    重新编译 = 重新生成 asm 指令,而不是用不同的选项重新组装相同的 asm。编译器就是你的大脑。


    1. 是否所有 mov 指令都需要用“rel”关键字重写

    不,您可以像普通人一样在文件顶部使用default rel,而不是修改每个寻址模式以明确使用[rel foo]

    1. 和 3. 还有其他需要特殊处理的指令类型吗?

    mov 指令无关,与寻址模式有关。所有指令(包括 LEA)对寻址模式使用相同的 ModR/M + 可选 SIB + disp0/8/32 编码。 (除了mov 的一种形式,它在加载/存储 AL/AX/EAX/RAX 时可以使用 64 位绝对地址。但您也不希望这样。)

    您还需要避免将地址用作 32 位绝对立即数操作数。因此,如果要将地址放入寄存器,则需要一个 RIP 相对 LEA,而不是可以在位置相关代码中使用的更高效的 5 字节 mov-immediate。

    ;; putting a label address into a register
    default rel
        mov edi, my_string     ; optimal in position-dependent executables on Linux
        lea rdi, [my_string]   ; optimal otherwise, best you can do for PIC/PIE
    
        mov rdi, my_string     ; Never use: 64-bit absolute is inefficient
    

    您应该使用 64 位绝对地址的唯一情况是在 .data.rodata 中作为跳转表的内容或其他指向静态地址的指针。不在代码中;请改用 RIP-relative。


    显然,您必须避免使用[disp32 + reg] 寻址模式,例如[array + rdi][array + rdx*4]唯一的RIP-relative寻址模式是[RIP + rel32];其他模式仍然使用 32 位位移作为符号扩展的 32 位绝对值(因此它可以是像 1024 这样的常量偏移量,根本不是地址)。

    Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array(MachO64 不允许 32 位绝对值,因此它与 Linux/ELF PIC 对象的限制相同)

    我知道 R_X86_64_32S 表示 32 位寻址,但我的代码中没有任何 32 位寻址。

    [abs foo] 是一个扩展为 64 的 disp32 符号。这就是重定位类型为 32S 的原因。相比之下,mov edi, foo 使用 R_X86_64_32

    它不是 32 位地址大小,但绝对地址仍然必须编码为 32 位有符号整数。这在 PIE/PIC 对象中是不允许的,该对象必须可在 64 位地址空间中的任何位置重定位。


    相关:


    PIC 库应支持符号插入。请参阅Sorry state of dynamic libraries on Linux 了解更多信息。

    如果您希望对自己的global 符号进行有效的内部访问(无需通过 GOT),您可能需要为它们定义具有“隐藏”ELF 可见性的弱别名。或者干脆把 2 个标签放在同一个地方,一个全局的,一个隐藏的。请参阅 NASM 手册中的7.9.5 elf Extensions to the GLOBAL Directive

       global   hashlookup:function hidden
    

    另外,NASM 手册说明:

    在编写共享库代码时,必须声明全局符号的类型和大小。有关详细信息,请参阅第 9.2.4 节。

    【讨论】:

      猜你喜欢
      • 2018-06-19
      • 1970-01-01
      • 2017-04-10
      • 1970-01-01
      • 1970-01-01
      • 2013-11-15
      • 2017-07-23
      • 1970-01-01
      • 2019-05-25
      相关资源
      最近更新 更多