Let's look at some of the code:
main: call ini
This will push the value of the instruction pointer to the stack (so that you can later return to this position in the code) and go to the address label ini. The ret command uses the value stored on the stack to return from the subroutine.
The following is the initialization routine for the subroutine. It stores the values โโof some registers on the stack and sets the stack frame by copying the stack pointer (esp) to the base pointer register (ebp). If the routine has local variables, the stack pointer is reduced to make room for variables on the stack, and the base pointer is used to access local variables in the stack frame. In this example, the only local variable is the (unused) return value.
The push command decreases the stack pointer (esp) with the size of the data that will be pressed, and then saves the value to that address. The pop command does the opposite, first gets a value, then increments the stack pointer. (Note that the stack grows down, so the address of the stack pointer gets lower when the stack grows.)
ini: pushl %ebp // save ebp on the stack rrmovl %esp, %ebp // ebp = esp (create stack frame) pushl %ebx // save ebx on the stack pushl %eax // push eax on the stack (only to decrement stack pointer) irmovl $0, %eax // eax = 0 rmmovl %eax, -8(%ebp) // store eax at ebp-8 (clear return value)
The code follows the standard template, so it looks a bit uncomfortable when there are no local variables, and there is an unused return value. If there are local variables, subtraction will be used to reduce the stack pointer instead of pressing eax.
The following is the output sequence of the subroutine. It restores the stack to a position before creating the stack frame, and then returns to the code that calls the subroutine.
ini_finish: irmovl $4, %ebx // ebx = 4 addl %ebx, %esp // esp += ebx (remove stack frame) popl %ebx // restore ebx from stack popl %ebp // restore ebp from stack ret // get return address from stack and jump there
In response to your comments:
The ebx register is popped and unloaded to store its value. The compiler, apparently, always puts this code there, probably because the register is used very often, just not in this code. Similarly, a stack frame is always created by copying esp to ebp, even if it is not really needed.
An instruction that pushes eax exists only to reduce the stack pointer. This was done for small decrements, as it was shorter and faster than subtracting the stack pointer. The space that it reserves for the return value again, apparently, always does this, even if the return value is not used.
In your diagram, the esp register sequentially points to four bytes that are too high in memory. Remember that the stack pointer decreases after a value is pressed, so it will indicate the value pressed, not the next value. (The memory addresses are also deleted, something like 0x600, not 0x20, as indicated in the stack label declaration.)