The parameters are pushed on the stack and then function calls are made. So the parameters are just below the return address. If a function wants to read the parameters, then it can read from the stack just below the return address.
Let's see this in an example
void fun(int x, int y)
{
x++;
x += y;
}
int main()
{
fun(2, 3);
}
Here is the generated assembly code:
.text
.globl fun
fun:
pushl %ebp
movl %esp, %ebp
addl $1, 8(%ebp)
movl 12(%ebp), %eax
addl %eax, 8(%ebp)
popl %ebp
ret
.globl main
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
movl $3, 4(%esp)
movl $2, (%esp)
call fun
leave
ret
Location of local variables of the stack (local variables are explained here)
x ==> 8(%ebp) y ==> 12(%ebp), %eax
This is very much related to what is described in the local variable chapters. The parameters are allocated as the local variables only. The only one difference is that the local variables are allocated on the top where the return address is pushed on the stack and the parameters lie beneath the return address.
The parameter passing can be explained with this diagram:
Comments on the generated code:
# .text segment starts
.text
# export the the function fun
.globl fun
# function fun starts here
fun:
# save ebp on stack
pushl %ebp
movl %esp, %ebp
# x++;
addl $1, 8(%ebp)
# tmp = y
movl 12(%ebp), %eax
# x = x + tmp
addl %eax, 8(%ebp)
#restore ebp and return
popl %ebp
ret
# export the main function
.globl main
# main function starts here
main:
# save the ebp register
pushl %ebp
movl %esp, %ebp
# stack_pointer -= 8
subl $8, %esp
# push the second parameter
movl $3, 4(%esp)
# push the first parameter
movl $2, (%esp)
# call the function fun
call fun
# return the main function
leave
ret