0

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).

toby_p
  • 11
  • 4
  • @lurker: Thanks for the info. It is true that I need some sort of return value, this turned out to be a problem later in the project. I then decided to terminate with `xor %eax, %eax` and `ret`. Is there a difference between doing it that way and doing it your way? – toby_p Mar 12 '18 at 15:58

1 Answers1

1

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!

toby_p
  • 11
  • 4