呼叫約定(calling convention)是指在程式語言中為了實現函式呼叫而建立的一種協議。這種協議規定了該語言的函式中的引數傳送方式、引數是否可變和由誰來處理堆疊等問題。不同的語言定義了不同的呼叫約定。
在c++中,為了允許操作符過載和函式過載,c++編譯器往往按照某種規則改寫每乙個入口點的符號名,以便允許同乙個名字(具有不同的引數型別或者是不同的作用域)有多個用法,而不會 打破現有的基於c的鏈結器。這項技術通常被稱為名稱改編(name mangling)或者名稱修飾(name decoration)。許多c++編譯器廠商選擇了自己的名稱修飾方案。
因此,為了使其它語言編寫的模組(如visual basic應用程式、pascal或fortran的應用程式等)可以 呼叫c/c++編寫的dll的函式,必須使用正確的呼叫約定來匯出函式,並且不要讓編譯器對要匯出的函式進行任何名稱修飾。
1.呼叫約定(calling convention)
呼叫約定用來處理決定函式引數傳送時入棧和出棧的順序(由呼叫者還是被呼叫者把引數彈出棧),以及編譯器用來識別函式名稱的名稱修飾約定等問題。在microsoft vc++ 6.0中定義了下面幾種呼叫約定,我們將結合組合語言來一一分析它們:
1、__cdecl
__cdecl是c/c++和mfc程式預設使用的呼叫約定,也可以在函式宣告時加上__cdecl關鍵字來手工指定。採用__cdecl約定時,函式引數按照從右到左的順序入棧,並且由呼叫函式者把引數彈出棧以清理堆疊。因此,實現可變引數的函式只能使用該呼叫約定。由於每乙個使用__cdecl約定的函式都要包含清理堆疊的**,所以產生的可執行檔案大小會比較大。__cdecl可以寫成_cdecl。
下面將通過乙個具體例項來分析__cdecl約定:
在vc++中新建乙個win32 console工程,命名為cdecl。其**如下:
int __cdecl add(int a, int b); //函式宣告
void main()
int __cdecl add(int a, int b) //函式實現
函式呼叫處反彙編**如下:
;add(1,2);
push 2 ;引數從右到左入棧,先壓入2
push 1 ;壓入1
call @ilt+0(add) (00401005) ;呼叫函式實現
add esp,8 ;由函式呼叫清棧
2、__stdcall
__stdcall呼叫約定用於呼叫win32 api函式。採用__stdcal約定時,函式引數按照從右到左的順序入棧,被呼叫的函式在返回前清理傳送引數的棧,函式引數個數固定。由於函式體本身知道傳進來的引數個數,因此被呼叫的函式可以在返回前用一條ret n指令直接清理傳遞引數的堆疊。__stdcall可以寫成_stdcall。
還是那個例子,將__cdecl約定換成__stdcall:
int __stdcall add(int a, int b)
函式呼叫處反彙編**:
; add(1,2);
push 2 ;引數從右到左入棧,先壓入2
push 1 ;壓入1
call @ilt+10(add) (0040100f) ;呼叫函式實現
函式實現部分的反彙編**:
;int __stdcall add(int a, int b)
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
lea edi,[ebp-40h]
mov ecx,10h
mov eax,0cccccccch
rep stos dword ptr [edi]
;return (a + b);
mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0ch]
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 8 ;清棧
3、__fastcall
__fastcall約定用於對效能要求非常高的場合。__fastcall約定將函式的從左邊開始的兩個大小不大於4個位元組(dword)的引數分別放在ecx和edx暫存器,其餘的引數仍舊自右向左壓棧傳送,被呼叫的函式在返回前清理傳送引數的堆疊。__fastcall可以寫成_fastcall。
依舊是相類似的例子,此時函式呼叫約定為__fastcall,函式引數個數增加2個:
int __fastcall add(int a, double b, int c, int d)
函式呼叫部分的彙編**:
;add(1, 2, 3, 4);
push 4 ;後兩個引數從右到左入棧,先壓入4
mov edx,3 ;將int型別的3放入edx
push 40000000h ;壓入double型別的2
push 0
mov ecx,1 ;將int型別的1放入ecx
call @ilt+0(add) (00401005) ;呼叫函式實現
函式實現部分的反彙編**:
; int __fastcall add(int a, double b, int c, int d)
push ebp
mov ebp,esp
sub esp,48h
push ebx
push esi
push edi
push ecx
lea edi,[ebp-48h]
mov ecx,12h
mov eax,0cccccccch
rep stos dword ptr [edi]
pop ecx
mov dword ptr [ebp-8],edx
mov dword ptr [ebp-4],ecx
;return (a + b + c + d);
fild dword ptr [ebp-4]
fadd qword ptr [ebp+8]
fiadd dword ptr [ebp-8]
fiadd dword ptr [ebp+10h]
call __ftol (004011b8)
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 0ch ;清棧
關鍵字__cdecl、__stdcall和__fastcall可以直接加在要輸出的函式前,也可以在編譯環境的setting...->c/c++->code generation項選擇。它們對應的命令列引數分別為/gd、/gz和/gr。預設狀態為/gd,即__cdecl。當加在輸出函式前的關鍵字與編譯環境中的選擇不同時,直接加在輸出函式前的關鍵字有效。
DLL中呼叫約定和名稱修飾
dll中呼叫約定和名稱修飾 一 呼叫約定 calling convention 是指在程式語言中為了實現函式呼叫而建立的一種協議。這種協議規定了該語言的函式中的引數傳送方式 引數是否可變和由誰來處理堆疊等問題。不同的語言定義了不同的呼叫約定。在c 中,為了允許操作符過載和函式過載,c 編譯器往往按照...
DLL中呼叫約定和名稱修飾(二)
thiscall thiscall 呼叫約定是 c 中的非靜態類成員函式的預設呼叫約定。thiscall 只能被編譯器使用,沒有相應的關鍵字,因此不能被程式設計師指定。採用 thiscall 約定時,函式引數按照從右到左的順序入棧,被呼叫的函式在返回前清理傳送引數的棧,只是另外通過 ecx暫存器傳送...
DLL中呼叫約定和名稱修飾(一)
dll中呼叫約定和名稱修飾 一 呼叫約定 calling convention 是指在程式語言中為了實現函式呼叫而建立的一種協議。這種協議規定了該語言的函式中的引數傳送方式 引數是否可變和由誰來處理堆疊等問題。不同的語言定義了不同的呼叫約定。在c 中,為了允許操作符過載和函式過載,c 編譯器往往按照...