With most architecture+compiler combinations, a register is dedicated to holding the frame pointer, a pointer to a fixed position in the stack frame of a function containing the return address to the caller. For x86, ebp (rbp in 64 bit mode) is used for this purpose, and a stack frame looks something like this:
var_2 at [ebp-8] <-- top of stack, lowest address (yes this is counter-intuitive)
var_1 at [ebp-4]
return_address [ebp] <-- ebp points here
arg_1 at [ebp+4]
arg_2 at [ebp+8]
No matter how much data you push onto the stack in local variables (even variable length structures eg with alloca or variable length arrays in C), the parameters are always accessible at positive offsets from ebp while the (fixed-size) local variables are at negative offsets.Without a frame pointer, parameters must be accessed relative to the stack pointer instead. Disadvantages of this include:
Both parameters and local variables are at positive offsets in comparison to esp, so it's harder to tell the two apart.
The offsets can change throughout the function as variables are pushed and sometimes popped from the stack, making analysis even more difficult.
You can't allocate variable width structures on the stack because then the compiler would not know at what offsets the parameters/variables would lie.
And it's not even necessarily a performance win either, because x86 uses longer instruction encodings for sp-relative addressing vs. bp-relative addressing.
x86_64 and most RISC architectures don't have this problem, there are enough spare registers that the negatives of -fomit-frame-pointer outweigh the positives.
Ok, it's officially past my bedtime.
what you REALLY should wonder is, something like x86, but with code pointers stored in a separate address space.
void dangerous() {
char buf[10];
gets(buf); // the return in gets could be redirected
}