使用命令
从我的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
答案 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_name
或gcc -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}}更大而且不快。