I am trying to print an integer in 32 bit x86 assembly on macOS High Sierra using this code:
.cstring
STR_D:
.asciz "%d"
.globl _main
_main:
movl $53110, %edi #moves constant int into register
sub $8, %esp #make space on stack
movl %edi, 4(%esp) #move int to stack
movl $STR_D, 0(%esp) #move "%d" to stack
call _printf
add $8, %esp #restore stack pointer
ret
I am compiling using the command
gcc -m32 -Wl,-no_pie -o foo foo.s
and then executing the executable foo. I then get an error saying "Illegal instruction: 4". I am pretty sure the error lies in the instruction moving the string constant to the stack. If I remove that line and the following function call, everything works. What am I doing wrong?
Further information/question: I am doing this as part of a project to write a compiler. When I execute these instructions on Linux (while of course changing platform-specific stuff such as main instead of _main, .data instead of .cstring, etc), it works. Why does macOS not work? Does this have to do with stack alignment?
My compiler version (obtained by gcc -version) is Apple LLVM 9.0.0 (clang-900.0.39.2).
Okay, I found out what the problem was. It is twofold:
First, I discovered that I am missing a .text segment. This is apparently needed, even if it is empty. That was the reason for the "Illegal instruction: 4" error. However, if you correct this, you get a SEGFAULT.
As I suspected in the question, it has to do with stack alignment. macOS has different requirements than Linux: the stack needs to be 16-bit aligned on macOS. This means that before doing a function call (which pushes the return address onto the stack), the stack needs to be 12-aligned (meaning the stack pointer needs to look like this: 0xnnnnnnnC). To ensure this, one needs to make sure to either
1) push n times, where n is a multiple of 3 (push 3 times, 6 times, 9 times, etc)
2) if you modify the stack pointer yourself (like I did), make sure to comply with the 12-bit requirement
So all in all, code that works for me looks like this:
.cstring
STR_D:
.asciz "%d"
.text #need this, even if it's empty
.globl _main
_main:
movl $53110, %edi #moves constant int into register
sub $12, %esp #make space on stack, respecting alignment
movl %edi, 4(%esp) #move int to stack
movl $STR_D, 0(%esp) #move "%d" to stack
call _printf
add $12, %esp #restore stack pointer
ret
Edit: if someone is interested in stack alignment, I found the explanation on this website very useful!