Mach-O 64位格式不支持32位绝对地址。NASM访问阵列

Apt*_*hos 5 x86-64 mach-o nasm

使用以下命令在我的Mac计算机上运行此代码:

nasm -f macho64 -o max.a maximum.asm
Run Code Online (Sandbox Code Playgroud)

这是我尝试在计算机上运行的代码,该代码在数组中找到最大的数字。

section .data

data_items:
    dd 3,67,34,222,45,75,54,34,44,33,22,11,66,0

    section .text

global _start

_start:
    mov edi, 0
    mov eax, [data_items + edi*4]
    mov ebx, eax

start_loop:
    cmp eax, 0
    je loop_exit
    inc edi
    mov eax, [data_items + edi*4]
    cmp eax, ebx
    jle start_loop

mov ebx, eax
jmp start_loop

loop_exit:

mov eax, 1
int 0x80
Run Code Online (Sandbox Code Playgroud)

错误:

maximum.asm:14: error: Mach-O 64-bit format does not support 32-bit absolute addresses
maximum.asm:21: error: Mach-O 64-bit format does not support 32-bit absolute addresses
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 8

首先,要注意具有macho64输出格式,具有64位绝对寻址(NASM 2.13.02+)相对于NASM 2.11.08具有RIP的NASM错误。不建议使用64位绝对寻址,因此,即使对于有故障的NASM 2.13.02及更高版本,此答案也应适用。(这些错误不会导致此错误,它们会导致在运行时使用错误的地址。)


[data_items + edi*4]是32位寻址模式。甚至[data_items + rdi*4]只能使用32位绝对位移,因此它也不起作用。请注意,将地址用作32位(符号扩展)立即cmp rdi, data_items数也是一个问题:仅mov允许64位立即数。

OS X上的64位代码完全不能使用32位绝对地址。可执行文件在高于4GiB的基地址处加载,因此标签地址只是普通格式,不适合32位整数(扩展名为零或符号)。RIP相对寻址是最好/最有效的解决方案,你是否需要它来与位置无关或没有1

在NASM中,default rel文件顶部将使所有[]内存操作数更喜欢RIP相对寻址。另请参阅NASM手册中的3.3节“有效地址 ”。

default rel                     ; near the top of file; affects all instructions

my_func:
    ...
    mov   ecx, [data_items]         ; uses the default: RIP-relative

    ;mov  ecx, [abs data_items]     ; override to absolute [disp32], unusuable
    mov   ecx, [rel data_items]     ; explicitly RIP-relative
Run Code Online (Sandbox Code Playgroud)

但是相对RIP 仅在不涉及其他寄存器的情况下才有可能,因此要为静态数组建立索引,您需要首先获取寄存器中的地址。使用相对于RIP的lea rsi, [rel data_items]

 lea   rsi, [data_items]    ; can be outside the loop
 ...
 mov   eax, [rsi + rdi*4]
Run Code Online (Sandbox Code Playgroud)

或者,您可以add rsi, 4在循环内使用更简单的寻址模式,例如mov eax, [rsi]

请注意,mov rsi, data_items这样做可以将地址保存到寄存器中,但是您不希望这样做,因为它效率较低。

从技术上讲,数组+ -2GiB内的任何地址都可以使用,因此,如果您有多个数组,则可以相对于一个公共基址来寻址其他数组,只需将一个寄存器与一个指针绑定即可。例如,lea rbx, [arr1]/ ...... / mov eax, [rbx + rdi*4 + arr2-arr1]相对寻址错误 -Mac 10.10提到了Agner Fog的“优化程序集”指南中有一些阵列寻址的示例,包括一个以__mh_execute_header用作参考点的阵列寻址。(该问题中的代码看起来像是另一种尝试,将这个32位Linux示例从PGU本书移植到64位OS X,同时首先学习了asm。)


请注意,在Linux上,位置相关的可执行文件被加载到虚拟地址空间的低32位中,因此您将在Linux示例中看到类似的代码,mov eax, [array + rdi*4]或者mov edi, symbol_namehttp://gcc.godbolt.org/上看到编译器输出。 gcc -pie -fPIE会在Linux上制作与位置无关的可执行文件,并且在许多最新发行版中都是默认设置,但Godbolt则不是。

这在MacOS上无济于事,但如果有人对其在其他OS上看到的代码感到困惑,或者为什么AMD64架构师费心地允许[disp32]在x86-64上使用所有寻址模式,我会提到这一点。


而且,顺便说一句,更喜欢在64位代码中使用64位寻址模式。例如使用[rsi + rdi*4],不使用[esi + edi*4]。您通常不希望将指针截断为32位,并且它花费了额外的地址大小前缀来进行编码。

同样,您应该syscall用来进行64位系统调用,而不是int 0x80在i386和x86-64上,UNIX和Linux系统调用的调用约定是什么,以区别传入args的寄存器。


脚注1: OS X支持64位绝对寻址,但仅在位置相关的可执行文件(非PIE)中支持。x64 nasm这个相关问题:将内存地址压入堆栈和调用函数包括ld使用gcc main.o链接时出现的警告:

ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not
allowed in code signed PIE, but used in _main from main.o. To fix this warning,
don't compile with -mdynamic-no-pic or link with -Wl,-no_pie
Run Code Online (Sandbox Code Playgroud)

因此,链接器检查是否使用了64位绝对重定位,如果使用,则禁用创建与位置无关的可执行文件。PIE可以从ASLR的安全性中受益。我认为共享库代码必须始终与OS X无关。我不知道是否允许跳转表或指针作为数据的其他情况(即由动态链接器修复),或者如果您不制作与位置相关的可执行文件,是否需要在运行时进行初始化。

mov r64, imm64大于(10个字节),但不大于lea r64, [RIP_rel32](7个字节)。

因此,您可以使用mov rsi, qword data_items运行速度差不多的RIP相对LEA代替,并且占用更少的代码缓存和uop缓存空间。64位立即数在Sandybridge-family(http://agner.org/optimize/)上也有uop缓存获取的惩罚:它们需要2个周期来读取uop缓存行,而不是1。

x86还具有mov从64位绝对地址加载/存储到64位绝对地址的形式,但仅适用于AL / AX / EAX / RAX。参见http://felixcloutier.com/x86/MOV.html。您也不希望这样做,因为它比更大且不快mov eax, [rel foo]


(相关:相同问题的AT&T语法版本


归档时间:

查看次数:

1259 次

最近记录:

6 年,1 月 前