我正在使用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位代码。
根据需要,该文件的标题为[BITS 64]和[default rel]。
数据段被声明为.data align = 16
。data节中的每个变量都定义为dq,例如,数字:dq0。
文件的顶部包含以下格式的导出:全局ABC:function。
我怀疑只有数据移动指令会受到影响-数学指令不会受到影响。对于重新分配的外部调用,我添加了wrt ..plt特殊符号,但是仍然出现相同的错误。
这是我的问题:
是否需要使用“ rel”关键字重写所有mov指令,例如mov rax,[rel abc]而不是mov rax,[abc]?
是否需要更改lea指令(例如lea rdi,[rel abc])?
还有其他需要特殊处理的指令类型吗?
我没有在此处发布整个(很长)的nasm代码列表,因为我不是在逐行进行分析。我只想知道对于64位相对寻址需要重写哪些指令类型(例如mov,cmp,jmp,lea),以及如何重写。它是否仅涉及对数据部分中定义的变量的访问(例如,mov rcx,[abc],其中abc在数据部分中定义为abc:dq 0)。
总而言之,我的问题是:由于NASM编译器没有fPIC选项,我需要对64位NASM的与位置无关的代码进行哪些更改?我当然不是逐行的意思,而是需要添加或重写哪些类型的指令。
非常感谢。
很遗憾,NASM编译器没有-fPIC选项。
当然不是;这是compiler的代码生成选项。 NASM是汇编程序,而不是编译器。它汇编的指令是由源文件而不是命令行选项设置的。 (错误消息是假设人们在编译器输出上使用的是ld
,而不是手写的asm。)
Recompile =重做以生成asm指令,而不是使用不同的选项重新组装相同的asm。编译器是您的大脑。
- 是否需要使用“ rel”关键字重写所有mov指令
不,您可以像普通用户一样在文件顶部使用default rel
,而不是修改每种寻址模式以显式使用[rel foo]
。
- 和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
对于跳转表或其他指向静态地址的指针,仅在.data
或.rodata
中使用64位绝对地址。不在代码中;请使用相对RIP。
显然,您必须避免使用[disp32 + reg]
或[array + rdi]
之类的[array + rdx*4]
寻址模式。 唯一的RIP相对寻址模式是[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]
是disp32符号,扩展为64。这就是为什么重定位类型为32 Smov edi, foo
使用R_X86_64_32
。这不是32位地址大小,但是绝对地址仍然必须编码为32位带符号整数。必须在64位地址空间中的任何位置都可重定位的PIE / PIC对象中不允许这样做。
相关:
gcc -fno-plt
样式和call [rel printf wrt ..got]
从NASM PIE / PIC代码调用libc函数>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节。