Why is ebp being pushed here?
The programmer/compiler is using %ebp
as a frame pointer. Since %ebp
is a call-preserved register, in order to use it, its original value must be preserved first, so that this original value can be restored to that register before returning.
The frame pointer is used in a displacement addressing mode to access parameters and local variables. It is not
always necessary to use a frame pointer in 32-bit x86 code because the stack pointer can be used as the base register instead (with an adjusted displacement value), yet still common practice. Historically, the 16-bit 8086 did not support the stack pointer as a base register in displacement addressing, so a frame pointer was required for that. See here for more discussion, especially on how a frame pointer was needed on the 16-bit 8086.
ebp is popped once in end_factorial and pushed as many times as factorial is called, what happens to the rest of those pushes?
The processor does not see labels — they are removed by the assembler during the production of machine code.
The processor only sees machine code instructions, and thus, only machine code instructions influence the flow of control.
Every machine code instruction informs the processor what instruction to run next. Many instructions simply tell the processor to proceed with the next sequential instruction — and imull
does that. The je
instruction says to either jump to the label (condition true) or else continue sequentially (condition false).
So, when you're interpreting the flow of control in assembly language, mind that labels do nothing with/for the processor — labels only inform the assembler of numbers to use when encoding real machine code instructions that use a label and they are otherwise removed and not seen by the processor.
end_factorial
is a part of factorial
, and will execute by either the change in flow of control caused by the je end_factorial
, or by the sequential flow of control from after the imull
, depending on conditional logic (whether at the base condition or not).
In the explanation part, what is that eip and where is it even pushed into the stack?
%eip
is the instruction pointer, also known as the program counter in other architectures. It is the register that sequences the instructions. The call
instruction pushes the return address onto the stack, effectively suspends execution of the caller, and transfers control to the callee, which accomplishes at least the control flow part of a function call. When the callee is ready to return to the caller, it uses ret
which takes the return address value off of the stack and puts it into the instruction pointer register — that effectively terminates the callee and also resumes the caller, at the instruction after the call. The return address can be thought of as a (hidden from C) parameter passed from caller to callee, so that the same callee can return to any caller at any call site.
For example, main
could call factorial
twice, from two different places in main
, and each invocation would provide a different return address value, so that the suspended main
is resumed following the proper call site (the one that invoked this call to factorial). Further, main
can call factorial
, and also main
can call foo
which can call factorial
. The return address mechanism supports general purpose returning to the proper caller.