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

时间:2017-11-15 06:33:47

标签: nasm x86-64 mach-o

使用命令

从我的Mac电脑上运行此代码
nasm -f macho64 -o max.a maximum.asm

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

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

错误:

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

1 个答案:

答案 0 :(得分:4)

首先,谨防使用带有64-bit absolute addressing (NASM 2.13.02+)RIP-relative in NASM 2.11.08的带有macho64输出格式的NASM错误。建议不要使用64位绝对寻址,因此即使对于有缺陷的NASM 2.13.02及更高版本,此答案也应该有效。 (错误不会导致此错误,导致在运行时使用错误的地址。)

[data_items + edi*4]是32位寻址模式。即使[data_items + rdi*4]只能使用32位绝对位移,所以它也不会起作用。请注意,使用地址作为32位(符号扩展)立即数,如cmp rdi, data_items is also a problem:仅mov允许64位立即数。

OS X上的64位代码无法使用32位绝对寻址。可执行文件加载在4GiB以上的基址上,因此标签地址不适合32位整数,具有零或符号扩展名。 RIP相对寻址是最佳/最有效的解决方案,无论您是需要位置无关还是 1

在NASM中,文件顶部的default rel 会使所有[]内存操作数更喜欢RIP相对寻址。另请参阅NASM手册中的Section 3.3 Effective Addresses

default rel                     ; near the top of file; affects later 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

但RIP相对is only possible when there are no other registers involved,所以用于索引静态数组,您需要先在寄存器中获取地址。使用RIP相对lea rsi, [rel data_items]

 lea   rsi, [data_items]    ; can be outside the loop
 ...
 mov   eax, [rsi + rdi*4]

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

请注意,mov rsi, data_items可用于将地址输入注册表,但您不希望这样做,因为效率较低。

从技术上讲,数组+ -2GiB内的任何地址都可以工作,所以如果你有多个数组,你可以相对于一个公共基地址寻址其他数组,只用一个指针绑定一个寄存器。例如lea rbx, [arr1] / ... / mov eax, [rbx + rdi*4 + arr2-arr1]Relative Addressing errors - Mac 10.10提到Agner Fog"优化组装" guide有一些数组寻址的例子,包括使用__mh_execute_header作为参考点的一个。 (该问题中的代码看起来像是另一种尝试将这个32位Linux示例从PGU书籍移植到64位OS X,同时首先学习asm。)

请注意,在Linux上,位置相关的可执行文件在虚拟地址空间的低32位中加载,因此您将在Linux示例中看到mov eax, [array + rdi*4]mov edi, symbol_namegcc -pie -fPIE上的代码[disp32][rsi + rdi*4] 3}}。 [esi + edi*4]将在Linux和http://gcc.godbolt.org/上创建与位置无关的可执行文件,而不是Godbolt。

这对MacOS没有帮助,但我提到它,以防任何人对他们在其他操作系统中看到过的代码感到困惑,或者为什么AMD64架构师不愿允许syscall在x86-64上完全寻址模式。

BTW,更喜欢在64位代码中使用64位寻址模式。例如使用int 0x80,而不是ld。您通常不希望截断指向32位的指针,并且需要额外的地址大小前缀才能进行编码。

同样,您应该使用gcc main.o进行64位系统调用,而不是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 is the default on many recent distros表示寄存器传递args的区别。

脚注1: OS X支持64位绝对寻址,但仅限于位置相关的可执行文件(非PIE)。相关问题What are the calling conventions for UNIX & Linux system calls on i386 and x86-64包含mov r64, imm64使用lea r64, [RIP_rel32]进行链接的警告:

mov rsi, qword data_items

因此,链接器会检查是否使用了任何64位绝对重定位,如果是,则禁用创建与位置无关的可执行文件。出于安全考虑,PIE可以从x64 nasm: pushing memory addresses onto the stack & call function中受益。我认为共享库代码总是必须在OS X上与位置无关;我不知道是否允许跳转表或其他指针作为数据的情况(即由动态链接器修复),或者如果你不在运行时需要在运行时初始化它们 - 依赖可执行文件。

mov更大(10个字节),并且不会快于mov eax, [rel foo](7个字节)。

所以你可以使用{{1}}而不是相对于RIP的相对LEA,它运行速度快,并且在代码缓存和uop缓存中占用的空间更少。 64位immediates对Sandybridge系列(ASLR)也有一个uop-cache fetch惩罚:他们需要2个周期才能从uop缓存行读取而不是1。

x86还具有{{1}}形式,可以加载/存储64位绝对地址,但仅适用于AL / AX / EAX / RAX。见http://agner.org/optimize/。你也不想要这个,因为它比{{1}}更大而且不快。

(相关:http://felixcloutier.com/x86/MOV.html