JustAnotherSoul
JustAnotherSoul

Reputation: 358

Printing a string in x86 Assembly on Mac OS X (NASM)

I'm doing x86 on Mac OS X with NASM. Copying an example and experimenting I noticed that my print command needed a four bytes pushed onto the stack after the other parameters but can't figure out why line five is necessary:

1 push    dword len       ;Length of message
2 push    dword msg       ;Message to write
3 push    dword 1         ;STDOUT
4 mov     eax,4           ;Command code for 'writing'
5 sub     esp,4           ;<<< Effectively 'push' Without this the print breaks
6 int     0x80            ;SYSCALL
7 add     esp,16          ;Functionally 'pop' everything off the stack

I am having trouble finding any documentation on this 'push the parameters to the stack' syntax that NASM/OS X seems to require. If anyone can point me to a resource for that in general that would most likely answer this question as well.

Upvotes: 2

Views: 676

Answers (1)

Tom
Tom

Reputation: 5201

(Most of the credit goes to @Michael Petch's comment; I'm repeating it here so that it is an answer, and also in order to further clarify the reason for the additional four bytes on the stack.)

macOS is based on BSD, and, as per FreeBSD's documentation re system calls, by default the kernel uses the C calling conventions (which means arguments are pushed to the stack, from last to first), but assuming four extra bytes pushed to the stack, as "it is assumed the program will call a function that issues int 80h, rather than issuing int 80h directly".

That is, the kernel is not built for direct int 80h calls, but rather for code that looks like this:

kernel: ; subroutine to make system calls
    int 80h
    ret

.
.
.

; code that makes a system call
    call kernel ; instead of invoking int 80h directly

Notice that call kernel would push the return address (used by the kernel subroutine's ret to return to calling code after the system call) onto the stack, accounting for four additional bytes – that's why it's necessary to manually push four bytes to the stack (any four bytes – their actual value doesn't matter, as it is ignored by the kernel – so one way to achieve this is sub esp, 4) when invoking int 80h directly.

The reason the kernel expects this behaviour – of calling a method which invokes the interrupt instead of invoking it directly – is that when writing code that can be run on multiple platforms it's then only needed to provide a different version of the kernel subroutine, rather than of every place where a system call is invoked (more details and examples in the link above).

Note: all the above is for 32-bit; for 64-bit the calling conventions are different – registers are used to pass the arguments rather than the stack (there's also a call convention for 32-bit which uses registers, but even then it's not the same registers), the syscall instruction is used instead of int 80h, and no extra four bytes (which, on 64-bit systems, would actually be eight bytes) need to be pushed.

Upvotes: 1

Related Questions