0

I'm trying to understand better how the stack works and I wrote this program.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define SIZE 0

void proof(){
    unsigned int buf[SIZE];
    unsigned int i = 0;

    printf("buf is at %X\n", (unsigned int)buf);
    //printf("return address is at buf[size+4] %X\n", (unsigned int)buf[SIZE+4]);
    //printf("return address is at buf[size]+16 %X\n", (unsigned int)&buf[SIZE]+12);
    printf("Effective return address %p\n", __builtin_return_address(0));
    printf("Proof address %p\n",&proof);
}

void main(){
    proof();
}

I have imagined the stack like this:

return address
previous frame pointer
the variable i
buf[SIZE]

From the output I can see that the return address of this function is placed in buf[size+4] but I don't get why. Shouldn't the return address always be at ebp+4? I have tried taking the address of ebp with gdb but it is not the same I have obtained from my code. Moreover, in this case if the stack is like I have imagined, shouldn't the return address be at &buf[SIZE]+12 since we are moving in multiply of 4?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
ccragusa
  • 17
  • 1
  • 1
    Look at the generated assembly code. – Jabberwocky Jul 09 '21 at 11:22
  • You seem to be assuming that the order of declaration of the automatic variables is identical to the order of the variables in memory. This assumption is not necessarily correct. The compiler is free to store variables in memory in any order it wishes. – Andreas Wenzel Jul 09 '21 at 11:53
  • 1
    Please note that this isn't valid standard C. What non-standard extensions for which compiler are you using to compile this with? – Lundin Jul 09 '21 at 12:48
  • 1
    And when I compile this as a stand-alone function in gcc -O3, it doesn't use the stack at all other than for function calls... – Lundin Jul 09 '21 at 12:49
  • 1
    EBP+4(bytes) is the same location as `&buf[size+4]`, i.e. in bytes `(char*)&buf + size*4 + 12`. Remember that C address math scales by the type width, and you're using a `uint32_t` array, but asm offsets are always in bytes. Also, it would be more accurate to talk about the compiler choosing to place other things relative to the return address, which is already on the stack when the function is entered. Also, EBP is only set up as a frame pointer if the compiler chooses to use it that way, same with other layout choices. Look at the generated asm. – Peter Cordes Jul 09 '21 at 19:48
  • @Lundin: `__builtin_return_address(0)` is GNU C, probably either gcc or clang. https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html – Peter Cordes Jul 09 '21 at 19:49
  • Note that this code doesn't ever print EBP. `__builtin_frame_address(0)` might return that, or some offset from EBP, I forget. It's more useful to just look at the compiler-generated asm (https://godbolt.org/) – Peter Cordes Jul 09 '21 at 19:52

0 Answers0