彙編堆疊平衡的幾種方式

2021-07-31 10:54:21 字數 2122 閱讀 6734

任何程式在執行過程中都需要使用堆疊,作業系統為每乙個程式(程序及執行緒)設定乙個堆疊。在使用高階語言程式設計時,源程式中使用的函式呼叫、區域性變數都要用到堆疊,由編譯器來負責生成有關的機器指令。我的理解,堆疊就是維護當前執行緒中執行狀態的乙個資料結構,這種狀態包括:需要傳遞的變數,函式的返回位址,區域性變數等等。

與堆疊相關的 3 個暫存器是:ss, esp, ebp。

esp 暫存器中的內容作為堆疊的當前指標。push, pop, call, ret 等指令都與堆疊有關,使用 ss:esp 指向堆疊單元。

ebp 暫存器中的內容作為堆疊的「基準」指標。ss:ebp 指向的位址作為基準位址。在函式(子程式)內部,可以使用 [ebp+立即數] 的形式來取得主程式傳遞的引數,使用 [ebp-立即數] 的形式來訪問區域性變數。

從上述的描述可以看出,整個堆疊的是乙個經典的先入後出的棧結構,在棧中還存在著「棧幀」這樣的結構,用來表示當前函式執行的環境,裡面存放著當前函式的區域性變數,所需的返回位址。棧幀由 ebp 所指出,ebp 和 esp 之間即為當前函式幀,呼叫函式和被呼叫函式的棧幀是相鄰存放,這樣即可通過 ebp加減固定的數,來獲取主調函式所傳遞的函式引數。

上述的堆疊結構如下:

堆疊是經過精心設計的結構,使得程式設計人員能夠方便的設計函式來實現結構化設計,高階語言也得益於這種結構,c 語言基本上就是彙編的一種簡單的翻譯。這裡要強調的是,在用匯編寫程式的時候,完全可以不按這種結構來設計,比如引數我們可以放到指定的記憶體區,被呼叫的函式到指定的記憶體區去取出傳遞給它的引數;也可以讓函式引數全部通過暫存器來傳送,尤其是在 arm 這種暫存器特別多,而且每個暫存器的地位都相同的處理器之中。之所以要設計出堆疊,就是為了能夠以統一的方式來編寫程式。

對於 c 語言,函式有好幾種呼叫規則。最常見的是兩種,cdecl 方式和 stdcall 方式。

cdecl 方式

(1)使用堆疊傳遞引數

(2)主程式按從右向左的順序將引數逐個壓棧,最後乙個引數先入棧。每乙個引數壓棧一次。

(3)在子程式中,使用 [ebp+x] 的方式來訪問引數。x=8 代表第 1 個引數;x=12 代表第二個引數,依次類推

(4)子程式用 ret 指令返回。

(5)由主程式執行「add esp, n」指令調整 esp,達到堆疊平衡。

(6)一般返回值放在 eax 中

stdcall 方式

與cdecl的不同是,堆疊的平衡不是由主程式完成,而是由子程式通過呼叫「ret n」指令主動平衡。

這些呼叫規則,都是針對 c 語言來說的,在 c 語言和彙編需要互相呼叫的情況;c 語言編寫的不通的二進位制函式庫之間,都要注意函式的呼叫規則。

下面舉乙個普通的例子

[plain]view plain

copy

addproc1    proc   ; cdecl方式  

mov     ebp, esp  

mov     eax, dword ptr [ptr+8]  ; 取第乙個引數   

add     eax, dword ptr [ptr+12] ; 取第二個引數  

pop     ebp    ; 恢復棧幀為呼叫者,但這時候堆疊還未平衡  

ret  

addproc1    endp  

addproc2    proc   ; stdcall  

push    ebp  

mov     ebp, esp  

mov     eax, dword ptr [ptr+8]  

add     eax, dword ptr [ptr+12]  

pop     ebp  

ret     8    ; 和前面一樣,唯一不同是,直接由子程式恢復堆疊,8 正好是兩個引數的長度  

addproc2    endp  

start:  

push    20  

push    10  

call    addproc1  

add     esp, 8   ; 由主調函式恢復堆疊  

push    50  

push    60  

call    addproc2  

ret  

end start  

記憶體中的堆疊 堆疊平衡

1 記憶體中的堆疊使用是先從 高位的位址 到 低位的位址 使用 儲存的。2 堆疊使用的時候,最後需要進行堆疊平衡,也就是去平衡esp中暫存器儲存的值 3 esp暫存器中儲存的值對應的就是當前堆疊使用的位置 4 如果當前的壓入的堆疊資料 不是通過push指令 後面不需要的話,一般就是在sub esp,...

彙編中的堆疊傳參

這個堆疊傳參,就好比你想吃糖,可是你左邊的口袋裡已經裝滿了瓜子,所以你只能,先把糖放入右邊口袋,當你需要吃糖的時候。在從右邊口袋裡把糖取出來,就這麼簡單,這就是堆疊傳參,左邊口袋是通用暫存器,右邊口袋就是堆疊。糖果就是引數,向口袋放糖和從口袋拿糖就是呼叫堆疊,吃糖這個動作就是執行程式。當我們在執行函...

組合語言 6 利用堆疊傳遞引數及堆疊的修正

一 呼叫子程式時資訊的保護與恢復方法 1 在子程式中進行 subroute proc push axpush bxpush cx.popcx popbx popax retsubroute endp 2 在主程式中進行 push axpush bxpush cxcall subroute popcx...