Linux彙編教程11:函數與棧

Linux彙編教程1

開發一個程序,把所有的功能代碼都在一塊,會讓程序變得難以維護。爲了協助小組中的其他成員分工合作,我們需要把程序劃分成互相獨立的模塊。一個模塊問題不會牽連整個項目。一個程序有數千個函數構成,每一個函數實現一個功能,現在,我們開始學習函數部分。
一個函數有下面一個部分

函數名 —— 函數的名稱作爲一個標籤,代表函數代碼的起始位置。
函數參數 —— 函數參數是現實給函數處理的數據項
局部變量 —— 局部變量是函數進行處理時使用的數據存儲區,但函數返回是,變量的空間會被捨棄回收。函數中的局部變量,程序中的任何其他函數無法直接利用。
靜態變量 —— 靜態變量是函數處理時用到的數據存儲區,使用後的空間不會被捨棄。
全局變量 —— 全局變量是函數之外的管理數據的存儲區。
返回值 —— 返回一些數據或信息給調用了函數的部分。
返回地址 —— 返回地址不能直接在函數中使用。返回地址是但函數結束後接着執行代碼的位置。call指令會爲你處理返回地址,ret指令負責按照地址返回調用函數的地方。

下面一個部分,我們來講棧。

計算機的棧位於內存地址的最頂端。是用後進先出的方式,我們通過pushl指令來入棧,popl來出棧。我們的棧寄存器%esp包含指向當前棧頂的指針。當我們使用pushl入棧是,%esp中指針的值會減去4,當使用popl出棧的時候,%esp中指針的值會加4。
popl:

將棧頂數據彈出到某個內存位置或寄存器,相當於依次執行 movl (%esp), R/M和addl $4, %esp兩條指令

pushl:

將某個值入棧,相當於一次執行subl $4, %esp和 movl I/R/M, (%esp)兩條指令。
棧對於函數的局部變量、參數、返回地址的實現十分重要。在執行函數之前,會將函數的所有參數按逆序壓入棧內。接着call指令會實現兩個作用:把下一條指令的地址(也就是返回地址)壓入棧中;修改%eip中的指令指針以指向函數起始處。接着開始執行之前,棧中的分佈類似下面:
返回地址 << (%esp)
參數1
參數2
……
參數n
函數的參數被壓入棧中,最後入棧的是返回地址。現在%esp裏的指針指向返回地址。接下來函數本身還有一些操作:由於我們需要使用基址寄存器%ebp進行對棧中數據的索引訪問,所以我們需要把%ebp中原有的數據做一個保留。首先,通過pushl %ebp指令把%ebp入棧。%ebp是一個特殊的寄存器,用於訪問函數和局部變量;接着,執行movl %esp, %ebp指令,讓%ebp指針的值和現在的%esp一樣。現在的棧看起來是這樣:

原來的(%ebp) <<< (%esp) 和 (%ebp)
返回地址 <<< 4(%ebp)
參數1 <<< 8(%ebp)
參數2 <<< 12(%ebp)
……
參數n <<< (n+1)*4(%ebp)

現在就可以利用%ebp進行基址索引了,接下來,函數爲需要的局部變量開闢空間,我們只要移動棧指針就好了。如果我們需要3個字的內存,執行subl $12, %esp指令,這樣就有空間存儲變量,當函數返回時,這個堆棧也就消失了,這些變量也就沒有了。現在在棧中的情況是這樣:

局部變量3 <<< -12(%ebp) 和 (%esp)
局部變量2 <<< -8(%ebp)
局部變量1 <<< -4(%ebp)
原來的(%ebp) <<< (%ebp)
返回地址 <<< 4(%ebp)
參數1 <<< 8(%ebp)
參數2 <<< 12(%ebp)
……
參數n <<< (n+1)*4(%ebp)
當一個函數執行結束後,函數還會進行三個步驟

將返回值存儲到%eax
移除當前棧幀,並使調用代碼的棧幀恢復
將控制權交還給調用它的程序。利用ret指令

所以要讓函數返回,需要使用下面的指令:

movl %ebp, %esp
popl %ebp
ret

讓%esp中是值指向舊的%ebp中的值保存的地方,在把就的舊的%ebp中的值彈出到%ebp中,現在%esp的值就指向了返回地址,在使用ret,將棧頂的值彈出,並將指令指針寄存器%eip設置爲這個彈出值。

版權聲明

Moriarty_221爲本文的CSDN博客

如未註明,均爲原創,轉載請註明出處

轉載請註明:coskimo » Linux彙編教程11:函數與棧

版權所有 © 科斯基摩 | 本網站採用cc by-nc-sa 3.0協議進行授權

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章