從彙編的角度分析函式呼叫過程(1)

2021-08-15 14:36:54 字數 2015 閱讀 8076

函式的引數傳遞有2種方式:堆疊方式暫存器方式

如果是堆疊方式傳遞的,就需要定義函式引數在堆疊中的傳遞順序,並約定函式被呼叫之後,由誰來平衡堆疊;

如果是暫存器方式傳遞的,就需要確定引數存放在哪個暫存器中。

每一種方式都有其優缺點,而且與使用的程式語言有關係,不存在哪種方式好與壞。

我們在開發中經常遇到呼叫約定型別,如__cdeclstdcallpascalfastcall。這些呼叫約定型別就用來指定函式引數的傳遞方式的。上面幾種約定型別,除了fastcall是使用暫存器方式傳遞引數外,其他的都是使用堆疊傳遞引數的。

visual studio中的c++工程,可以c++–>高階–>呼叫約定中進行呼叫約定的設定:

堆疊是一種「後進先出」的資料結構,esp暫存器始終指向棧頂。棧中資料位址從底部到頂部依次減小,也就是說,棧底對應高位址,棧頂對應低位址。

呼叫函式時,呼叫者依次把引數壓棧,然後呼叫函式,函式被呼叫之後,在堆疊中取得引數資料。函式呼叫結束以後,堆疊需要恢復到函式呼叫之前的樣子,而到底是由呼叫者來恢復還是由函式自身來恢復,根據不同的呼叫約定型別採用不同的方式。

約定型別

__cdecl

stdcall

pascal

fastcall

引數傳遞順序從右到左

從右到左

從左到右

使用暫存器

堆疊平衡者呼叫者

函式自身

函式自身

函式自身

__cdcel是c/c++/mfc程式預設的呼叫約定。

stdcall是絕大多數win32 api函式的約定方式,也有少部分使用__cdcel約定方式(如wsprintf等)。

在windows c/c++開發中常用的就是__cdeclstdcall這2種呼叫約定。

按照不同的呼叫約定來呼叫函式int add(int a, int b)。從呼叫者的視角來看,其彙編**分別表示如下:

__cdecl

push b     ;引數按從右到左傳遞

push a

call

addadd esp, 8

;呼叫者在函式外部平衡堆疊

stdcall

push b     ;引數按從右到左傳遞

push a

call

add;函式自己內部平衡堆疊,呼叫者不需要平衡堆疊

在函式呼叫過程中,引數入棧的過程如圖:

上圖中,ebp函式返回位址ret都是32位位址。因為函式呼叫完之後會將ebp恢復為暫存在堆疊中的原ebp值,所以從呼叫者角度來看,在函式的一次呼叫過程中ebp是不會變化的。

我們可以在函式中通過新的ebp獲取函式各個引數的值:

引數a =ebp + 0x8

引數b = ebp + 0xc

從彙編的角度分析函式呼叫過程(2)

include int add int a,int b int main 我們使用visual studio 2017編譯上面 並在在工程配置中將函式呼叫約定設定為 cdecl。在程式除錯過程中,可以在visual studio的反彙編視窗中看到c 對應的彙編 以及暫存器視窗中看到各個暫存器的值。m...

彙編看函式呼叫過程

分析下列原始碼 include dword stdcall function dword dwp1,pvoid p2 int main 轉彙編 vs2012 include dword stdcall function dword dwp1,pvoid p2 00fe1401 pop edi edi...

從彙編分析函式呼叫堆疊詳細過程

首先由下面簡單的 我們來考慮兩個問題 1.main函式呼叫sum,sum執行完以後,怎麼知道回到哪個函式中?2.sum函式執行完,回到main以後,怎麼知道從哪一行指令繼續執行的?int sum int a,int b intmain 首先,大家都知道函式執行的時候要在棧幀上開闢空間。乙個函式的呼叫...