DLL 中呼叫約定和名稱修飾

2021-04-21 08:16:26 字數 3353 閱讀 4949

呼叫約定(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 編譯器往往按照...