Mach-O 64位格式不支持32位绝对地址. NASM访问阵列 [英] Mach-O 64-bit format does not support 32-bit absolute addresses. NASM Accessing Array

查看:115
本文介绍了Mach-O 64位格式不支持32位绝对地址. NASM访问阵列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用以下命令在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

解决方案

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


[data_items + edi*4]是32位寻址模式.甚至[data_items + rdi*4]也只能使用32位绝对位移,因此也不起作用.请注意,将地址用作32位(符号扩展)立即数,例如 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

但是相对于RIP的只有在不涉及其他寄存器的情况下才有可能,因此要为静态数组建立索引,您需要先在寄存器中获取地址.使用相对于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]. 相对寻址错误-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_name的代码,或者在是许多最近发行版中的默认版本,而不是Godbolt.

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


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

类似地,您应该使用syscall而不是int 0x80进行64位系统调用. 的调用约定是什么UNIX& Linux系统调用i386和x86-64 来了解传递args的寄存器的区别.


脚注1: OS X支持64位绝对寻址,但仅在位置相关的可执行文件(非PIE)中支持.此相关问题 x64 nasm:将内存地址推入堆栈&调用功能包含使用gcc main.o链接的ld警告:

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

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

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

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

x86还具有mov的形式,可从64位绝对地址加载/存储到64位绝对地址,但仅适用于AL/AX/EAX/RAX.请参阅 http://felixcloutier.com/x86/MOV.html .您也不想要它,因为它比mov eax, [rel foo]大而且不快.


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

Running this code off my Mac computer, using command:

nasm -f macho64 -o max.a maximum.asm

This is the code I am attempting to run on my computer that finds the largest number inside an array.

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

Error:

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

解决方案

First of all, beware of NASM bugs with the macho64 output format with 64-bit absolute addressing (NASM 2.13.02+) and with RIP-relative in NASM 2.11.08. 64-bit absolute addressing is not recommended, so this answer should work even for buggy NASM 2.13.02 and higher. (The bugs don't cause this error, they lead to wrong addresses being used at runtime.)


[data_items + edi*4] is a 32-bit addressing mode. Even [data_items + rdi*4] can only use a 32-bit absolute displacement, so it wouldn't work either. Note that using an address as a 32-bit (sign-extended) immediate like cmp rdi, data_items is also a problem: only mov allows a 64-bit immediate.

64-bit code on OS X can't use 32-bit absolute addressing at all. Executables are loaded at a base address above 4GiB, so label addresses just plain don't fit in 32-bit integers, with zero- or sign-extension. RIP-relative addressing is the best / most efficient solution, whether you need it to be position-independent or not1.

In NASM, default rel at the top of your file will make all [] memory operands prefer RIP-relative addressing. See also Section 3.3 Effective Addresses in the NASM manual.

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

But RIP-relative is only possible when there are no other registers involved, so for indexing a static array you need to get the address in a register first. Use a RIP-relative lea rsi, [rel data_items].

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

Or you could add rsi, 4 inside the loop and use a simpler addressing mode like mov eax, [rsi].

Note that mov rsi, data_items will work for getting an address into a register, but you don't want that because it's less efficient.

Technically, any address within +-2GiB of your array will work, so if you have multiple arrays you can address the others relative to one common base address, only tieing up one register with a pointer. e.g. lea rbx, [arr1] / ... / mov eax, [rbx + rdi*4 + arr2-arr1]. Relative Addressing errors - Mac 10.10 mentions that Agner Fog's "optimizing assembly" guide has some examples of array addressing, including one using the __mh_execute_header as a reference point. (The code in that question looks like another attempt to port this 32-bit Linux example from the PGU book to 64-bit OS X, at the same time as learning asm in the first place.)


Note that on Linux, position-dependent executables are loaded in the low 32 bits of virtual address space, so you will see code like mov eax, [array + rdi*4] or mov edi, symbol_name in Linux examples or compiler output on http://gcc.godbolt.org/. gcc -pie -fPIE will make position-independent executables on Linux, and is the default on many recent distros, but not Godbolt.

This doesn't help you on MacOS, but I mention it in case anyone's confused about code they've seen for other OSes, or why AMD64 architects bothered to allow [disp32] addressing modes at all on x86-64.


And BTW, prefer using 64-bit addressing modes in 64-bit code. e.g. use [rsi + rdi*4], not [esi + edi*4]. You usually don't want to truncate pointers to 32-bit, and it costs an extra address-size prefix to encode.

Similarly, you should be using syscall to make 64-bit system calls, not int 0x80. What are the calling conventions for UNIX & Linux system calls on i386 and x86-64 for the differences in which registers to pass args in.


Footnote 1: 64-bit absolute addressing is supported on OS X, but only in position-dependent executables (non-PIE). This related question x64 nasm: pushing memory addresses onto the stack & call function includes an ld warning from using gcc main.o to link:

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

So the linker checks if any 64-bit absolute relocations are used, and if so disables creation of a Position-Independent Executable. A PIE can benefit from ASLR for security. I think shared-library code always has to be position-independent on OS X; I don't know if jump tables or other cases of pointers-as-data are allowed (i.e. fixed up by the dynamic linker), or if they need to be initialized at runtime if you aren't making a position-dependent executable.

mov r64, imm64 is larger (10 bytes) and not faster than lea r64, [RIP_rel32] (7 bytes).

So you could use mov rsi, qword data_items instead of a RIP-relative LEA which runs about as fast, and takes less space in code caches and the uop cache. 64-bit immediates also have a uop-cache fetch penalty for on Sandybridge-family (http://agner.org/optimize/): they take 2 cycles to read from a uop cache line instead of 1.

x86 also has a form of mov that loads/store from/to a 64-bit absolute address, but only for AL/AX/EAX/RAX. See http://felixcloutier.com/x86/MOV.html. You don't want this either, because it's larger and not faster than mov eax, [rel foo].


(Related: an AT&T syntax version of the same question)

这篇关于Mach-O 64位格式不支持32位绝对地址. NASM访问阵列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆