栈是向下生长的,%esp记录着栈顶,%ebp记录着当前的栈禎
# 以下是主调函数
_start:
push $3 # 函数参数
push $2 # 函数参数
call func # 调用函数
addl $8, %esp # 删除参数占用的堆栈空间
# 参数入栈后,执行call指令,call指令做两件事:
# 1.将当前call指令的下一条指令的地址入栈
# 2.修改%eip = function_addr,i.e. 让%eip等于被调函数的入口地址
.func:
pushl %ebp # 将_start中的%ebp栈禎入栈 create stack frame
movl %esp, %ebp # 将.func的栈禎保存到 ebp中
# i.e. %ebp 保存的永远是当前函数的栈禎
|->subl $8, %esp # 为local variable 申请两个局部变量空间,可能程序中使用两次pushl
|
| .....
| | 12(%ebp) | 就是第二个参数
| | 8(%ebp) | 就是第一个参数
| | 4(%ebp) | 是调用.func的_start函数中的addl $8, %esp的地址,i.e. 函数返回地址
| | %ebp | 是调用func之前的ebp
| | -4(%ebp) | 第一个局部变量
| | -8(%ebp) | 第二个局部变量 %esp
| .....
|
|->addl $8, %esp # 释放两个局部变量占用的栈内存,与前面的subl $8, %esp对应
movl ..., %eax # 设置返回值
movl %ebp, %esp # 将%esp设置为%ebp,这样下一步popl时
# 能把调用func前的%ebp弹出来
popl %ebp # 将%ebp恢复为调用func 前的%ebp
# destory stack frame
ret # ret指令与call指令是对应的
# ret 指令相当于popl %eip指令,经过popl后,
# %esp指向的是调用.func的_start函数中的
# addl 8, %esp的地址
注意观察:.func开头的两行 与 .func 的倒数2、3行是对应的
as -o x.o --gstabs+ x.s
ld -o x x.o