Search code examples
functionassemblymemory-managementcallstack

Why is the use of a stack, manipulated by both a stack pointer and a base pointer, the proper solution to handle function frames?


Can anyone enlighten us from both technical & historical perspective why computers use a stack, manipulated with a stack pointer and a base pointer, with a specific process to follow like saving the return address, pushing the old value of the base pointer, etc...

For more information about the process followed to handle function's call stack check https://www.learnvulnerabilityresearch.com/stack-frame-function-prologue

My question is: why is it the proper solution?


Solution

  • It is not the proper solution, it is just a common solution. Using a frame pointer (in addition to a stack pointer) can be seen as simply wasting a register. One could easily manipulate the stack only as needed. You could do pushes and pops across the function allowing for some code sequences in a function to have less stack activity and higher performance.

    Why would you use a stack frame and worse add a frame pointer? It is easier to read, easier to debug. It is also easier to construct the function, the compiler will/can have determined the total stack usage for the whole function and can generate function prologue and epilogue based on that knowledge.

    May not be useful for x86, but other architectures you can gain a register back if you tell the compiler to not use frame pointers. Some architectures or with some compilers that register is not general purpose so the gains may be smaller.

    Because of this habit, some debug tools may rely on the frame pointer for unwinding functions. So like building for debug instead of release you are making a bigger slower program to maybe make debug easier.

    There is rarely if ever something considered the "proper solution", certainly not in this case, it is a choice. No one way to do something, no right way to do something. There are many right ways. Some may be better in certain use cases than others. That notion that there can be a right way, and that one popular instance uses some solution leads to others using that solution, without asking why, making it look like it is the only way. But dad all my friends are going to the party, well if all your friends jumped off the cliff...

    x86 is a old architecture both in its age but also its architecture of having 8 bit CISC instructions and not a lot of registers. Early calling conventions for an architecture like this are stack based, there is a dedicated frame pointer. As memory gets cheaper and performance gets better and compiled languages come along creating a desire for a function prologue and epilogue, etc, etc, etc.

    The folks that were in the offices or meetings at the time over the last several decades are not going to be found on this site, so we cannot get answers as to why specific compilers chose to use a stack frame (for each version). The best you can hope for here is opinion.

    The bottom line though is it is certainly not "the proper solution". It is simply "a" solution. Perhaps "a common" solution. For some architectures with popular compilers like gcc and clang, a frame pointer is not used by default, you have to force it if you want it. (still have a prologue and epilogue).