函數調用約定

函數調用約定就是描述參數如何傳遞,堆棧由調用方還是被調用方平衡,返回值如何返回等規則。

函數調用約定的幾種類型有:__stdcall, __cdecl, __fastcall, __thiscall, __nakedcall, __pascal

下面介紹幾種常見的函數調用約定(以VS2010編譯器爲例):

(1) __cdecl調用約定

1. 參數從右向左傳遞,放在棧中

2. 棧平衡由調用函數來執行

3. 不定參數的函數可以使用

下面看一個彙編的例子

	int a = 1, b = 2;
 mov         dword ptr [a],1  
 mov         dword ptr [b],2  
	int sum = Sum(a, b);
 mov         eax,dword ptr [b]    // 參數從右向左壓入棧,壓入參數b
 push        eax  
 mov         ecx,dword ptr [a]    // 參數從右向左壓入棧,壓入參數a
 push        ecx  
 call        Sum (13611A9h)       // 調用函數
 add         esp,8                // 調用方平衡堆棧(彈出參數)
 mov         dword ptr [sum],eax  // 返回值保存在eax中
int __cdecl Sum(int a, int b)
{
 push        ebp                 // 保存上一層函數棧底指針
 mov         ebp,esp             // 設置本層函數棧底指針
 sub         esp,0C0h            // 設置本層函數棧頂指針
 push        ebx                 // 保存寄存器的值:ebx、esi、edi
 push        esi  
 push        edi  
 lea         edi,[ebp-0C0h]      // 棧內容賦初值(調試代碼中使用)
 mov         ecx,30h  
 mov         eax,0CCCCCCCCh  
 rep stos    dword ptr es:[edi]  
	return a+b;
 mov         eax,dword ptr [a]  
 add         eax,dword ptr [b]  // 將返回值保存在eax中
}
 pop         edi                // 恢復寄存器的值:edi、esi、ebx(相反的順序彈出)
 pop         esi  
 pop         ebx  
 mov         esp,ebp            // 恢復上層函數棧頂指針
 pop         ebp                // 恢復上層函數棧底指針
 ret                            // 沒有棧平衡操作

 __cdecl是c/c++默認的調用方式。從上面的彙編代碼可以看到,__cdecl調用方式在函數內沒有任何平衡參數的操作,而在退出函數後對esp執行了加8(add esp,8)的操作。C語言中經常使用的printf函數就是典型的__cdecl調用方式,由於printf的參數可以有多個,所以只能以__cdecl方式調用。

(2)__stdcall調用約定

1. 參數從右向左傳遞,放在棧中

2. 棧平衡操作由被調用函數執行

3. 不定參數的函數無法使用

看一下彙編的例子

	int a = 1, b = 2;
 mov         dword ptr [a],1  
 mov         dword ptr [b],2  
	int sum = Sum(a, b);
 mov         eax,dword ptr [b]    // 參數從右向左壓入棧,壓入參數b
 push        eax  
 mov         ecx,dword ptr [a]    // 參數從右向左壓入棧,壓入參數a
 push        ecx  
 call        Sum (13611C2h)  
 mov         dword ptr [sum],eax  // 沒有平衡棧操作,平衡操作由函數Sum內部完成
int __stdcall Sum(int a, int b)
{
 push        ebp  
 mov         ebp,esp  
 sub         esp,0C0h  
 push        ebx  
 push        esi  
 push        edi  
 lea         edi,[ebp-0C0h]  
 mov         ecx,30h  
 mov         eax,0CCCCCCCCh  
 rep stos    dword ptr es:[edi]  
	return a+b;
 mov         eax,dword ptr [a]  
 add         eax,dword ptr [b]  
}
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret         8                  // 平衡棧操作,棧彈出8個字節,等價於esp += 8
__stdcall與__cdecl除了棧平衡任務由誰來完成以外,其餘部分一樣。

(3)__fastcall調用約定

1. 最左邊的兩個不大於4字節的參數分別放在ecx和edx寄存器,其餘參數仍然從右到左壓入棧

2. 被調用方平衡棧

3. 不定參數無法使用

例子:

int a = 1, b = 2, c = 3;
 mov         dword ptr [a],1  
 mov         dword ptr [b],2  
 mov         dword ptr [c],3  
int sum = Sum(a, b, c);
 mov         eax,dword ptr [c]  
 push        eax  
 mov         edx,dword ptr [b]    // 最左邊的第二個參數由edx傳遞
 mov         ecx,dword ptr [a]    // 最左邊的第一個參數由ecx傳遞
 call        Sum (0E611CCh)  
 mov         dword ptr [sum],eax  // 沒有平衡棧操作,平衡棧操作由被調用方完成
int __fastcall Sum(int a, int b, int c)
{
 push        ebp  
 mov         ebp,esp  
 sub         esp,0D8h  
 push        ebx  
 push        esi  
 push        edi  
 push        ecx  
 lea         edi,[ebp-0D8h]  
 mov         ecx,36h  
 mov         eax,0CCCCCCCCh  
 rep stos    dword ptr es:[edi]  
 pop         ecx  
 mov         dword ptr [ebp-14h],edx  
 mov         dword ptr [ebp-8],ecx  
	return a+b+c;
 mov         eax,dword ptr [a]  
 add         eax,dword ptr [b]  
 add         eax,dword ptr [c]  
}
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret         4 // 平衡棧,4個字節,因爲前兩個參數是通過寄存器傳遞的,只有第三個參數壓入棧中了

但是,對於浮點值、遠指針和__int64類型總是通過棧來傳遞。

(4) __thiscall調用約定

thiscall僅僅用於c++成員函數。this指針存放於ecx寄存器中,參數從右到左壓棧,被調用方平衡棧。thiscall不是關鍵詞不能被程序員指定。

例子:

	int a = 1, b = 2;
 mov         dword ptr [a],1  
 mov         dword ptr [b],2  
	CTest test;
	int sum = test.Sum(a, b);
 mov         eax,dword ptr [b]  
 push        eax  
 mov         ecx,dword ptr [a]  
 push        ecx  
 lea         ecx,[test]             // 對象指針通過ecx傳遞
 call        CTest::Sum (1911D1h)  
 mov         dword ptr [sum],eax  
class CTest
{
public:
	int Sum(int a, int b)
	{
 push        ebp  
 mov         ebp,esp  
 sub         esp,0CCh  
 push        ebx  
 push        esi  
 push        edi  
 push        ecx  
 lea         edi,[ebp-0CCh]  
 mov         ecx,33h  
 mov         eax,0CCCCCCCCh  
 rep stos    dword ptr es:[edi]  
 pop         ecx  
 mov         dword ptr [ebp-8],ecx  
		return a+b;
 mov         eax,dword ptr [a]  
 add         eax,dword ptr [b]  
	}
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret         8     // 平衡棧




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