3

Here is a simple function

#include <stdio.h>

int foo() {
    int a = 3;
    int b = 4;
    int c = 5;
    return a * b * c;
}

int main() {
    int a = foo();
}

And the assembly for foo() looks like

foo:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 3
        mov     DWORD PTR [rbp-8], 4
        mov     DWORD PTR [rbp-12], 5
        mov     eax, DWORD PTR [rbp-4]
        imul    eax, DWORD PTR [rbp-8]
        imul    eax, DWORD PTR [rbp-12]
        pop     rbp
        ret

as seen with the rbp - N, the inner stack frame is being modified. So, why are there no leaves or add rsp, n?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Happy Jerry
  • 164
  • 1
  • 8

1 Answers1

9

It "isn't modified" in the sense that the stack pointer does not change. Since we never subtracted any offset from rsp on entry, of course we should not add any on exit.

And leave is simply the equivalent of mov rsp, rbp ; pop rbp. We did include pop rbp, and mov rsp, rbp would be redundant since as you can see, rsp and rbp are still equal at that point in the code.

In fact, this function stores its local variables on the stack without adjusting the stack pointer to "make space" for them, so they end up below the stack pointer. This is legal because the x86-64 SysV ABI mandates a red zone; code that does not make any function calls may safely use 128 bytes below the stack pointer. Signal handlers are guaranteed not to overwrite it.

Why does the x86-64 GCC function prologue allocate less stack than the local variables?

Where exactly is the red zone on x86-64?

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 1
    As good a red-zone explanation as it gets. Short, concise and to the point. – David C. Rankin May 13 '22 at 06:25
  • 1
    Fun fact: Windows x64 can also avoid moving RSP, since the shadow space (32 bytes above the return address) is big enough to hold the locals. Solving the same problem as the red-zone, with different tradeoffs. https://godbolt.org/z/Mc6z1YWfj shows MSVC doing that optimization (but only with optimization enabled and `volatile` to still make it store), but GCC (and clang) missing it with `__attribute__((ms_abi))`. – Peter Cordes May 13 '22 at 13:15