0

I have seen examples where the stack pointer/esp is decremented by 4 before calling printf and re-adjusted by 12 after calling printf:

section .text
global  main
extern printf

main:
    sub   esp, 4
    push  msg          
    push  format_str
    call  printf
    add   esp, 12
    ret

section .data:
    msg db "print me!", 0
    format_str db "%s", 0

And I have seen examples where the stack pointer/esp is decremented by 8 before calling printf and re-adjusted by 16 after calling printf:

section .text
global  main
extern printf

main:
    sub   esp, 8
    push  msg        
    push  format_str
    call  printf
    add   esp, 16
    ret

section .data:
    msg db "print me!", 0
    format_str db "%s", 0

From what I've read esp should be decremented by 8 and then re-adjusted/incremented by 16 before calling any function from libc.

The differences in these examples confuse me, which stack alignment example is correct and why? Can this process of incrementing/decrementing be explained to make this less confusing?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
telko456
  • 3
  • 2
  • 2
    The second example is actually wrong and the stack is not aligned on a 16-byte boundary. Not all examples on the internet are actually correct. – Michael Petch Nov 20 '20 at 16:08

1 Answers1

0

I have seen examples where the stack pointer/esp is decremented by 4 before calling printf and re-adjusted by 12 after calling printf:

According to a comment in another question, the stack shall be aligned at 16 bytes on systems (e.g. libraries, operating systems) that can use SSE instructions.

Assuming the stack pointer is aligned correctly when the function (main) is called, the call instruction subtracts 4 bytes from esp, so sub and push instructions must subtract exactly 12, 28, 40 ... bytes from esp to keep the stack pointer aligned correctly.

sub esp, 8

Obviously, in this case the compiler is not told to care for a 16-byte stack alignment.

And obviously the compiler allocates more stack than necessary in this case.

I have just told the compiler to generate an 8-byte and a 16-byte alignment for the stack; all other compiler options (and of course the source code) were the same.

The difference was that in the case of the 8-byte alignment, the compiler generated sub esp, 4, in the case of the 16-byte alignment sub esp, 20.

Obviously, this is a problem in the compiler optimization:

If sub esp,20 aligns the stack to 16 bytes, sub esp, 4 will also align to 16 bytes.

And using the "align to 8 byte" option shows that it would definitely possible to do a sub esp, 4 instead of a sub esp, 20.

This shows that some compilers reserve more stack than necessary for some unknown purposes.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
  • 1
    The `sub esp,8` example does *not* look like compiler output. For one thing, it's NASM syntax, so that would be some non-standard compiler already. It's barely plausible the C source could have used `static char msg[] = "print me!";` and so on to get those strings in the read-write `.data` section (instead of `.rodata`), or maybe a toy compiler could be bad enough to just always do that. Also, the `sub esp` is unnecessary if you're not going to re-align the stack by 16. This `main` doesn't return 0 (`xor eax,eax`), so could be C89 I guess? – Peter Cordes Nov 20 '20 at 20:00
  • 1
    By the time we invent enough excuses for a hobby-project compiler to have generated this code, it's at the same level as hand-written asm by someone who didn't really know what they were doing. So we might as well just say "whoever wrote this code", because it doesn't matter whether they did it directly or by writing a non-ABI-compliant compiler. (To be fair, only Linux's version of the i386 Sys V ABI requires 16-byte stack alignment; other OSes left it at 4 for 32-bit mode.) – Peter Cordes Nov 20 '20 at 20:01