DLL中呼叫慣例和名稱修飾

來源:互聯網
上載者:User
寫了一篇關於DLL中呼叫慣例和名稱修飾的文章,其中還有很多內容不確定~請大家指教~謝謝~文檔中心中的連結:  
http://www.csdn.net/Develop/read_article.asp?id=25141  
http://www.csdn.net/develop/Read_Article.asp?Id=25142  
http://www.csdn.net/develop/Read_Article.asp?Id=25143  
=============================  
DLL中呼叫慣例和名稱修飾  
 
呼叫慣例(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()  
{  
           Add(1,2);                                                            //函數調用  
}  
 
int  __cdecl  Add(int  a,  int  b)                        //函數實現  
{  
           return  (a  +  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)  
{  
return  (a  +  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)  
{  
return  (a  +  b  +  c  +  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。當加在輸出函數前的關鍵字與編譯環境中的選擇不同時,直接加在輸出函數前的關鍵字有效。  
4、thiscall  
thiscall呼叫慣例是C++中的非靜態類成員函數的預設呼叫慣例。thiscall只能被編譯器使用,沒有相應的關鍵字,因此不能被程式員指定。採用thiscall約定時,函數參數按照從右至左的順序入棧,被調用的函數在返回前清理傳送參數的棧,只是另外通過ECX寄存器傳送一個額外的參數:this指標。  
 
這次的例子中將定義一個類,並在類中定義一個成員函數,代碼如下:  
 
class  CSum  
                 {  
public:  
int  Add(int  a,  int  b)  
{  
return  (a  +  b);  
}  
};  
 
 
---------------------------------------------------------------  
 
2.名稱修飾(Name  Decoration)  
C或C++函數在內部(編譯和連結)通過修飾名(Decoration  Name)識別。函數的修飾名是編譯器在編譯函數定義或者原型時產生的字串。編譯器在建立.obj檔案時對函數名稱進行修飾。有些情況下使用函數的修飾名是必要的,如在模組定義檔案裡頭指定輸出C++重載函數、建構函式、解構函式,又如在彙編代碼裡調用C或C++函數等。  
 
在VC++中,函數修飾名由編譯類型(C或C++)、函數名、類名、呼叫慣例、傳回型別、參數等多種因素共同決定。下面分C編譯、C++編譯(非類成員函數)和C++類及其成員函數編譯三種情況說明:  
1、C編譯時間函數名稱修飾  
當函數使用__cdecl呼叫慣例時,編譯器僅在原函數名前加上一個底線首碼,格式為_functionname。例如:函數int  __cdecl  Add(int  a,  int  b),輸出後為:_Add。  
 
當函數使用__stdcall呼叫慣例時,編譯器在原函數名前加上一個底線首碼,後面加上一個@符號和函數參數的位元組數,格式為_functionname@number。例如:函數int  __stdcall  Add(int  a,  int  b),輸出後為:_Add@8。  
 
當函數是用__fastcall呼叫慣例時,編譯器在原函數名前加上一個@符號,後面是加一個@符號和函數參數的位元組數,格式為@functionname@number。例如:函數int  __fastcall  Add(int  a,  int  b),輸出後為:@Add@8。  
 
       以上改變均不會改變原函數名中的字元大小寫。  
2、C++編譯時間函數(非類成員函數)名稱修飾  
當函數使用__cdecl呼叫慣例時,編譯器進行以下工作:  
 
1.以?標識函數名的開始,後跟函數名;  
2.函數名後面以@@YA標識開始,後跟傳回值和參數表;  
3.當函數的傳回值或者參數與C++類無關的時候,傳回值和參數表以下列代號表示:  
           B:const  
D:char  
E:unsigned  char  
F:short  
G:unsigned  short  
H:int  
I:unsigned  int  
J:long  
K:unsigned  long  
M:float  
N:double  
_N:bool  
PA:指標(*,後面的代號表明指標類型,如果相同類型的指標連續出現,以0  
代替,一個0代表一次重複)  
           PB:const指標  
           AA:引用(&)  
           AB:const引用  
U:類或結構體  
V:Interface(介面)  
W4:enum  
X:void  
4、@@YA標識之後緊跟的是該函數的傳回值類型,其後依次為參數的資料類型,指標標識在其所指資料類型前。當函數的傳回值或者參數與C++類無關的時候,其處理符合本條規則,否則按照5、6規則處理;  
5、當函數傳回值為某個類或帶有const性質的類的時候,傳回值的命名為:?A/?B+V+類名+@@(不帶加號)。當函數傳回值為某個類的指標/引用或者帶有const性質的類的指標/引用的時候,傳回值的命名為:PA/AA或者PB/AB+V+類名+@@(不帶加號);  
6、函數參數為某個類的時候,並且該參數所使用的類曾經出現過的話(也就是與函數傳回值所使用的類相同或者與前一個參數使用的類相同),則該參數類型格式為:V+1+@(不帶加號)。如果該參數所使用的類沒有出現過的話,則該參數類型格式為:V+類名+@@(不帶加號)。函數參數為某個類的指標/引用或者帶有const性質指標/引用的時候,則該參數類型格式是在上述格式的基礎上在V前面加上代表指標/參考型別或者帶有const性質指標/參考型別的標識符(PA/AA或PB/AB);  
7、參數表後以@Z標識整個名字的結束,如果該函數無參數

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.