Consider this assembly code on x86-64 MacOS:
global start
section .text
start:
mov rax, 0x02000005 ; MacOS syscall: "open"
mov rdi, filename ; open; 1st parameter: file path
mov rsi, 0 ; open; 2nd parameter: flags
mov rdx, 0 ; open; 3rd parameter: in case of a newly created file, the file permissions
syscall
mov rdi, rax ; set the return value of open (the file descriptor or the error code)
; as the 1st parameter of the next syscall, "exit": exit status
mov rax, 0x02000001 ; MacOS syscall: "exit"
syscall
section .data
filename: db "doesntexist", 0 ; a non-existing filename
It calls just "open" and then "exit", returning the return code of the "open" syscall as the exit status.
For convenience, here's how to assemble and run it: nasm -f macho64 simple.asm && ld simple.o -static -o simple && ./simple
. And right after running, to see the exit status: echo $?
.
What surprises me, is that as of MacOS Monterey (12.2.1(21D62)), this program prints out 2
instead of a negative number converted to the exit status range. (Returning -1
would generate the exist status of 255
; this is easy to test, tweaking above code, but I also made separately sure that the rax
register on return is actually in the low positive range.) "open" is supposed to return a file descriptor or an error code, but a positive number corresponding to the file descriptor of stderr
doesn't look like either of those. To be sure that the code above is working just touch doesntexist
, run again and see that it returns a valid file descriptor of 3
.
Officially, MacOS supports only doing syscalls through the OS-provided API, not directly via an ABI like this. Is this just a case of undocumented/unstable behaviour of MacOS syscalls, or do I have a mistake or a misunderstanding somewhere?
Edit: In the comments, there was a valid concern of whether the return value was turned from negative to positive in the conversion from the return value of the syscall (a 64-bit register) to the exit status of the process (0-255). Here's an attempt to prove that this isn't the case:
global start
section .text
start:
mov rax, 0x02000005 ; MacOS syscall: open
mov rdi, filename ; open; 1st parameter: file path
mov rsi, 0 ; open; 2nd parameter: flags
mov rdx, 0 ; open; 3rd parameter: in case of a newly created file, the file permissions
syscall
cmp rax, 0 ; if the result was negative or positive
mov rax, 0x02000001 ; MacOS syscall: exit
jg ok ; if the result was positive, jump to "ok" and return 99
error:
mov rdi, 66
syscall
ok:
mov rdi, 99
syscall
section .data
filename: db "doesntexist", 0 ; a non-existing filename
The process returns 99
, which means that rax
was positive.