t stack is
base address for main <-- %EBP
....
top address of stack of main <-- %ESP
when we call this @frame, the new stack is
base address for main <-- %EBP
....
return address of caller
top address of stack of main <-- %ESP
then let us examine the progress of callee, the assemble is :
frame:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
leave
ret
As we can see, It will save the frame information of caller. and then create a new frame. When we execute the first command:
pushl %ebp
the stack frame is
bottom of stack of main <-- %EBP
....
return address of caller
base address of frame of caller
top of stack of main <-- %ESP
when we execute the second instruction:
movl %ESP, %EBP;
the new stack frame is:
stack bottom of main
....
return address of caller
base address of frame of caller
stack top of main <-- %EBP
stack top of callee <-- %ESP
Actually, the stack top of caller is the stack bottom of callee. So far,we didn't allocate memory space for local variable of this function. So the stack bottom of callee and stack top of callee is same temporarily. But in the next instruction, we will allocate space:
subl $16, %esp
As we can see, the memory space allocated is 16 bytes because of some reason about memory alignment and the like. Actually, we just use the first 4 bytes. The new stack is :
stack bottom of main
....
return address of caller
base address of frame of caller
stack top of main <-- %EBP
local variable b
....
stack top of callee <-- %ESP
So far, we have been build a valid stack frame for this new function call.
The next question is how can we resume the frame of caller when we complete this subroutine?
That is easy . Recall the frame above, we just need :
movl %EBP, %ESP;
pop %EBP;
Actually, there is another more simpler instruction--leave. It will complete those two steps.
Now, the stack is :
stack bottom of main <-- %EBP
....
return address of caller
stack top of main <-- %ESP
It is seems like all of things become OK.
4. How can we pass parameters between caller and callee ?
Usually, Pass parameters is necessary when we call a function. Where should be the place we reside
those data ? Let us see the example below, we call a function with several parameters.
int main()
{
int ret;
...
ret = parameters(1,2,3);
...
}
int parameters( int a, int b, int c)
{
int sum;
sum = a + b + c;
return sum;
}
The corresponding assemble code is :
main:
...
movl $3, 8(%esp)
movl $2, 4(%esp)
movl $1, (%esp)
call parameters
movl %eax, -8(%ebp)
...
parameters:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl 12(%ebp), %eax
addl 8(%ebp), %eax
addl 16(%ebp), %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
ret
Now, examine those instructions.Before we call this function, the stack is :
stack bottom of main <-- %EBP
...
stack top of main <-- %ESP
Then , we push three parameters in reverse order:
stack bottom of main <-- %EBP
...
3 <-- 3th parameter
2
1
stack top of main <-- %ESP
Then,
call parameters????????????
This is same as we analysis above. we jump to the new instruction stream, and build a new stack frame.
stack bottom of main
...
3 <-- 3th parameter
2
1
return address of caller
base address of frame of caller
stack top of main <-- %EBP
...
stack top of callee <-- %ESP
In subroutine, if we need parameters, we can simply get it.