So I’m learning x86_64 nasm assembly on my mac for fun. After hello world and some basic arithmetic, I tried copying a slightly more advanced hello world program from this site and modifying it for 64 bit intel, but I can’t get rid of this one error message: hello.s:53: error: Mach-O 64-bit format does not support 32-bit absolute addresses. Here is the command I use to assemble and link: nasm -f macho64 hello.s && ld -macosx_version_min 10.6 hello.o. And here is the relevant line:
cmp rsi, name+8
rsi is the register I am using for my index in the loop, and name is a quad word reserved for the user input which is the name, which by this point has already been written.
Here is a part of the code (to see the rest, click the link and go to the bottom, the only difference is that I use 64 bit registers):
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
The
cmpinstruction does not support a 64-bit immediate operand. As such, you cannot put a 64-bit immediate address reference in one of its operands – loadname+8into a register then compare to that register.You can see what instruction encodings are permitted in the Intel ISA manual (warning: huge PDF). As you can see on the entry for CMP, there are
CMP r/m32,andimm32
CMP r/m64,encodings, which allow for comparisons of a 32-bit immediate with both 32-bit and 64-bit registers, but not aimm32
CMP r/m64, imm64. There is, however, aMOV r64, imm64encoding.Or even better, use a RIP-relative LEA: Use
default relthenlea r64, [name+8]. This is more efficient and smaller thanmov r64, imm64.Since nasm is crashing, the failure of
MOV rcx, name+8is just plain a bug in nasm. Please report it to the nasm devs (after making sure you’re using the latest version of nasm; also, check that this patch doesn’t fix the problem). In any case, though, one workaround would be to add a symbol for the end ofname:Now simply use
MOV rcx, name_end. This has the advantage of not needing to update the referents when the size ofnamechanges. Alternately you could use a different assembler, such as the clang or GNU binutils assemblers.Discussion in comments points out that Linux can use a symbol address as a 32-bit immediate. This is true only in non-PIE executables which are linked with a base address in the low 2GiB of virtual address space. But MacOS chooses to put the image base address above 4GiB so you can’t use
mov r32, imm32orcmp r64, sign_extended_imm32with symbol addresses.