0

How does gcc decides how much memory allocate for stack and why does it not decrement %rsp anymore when I remove printf() (or any function call) from my main?

1. I noticed when I played around with a code sample: https://godbolt.org/z/fQqkNE that the 6th line in gcc assembly viewer subq $48, %rsp gets removed if I remove printf() from my C code on line 22. It looks like when I don’t make any function calls from within my main, then the %rsp does not get decremented, but data still gets allocated based on %rbp and offsets. I thought %rsp changes only when stack grows. My theory is that since it won’t make any other function calls, it knows that it won’t need to keep stack for other nonexistent functions. But shouldn’t %rsp still grow as data is getting saved?

2. When adding variables to my rect struct, I also noticed that it sometimes allocates memory in steps greater than what the added data type size was. What is the convention it follows when deciding how much memory to allocate to stack?

3. Is there an online tool that would take assembly code as input, and then draw an image of stack and tell me state of every register at any point of execution? Godbolt.org is a very good tool, I just wish it had these 2 extra features.

I'll paste the code below in case the link to godbolt stops working in the future:

#include <stdio.h>
#include <stdint.h>
struct rect {
    int a;
    int b;
    int* c;
    int d[2];
    uint8_t f;
};

int main() {
    int arr[2] = {2, 3};
    struct rect Rect;
    Rect.a = 10;
    Rect.b = 20;
    Rect.c = arr;

    Rect.d[0] = Rect.a;
    Rect.d[1] = Rect.b;

    Rect.f =255;
    printf("%d and %d", Rect.a, Rect.b);

    return 0;
}
.LC0:
        .string "%d and %d"
main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $48, %rsp
        movl    $2, -8(%rbp)
        movl    $3, -4(%rbp)
        movl    $10, -48(%rbp)
        movl    $20, -44(%rbp)
        leaq    -8(%rbp), %rax
        movq    %rax, -40(%rbp)
        movl    -48(%rbp), %eax
        movl    %eax, -32(%rbp)
        movl    -44(%rbp), %eax
        movl    %eax, -28(%rbp)
        movb    $-1, -24(%rbp)
        movl    -44(%rbp), %edx
        movl    -48(%rbp), %eax
        movl    %eax, %esi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        leave
        ret

P.S.: The book I follow uses AT&T syntax for teaching x86. Which is weird because it makes finding online tutorials much harder.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
miran80
  • 945
  • 7
  • 22
  • 1
    x86-64 System V has a red zone, 128 bytes below RSP that won't get stepped on asynchronously by signal handlers or whatever. GCC can use that for locals if it's not going to make any function calls itself (which would *synchronously* use space below RSP). See [Why does the compiler reserve a little stack space but not the whole array size?](https://stackoverflow.com/q/51523127) – Peter Cordes Apr 08 '20 at 11:40
  • 1
    2. GCC likes to keep the stack aligned by 16, and must do so before a function call. (So it knows the stack was aligned by 16 before the `call` to `main`.) Plus you compiled with optimization disabled, and there can easily be wasted space even with optimizations. – Peter Cordes Apr 08 '20 at 11:44
  • 1
    3. I'm not aware of any visualization tools. You can compile with `-fverbose-asm` to have GCC annotate every instruction with the C names of the operands, though. It works fairly well in braindead debug mode (the default `-O0`); with optimization enabled C vars tend to get replaced by invented temporaries. – Peter Cordes Apr 08 '20 at 11:47

0 Answers0