C/C++/动态链接库DLL中函数的调用约定与名称修饰
參見:http://blog.twofei.com/cc/impl/calling-convension.html
??? 調用約定(Calling Convention)是指在程序設計語言中為了實現函數調用而建立的一種協議.
這種協議規定了該語言的函數中的參數傳送方式,參數是否可變和由誰來處理堆棧等問題. 不同的語
言定義了不同的調用約定.
??? 在C++中,為了允許操作符重載和函數重載,C++編譯器往往按照某種規則改寫每一個入口點的符號名,
以便允許同一個名字(具有不同的參數類型或者是不同的作用域)有多個用法,而不會打破現有的基于C的鏈
接器. 這項技術通常被稱為名稱改編(Name Mangling)或者名稱修飾(Name Decoration). 許多C++編譯
器廠商選擇了自己的名稱修飾方案.
??? 因此,為了使其它語言編寫的模塊(如Visual Basic應用程序,Pascal或Fortran的應用程序等)可以調
用C/C++編寫的函數,必須使用正確的調用約定來導出函數,并且不要讓編譯器對要導出的函數進行任何名稱修飾.
1.調用約定(Calling Convention)
??? 調用約定用來處理決定函數參數傳送時入棧和出棧的順序(由調用者還是被調用者把參數彈出棧),以及編譯
器用來識別函數名稱的名稱修飾約定等問題. 在Microsoft VC++ 6.0中定義了下面幾種調用約定,我們將結合匯
編語言來一一分析它們:
*** C語言中的調用約定 ***
??? 1. __cdecl
??????? __cdecl是C/C++(非類成員函數以及靜態類成員函數)程序默認使用的調用約定,也可以在函數聲明時
??? 加上 __cdecl 關鍵字 來手工指定. 采用__cdecl約定時,函數參數按照 從右到左 順序入棧,并且由調用
??? 函數者把參數彈出棧以清理堆棧. 因此,實現可變參數的函數只能使用該調用約定. 由于每一個使用
??? __cdecl約定的函數都要包含清理堆棧?? 的代碼,所以產生的可執行文件大小會稍大一丁點.
??? 下面將通過一個具體實例來分析__cdecl約定:
? 函數調用處反匯編代碼如下:
main:0134474E 6A 02 push 2 ;壓入參數2(最右邊)01344750 6A 01 push 1 ;壓入參數1(右邊倒數第2個)01344752 E8 F5 CC FF FF call Add (0134144Ch) ;調用目標函數01344757 83 C4 08 add esp,8 ;恢復堆棧Add:00A8291E 8B 45 08 mov eax,dword ptr [a] 00A82921 03 45 0C add eax,dword ptr [b] 00A8292A C3 ret ;被調用者不清理堆棧?
由此可見,在__cdecl中:
??????? 1.參數從右到左依次傳遞
??????? 2.參數個數等于函數聲明時的個數(變參除外)
??????? 3.由調用者清理堆棧
? 2. __stdcall
??????? __stdcall調用約定用于調用Win32 API函數 Win32API的WINAPI/CALLBACK等都是它. 采用__stdcall
??? 約定時,函數參數按照 從右到左 的順序入棧,被調用的函數在返回前清理參數堆棧,函數參數個數固定.
??? 由于函數體本身知道傳進來的參數個數,因此被調用的函數可以在返回前用一條ret n指令直接清理傳遞參數的堆棧.
??? 還是上面那個例子,將__cdecl約定換成__stdcall:
??? 函數調用處反匯編代碼(從簡):
main:;Add(1,2);0131474E 6A 02 push 2 ;參數依然是從右到左傳遞01314750 6A 01 push 1 01314752 E8 FA CC FF FF call Add (01311451h) 01314757 33 C0 xor eax,eax ;調用函數并不清理堆棧0131476C C3 retint __stdcall Add(int a,int b):0131291E 8B 45 08 mov eax,dword ptr [a] ;完成相加操作01312921 03 45 0C add eax,dword ptr [b]0131292A C2 08 00 ret 8 ;返回時清理參數棧 ??? 由此可見,在__stdcall中:
? ? ?????? 1.函數參數從右到左傳遞
? ?? ????? 2.參數個數等于函數聲明時的個數
?? ? ? ??? 3.由被調用者清理參數棧
?
??? 3. __fastcall
??????? __fastcall約定用于對性能要求非常高的場合.
??????? __fastcall約定將函數的從左邊開始的兩個大小不大于4個字節(DWORD)的參數分別放在ECX和EDX寄存器,
??? 其余的參數仍舊自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的堆棧.
??????? (暫無代碼)
??????? 1.函數參數從右到左傳遞
??????? 2.由被調用者清理堆棧
*** C++中的調用約定 ***
??????? 以上三種C語言中可用的調用約定均可以用于修飾C++中的非靜態成員函數,只是會將this指針當作一個普通的
??? 參數一樣壓入(__cdecl,__stdcall)/傳遞到ECX(EDX)(__fastcall).
??????? __thiscall調用約定是C++中專用的,是非靜態類成員函數的默認調用約定(注:在VC6中沒有此關鍵字).
??? 采用 __thiscall約定時,函數參數按照從右到左的順序入棧,被調用的函數在返回前清理傳送參數的棧,
??? 只是它是通過ECX寄存器傳送一個額外的參數:this指針
??? 這次的例子中將定義一個類,并在類中定義一個成員函數,代碼如下:
?
??? 再來看看這個稍微復雜一點的C++類調用的反匯編代碼:
??????? 1. 除 Add3 的__fastcall外其余全部通過從右到左參數
??????? 2. 除默認的函數需要的參數個數外, 額外傳入一個this指針
??????? 3. 清理堆棧的方式保持不變
?
<名稱修飾,后面補上>
----------------
女孩不哭 @ 2013-09-11 00:18:11 @ http://www.cnblogs.com/nbsofer
?
轉載于:https://www.cnblogs.com/memset/p/calling_convention.html
總結
以上是生活随笔為你收集整理的C/C++/动态链接库DLL中函数的调用约定与名称修饰的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络层:指引
- 下一篇: 十七、二分查找法(java)