10 assembly gcc x86-64 att addressing-mode
我正在尝试将“main”的地址加载到 GNU 汇编器中的寄存器 (R10) 中。我没办法。在这里,我有什么和我收到的错误消息。
main:
lea main, %r10
Run Code Online (Sandbox Code Playgroud)
我还尝试了以下语法(这次使用 mov)
main:
movq $main, %r10
Run Code Online (Sandbox Code Playgroud)
使用以上两种方法,我都会收到以下错误:
/usr/bin/ld: /tmp/ccxZ8pWr.o: relocation R_X86_64_32S against symbol `main' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
使用 -fPIC 编译不能解决问题,只会给我同样的错误。
Pet*_*des 15
在 x86-64 中,大多数立即数和位移仍然是 32 位,因为 64 位会浪费太多代码大小(I-cache 占用空间和获取/解码带宽)。
lea main, %reg是一种绝对disp32寻址模式,它将阻止加载时地址随机化 (ASLR) 选择随机 64 位(或 47 位)地址。所以它在 Linux 上不受支持,除了位置相关的可执行文件,或者在 MacOS上根本不支持静态代码/数据总是在低 32 位之外加载。(有关文档和指南的链接,请参阅x86 标记 wiki。)在 Windows 上,您可以将可执行文件构建为“大地址感知”或不“识别”。如果不选择,地址将适合 32 位。
将静态地址放入寄存器的标准有效方法是RIP 相关的 LEA:
# RIP-relative LEA always works. Syntax for various assemblers:
lea main(%rip), %r10 # AT&T syntax
lea r10, [rip+main] # GAS .intel_syntax noprefix equivalent
lea r10, [rel main] ; NASM equivalent, or use default rel
lea r10, [main] ; FASM defaults to RIP-relative. MASM may also
Run Code Online (Sandbox Code Playgroud)
请参阅x86-64 GAS Intel 语法中的“[RIP + _a]”等 RIP 相关变量引用如何工作?有关 3 种语法的解释,以及为什么 x86-64 中的全局变量相对于指令指针进行访问?(和这个)原因是为什么 RIP 相对是处理静态数据的标准方法。
这使用从当前指令末尾开始的 32 位相对位移,例如jmp/ call。这可以达到在任何静态数据.data,.bss,.rodata,或在功能上.text,假定通常的2GiB总大小限制静态代码+数据。
在Linux 上的位置相关代码(gcc -fno-pie -no-pie例如构建)中,您可以利用 32 位绝对寻址来节省代码大小。此外,mov r32, imm32在 Intel/AMD CPU 上的吞吐量略高于 RIP 相关 LEA,因此乱序执行可能能够更好地与周围代码重叠。(优化代码大小通常不如大多数其他事情重要,但是当所有其他条件相同时,选择较短的指令。在这种情况下,所有其他条件至少相同,或者使用 更好mov imm32。)
看到x86-64 Linux 中不再允许使用 32 位绝对地址了吗?有关 PIE 可执行文件如何成为默认值的更多信息。(这就是为什么您在-fPIC使用 32 位绝对值时出现链接错误的原因。)
# in a non-PIE executable, mov imm32 into a 32-bit register is even better
# same as you'd use in 32-bit code
## GAS AT&T syntax
mov $main, %r10d # 6 bytes
mov $main, %edi # 5 bytes: no REX prefix needed for a "legacy" register
## GAS .intel_syntax
mov edi, OFFSET main
;; mov edi, main ; NASM and FASM syntax
Run Code Online (Sandbox Code Playgroud)
请注意,写入任何 32 位寄存器总是零扩展到完整的 64 位寄存器(R10 和 RDI)。
lea main, %edi或者lea main, %rdi也可以在 Linux 非 PIE 可执行文件中工作,但从不使用具有[disp32]绝对寻址模式的LEA (即使在不需要 SIB 字节的 32 位代码中);mov总是至少一样好。
当您有一个唯一确定它的寄存器操作数时,操作数大小后缀是多余的;我更喜欢只写mov而不是movlor movq。
愚蠢/糟糕的方式是将 10 字节 64 位绝对地址作为立即数:
# Inefficient, DON'T USE
movabs $main, %r10 # 10 bytes including the 64-bit absolute address
Run Code Online (Sandbox Code Playgroud)
如果你使用mov rdi, main而不是mov edi, main很多人最终这样做,这就是你在 NASM 中得到的。Linux的动态链接不实际支持运行时的修正64位绝对地址。但它的用例是用于跳转表,而不是用于作为立即数的绝对地址。
movq $sign_extended_imm32, %reg(7 个字节)仍然使用 32 位绝对地址,但在符号扩展mov到 64 位寄存器上浪费了代码字节,而不是从写入 32 位寄存器到 64 位的隐式零扩展。
通过使用movq,你告诉GAS你想要一个R_X86_64_32S搬迁,而不是R_X86_64_6464位绝对搬迁。
您想要这种编码的唯一原因是内核代码,其中静态地址位于 64 位虚拟地址空间的高 2GiB,而不是低 2GiB。 与某些 CPU相比,它mov具有轻微的性能优势lea(例如,在更多端口上运行),但通常如果您可以使用 32 位绝对值,则它位于 2GiB 的低虚拟地址空间中mov r32, imm32。
(相关:x86-64 中 movq 和 movabsq 的区别)
PS:我故意省略了对“大”或“巨大”内存/代码模型的任何讨论,其中 RIP 相对 +-2GiB 寻址无法访问静态数据,或者甚至可能无法访问其他代码地址。以上适用于 x86-64 System V ABI 的“小型”和/或“小型 PIC”代码模型。您可能需要movabs $imm64中型和大型模型,但这种情况非常罕见。
我不知道是否mov $imm32, %r32适用于带有运行时修正的 Windows x64 可执行文件或 DLL,但相对于 RIP 的 LEA 确实适用。
半相关:在 x86 机器代码中调用绝对指针- 如果您正在 JIT,请尝试将 JIT 缓冲区放在现有代码附近以便您可以call rel32,否则movabs将指针放入寄存器。
| 归档时间: |
|
| 查看次数: |
5302 次 |
| 最近记录: |