函式呼叫約定與相關指令

2021-06-04 08:04:14 字數 2900 閱讀 4039

函式呼叫約定與相關指令

函式呼叫約定描述了函式傳遞引數方式和棧協同工作的技術細節。不同的作業系統、不同的語言、不同的編譯器在實現函式呼叫時的原理雖然基本相同,但具體的呼叫約定還是有差別的。這包括引數傳遞方式,引數入棧順序是從右向左還是從左向右,函式返回時恢復堆疊平衡的操作在子函式中進行還是在母函式中進行。表4-1-1列出了幾種呼叫方式之間的差異。

表4-1-1 呼叫方式之間的差異

csyscall

stdcall

basic

fortran

pascal

引數入棧順序

右→左右→左

右→左左→右

左→右左→右

恢復棧平衡操作的位置

母函式子函式

子函式子函式

子函式子函式

表4-1-2 函式呼叫約定

呼叫約定的宣告

引數入棧順序

恢復棧平衡的位置

__cdecl

右→左母函式

__fastcall

右→左子函式

__stdcall

右→左子函式

如果要明確使用某一種呼叫約定,只需要在函式前加上呼叫約定的宣告即可,否則預設情況下,vc會使用__stdcall的呼叫方式。本篇中所討論的技術在不加額外說明的情況下,都是指這種預設的__stdcall呼叫方式。

除了上邊的引數入棧方向和恢復棧平衡操作位置的不同之外,引數傳遞有時也會有所不同。例如,每乙個c++類成員函式都有乙個this指標,在windows平台中,這個指標一般是用ecx暫存器來傳遞的,但如果用gcc編譯器編譯,這個指標會作為最後乙個引數壓入棧中。

注意:同一段**用不同的編譯選項、不同的編譯器編譯鏈結後,得到的可執行檔案會有很多不同。因此,請您在進行後續實驗前務必注意實驗環境的描述,否則所得結果可能會與實驗指導有所差異。

函式呼叫大致包括以下幾個步驟。

(1)引數入棧:將引數從右向左依次壓入系統棧中。

(2)返回位址入棧:將當前**區呼叫指令的下一條指令位址壓入棧中,供函式返回時繼續執行。

(3)**區跳**處理器從當前**區跳轉到被呼叫函式的入口處。

(4)棧幀調整:具體包括。

儲存當前棧幀狀態值,已備後面恢復本棧幀時使用(ebp入棧)。

將當前棧幀切換到新棧幀(將esp值裝入ebp,更新棧幀底部)。

給新棧幀分配空間(把esp減去所需空間的大小,抬高棧頂)。

對於__stdcall呼叫約定,函式呼叫時用到的指令序列大致如下。

;呼叫前

push 引數3      ;假設該函式有3個引數,將從右向左依次入棧

push 引數2

push 引數1

call 函式位址    ;call指令將同時完成兩項工作:a)向棧中壓入當前指令在記憶體

;中的位置,即儲存返回位址。b)跳轉到所呼叫函式的入口位址函

;數入口處

push ebp        ;儲存舊棧幀的底部

mov ebp,esp    ;設定新棧幀的底部(棧幀切換)

sub esp,***    ;設定新棧幀的頂部(抬高棧頂,為新棧幀開闢空間)

上面這段用於函式呼叫的指令在棧中引起的變化如圖4.1.7所示。

圖4.1.7 函式呼叫時系統棧的變化過程

題外話:關於棧幀的劃分,不同參考書中有不同的約定。有的參考文獻中把返回位址和前棧幀ebp值做為乙個棧幀的頂部元素,而有的則將其做為棧幀的底部進行劃分。在後面的除錯中,您會發現ollydbg在棧區標示出的棧幀是按照前棧幀ebp值進行分界的,也就是說,前棧幀ebp值既屬於上乙個棧幀,也屬於下乙個棧幀,這樣劃分棧幀後,返回位址就成為了棧幀頂部的資料。出於前後概念一致的目的,在本書中將堅持按照ebp與esp之間的部分做為乙個棧幀的原則進行劃分。這樣劃分出的棧幀如圖4.1.7最後一幅圖所示,棧幀的底部存放著前棧幀ebp,棧幀的頂部存放著返回位址。劃分棧幀只是為了更清晰地了解系統棧的運作過程,並不會影響它實際的工作。

類似地,函式返回的步驟如下。

(1)儲存返回值:通常將函式的返回值儲存在暫存器eax中。

(2)彈出當前棧幀,恢復上乙個棧幀。

具體包括:

在堆疊平衡的基礎上,給esp加上棧幀的大小,降低棧頂,**當前棧幀的空間。

將當前棧幀底部儲存的前棧幀ebp值彈入ebp暫存器,恢復出上乙個棧幀。

將函式返回位址彈給eip暫存器。

(3)跳**按照函式返回位址跳回母函式中繼續執行。

還是以c語言和win32平台為例,函式返回時的相關的指令序列如下。

add esp, ***  ; 降低棧頂,**當前的棧幀

pop ebp        ; 將上乙個棧幀底部位置恢復到ebp,

retn            ; 這條指令有兩個功能:a)彈出當前棧頂元素,即彈出棧幀中的返回位址。

至此,; 棧幀恢復工作完成。b)讓處理器跳轉到彈出的返回位址,恢復呼叫前

的**區

按照這樣的函式呼叫約定組織起來的系統棧結構如圖4.1.8所示。

題外話:win32平台下有很多暫存器,intel指令集中的指令也有很多,現在立刻一一介紹它們無疑相當於給已經滿頭霧水的您再澆一桶冷水。雖然這裡僅僅列出了3個暫存器和幾條指令的作用,但只要您完全理解它們,就一定能順利理解本書的後續章節,因為它們是棧溢位利用的關鍵,也是計算機架構的核心所在。當然,入門以後要想提高到乙個新的層次,用《ibm x86彙編》或者《win32 彙編》惡補一下彙編知識是非常必要的。

圖4.1.8 函式呼叫的實現

函式呼叫約定(動態庫相關 )

cdecl c 和c 程式的預設呼叫規範。stdcall 標準呼叫約定 即winapi呼叫約定 也就是pascal呼叫約定。delphi就是採用 stdcall約定,所以在呼叫和c程式的動態庫時就會出現因為函式名稱改編問題導致找不到函式名的問題。函式呼叫約定的作用 指定函式在被呼叫時形參的壓棧順序 ...

函式呼叫約定與名字修飾約定

在windows下,由於很多語言支援動態鏈結庫技術,因此動態鏈結庫是一種很好的混合程式設計方法。語言對函式的約定有兩種 函式呼叫約定和名字修飾約定。不同語言預設的呼叫呼叫約定和函式的命名方式是不同的,要想不同的語言開發的動態鏈結庫能夠相互呼叫,那麼開發動態鏈結庫的語言和呼叫鏈結庫的語言的函式約定必須...

函式呼叫約定

函式呼叫約定有多種,這裡簡單說一下 1 stdcall 呼叫約定相當於16位動態庫中經常使用的 pascal 呼叫約定。在32位的vc 5.0中pascal呼叫約定不再被支援 實際上它已被定義為 stdcall。除了 pascal外,fortran和 syscall也不被支援 取而代之的是 stdc...