汇编器错误:Mach-O 64位不支持绝对32位地址

mk1*_*k12 5 macos assembly x86-64 mach-o nasm

所以我正在我的mac上学习x86_64 nasm程序集以获得乐趣.在hello world和一些基本的算术之后,我尝试从这个站点复制一个稍高级的hello world程序并将其修改为64位intel,但我无法摆脱这一个错误消息:hello.s:53: error: Mach-O 64-bit format does not support 32-bit absolute addresses.这是我用来汇编和链接的命令:nasm -f macho64 hello.s && ld -macosx_version_min 10.6 hello.o.这是相关的:

cmp rsi, name+8
Run Code Online (Sandbox Code Playgroud)

rsi是我在循环中用于索引的寄存器,name是为用户输入保留的四字,这是名称,到目前为止已经写入了.

这是代码的一部分(要查看其余部分,单击链接并转到底部,唯一的区别是我使用64位寄存器):

loopAgain:
mov al, [rsi]           ; al is a 1 byte register
cmp al, 0x0a            ; if al holds an ascii newline...
je exitLoop             ; then jump to label exitLoop

; If al does not hold an ascii newline...
mov rax, 0x2000004      ; System call write = 4
mov rdi, 1              ; Write to stdout = 1
mov rdx, 1              ; Size to write
syscall

inc rsi

cmp rsi, name+8         ; LINE THAT CAUSES ERROR
jl loopAgain
Run Code Online (Sandbox Code Playgroud)

bdo*_*lan 4

cmp指令不支持 64 位立即数操作数。因此,您不能将 64 位立即地址引用放入其操作数之一 - 加载name+8到寄存器中然后与该寄存器进行比较。

您可以在Intel ISA 手册中查看允许哪些指令编码(警告:巨大的 PDF)。正如您在 CMP 的条目中看到的,有CMP r/m32, imm32CMP r/m64, imm32编码,允许将 32 位立即数与 32 位和 64 位寄存器进行比较,但不允许CMP r/m64, imm64. 然而,有一种MOV r64, imm64编码。

或者更好的是,使用 RIP 相关的 LEA:使用default relthen lea r64, [name+8]。这比 更高效且更小mov r64, imm64


由于 nasm 正在崩溃,因此失败MOV rcx, name+8只是 nasm 中的一个错误。请将其报告给 nasm 开发人员(在确保您使用的是最新版本的 nasm 后;另外,请检查此补丁是否无法解决问题)。但无论如何,一种解决方法是在 的末尾添加一个符号name

name:
    resb 8
name_end:
Run Code Online (Sandbox Code Playgroud)

现在只需使用MOV rcx, name_end. 这样做的优点是当大小发生name变化时不需要更新引用对象。或者,您可以使用不同的汇编器,例如 clang 或 GNU binutils 汇编器。


评论中的讨论指出,Linux 可以使用符号地址作为 32 位立即数。仅在与虚拟地址空间低 2GiB 中的基地址链接的非 PIE 可执行文件中才是如此。但是 MacOS 选择将图像基地址放在 4GiB 以上,因此您不能将mov r32, imm32orcmp r64, sign_extended_imm32与符号地址一起使用。