棧是向下生長的,%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