日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

C/C++/动态链接库DLL中函数的调用约定与名称修饰

發(fā)布時(shí)間:2025/3/15 c/c++ 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C/C++/动态链接库DLL中函数的调用约定与名称修饰 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

參見(jiàn):http://blog.twofei.com/cc/impl/calling-convension.html

??? 調(diào)用約定(Calling Convention)是指在程序設(shè)計(jì)語(yǔ)言中為了實(shí)現(xiàn)函數(shù)調(diào)用而建立的一種協(xié)議.
這種協(xié)議規(guī)定了該語(yǔ)言的函數(shù)中的參數(shù)傳送方式,參數(shù)是否可變和由誰(shuí)來(lái)處理堆棧等問(wèn)題. 不同的語(yǔ)
言定義了不同的調(diào)用約定.

??? 在C++中,為了允許操作符重載和函數(shù)重載,C++編譯器往往按照某種規(guī)則改寫(xiě)每一個(gè)入口點(diǎn)的符號(hào)名,
以便允許同一個(gè)名字(具有不同的參數(shù)類(lèi)型或者是不同的作用域)有多個(gè)用法,而不會(huì)打破現(xiàn)有的基于C的鏈
接器. 這項(xiàng)技術(shù)通常被稱(chēng)為名稱(chēng)改編(Name Mangling)或者名稱(chēng)修飾(Name Decoration). 許多C++編譯
器廠商選擇了自己的名稱(chēng)修飾方案.

??? 因此,為了使其它語(yǔ)言編寫(xiě)的模塊(如Visual Basic應(yīng)用程序,Pascal或Fortran的應(yīng)用程序等)可以調(diào)
用C/C++編寫(xiě)的函數(shù),必須使用正確的調(diào)用約定來(lái)導(dǎo)出函數(shù),并且不要讓編譯器對(duì)要導(dǎo)出的函數(shù)進(jìn)行任何名稱(chēng)修飾.



1.調(diào)用約定(Calling Convention)
??? 調(diào)用約定用來(lái)處理決定函數(shù)參數(shù)傳送時(shí)入棧和出棧的順序(由調(diào)用者還是被調(diào)用者把參數(shù)彈出棧),以及編譯
器用來(lái)識(shí)別函數(shù)名稱(chēng)的名稱(chēng)修飾約定等問(wèn)題. 在Microsoft VC++ 6.0中定義了下面幾種調(diào)用約定,我們將結(jié)合匯
編語(yǔ)言來(lái)一一分析它們:

  *** C語(yǔ)言中的調(diào)用約定 ***
??? 1. __cdecl
??????? __cdecl是C/C++(非類(lèi)成員函數(shù)以及靜態(tài)類(lèi)成員函數(shù))程序默認(rèn)使用的調(diào)用約定,也可以在函數(shù)聲明時(shí)
??? 加上 __cdecl 關(guān)鍵字 來(lái)手工指定. 采用__cdecl約定時(shí),函數(shù)參數(shù)按照 從右到左 順序入棧,并且由調(diào)用
??? 函數(shù)者把參數(shù)彈出棧以清理堆棧. 因此,實(shí)現(xiàn)可變參數(shù)的函數(shù)只能使用該調(diào)用約定. 由于每一個(gè)使用
??? __cdecl約定的函數(shù)都要包含清理堆棧?? 的代碼,所以產(chǎn)生的可執(zhí)行文件大小會(huì)稍大一丁點(diǎn).

??? 下面將通過(guò)一個(gè)具體實(shí)例來(lái)分析__cdecl約定:

int __cdecl Add(int a,int b){return a+b;}void main(void){Add(1,2);}

?  函數(shù)調(diào)用處反匯編代碼如下:

main:0134474E 6A 02 push 2 ;壓入?yún)?shù)2(最右邊)01344750 6A 01 push 1 ;壓入?yún)?shù)1(右邊倒數(shù)第2個(gè))01344752 E8 F5 CC FF FF call Add (0134144Ch) ;調(diào)用目標(biāo)函數(shù)01344757 83 C4 08 add esp,8 ;恢復(fù)堆棧Add:00A8291E 8B 45 08 mov eax,dword ptr [a] 00A82921 03 45 0C add eax,dword ptr [b] 00A8292A C3 ret ;被調(diào)用者不清理堆棧

?

  由此可見(jiàn),在__cdecl中:
??????? 1.參數(shù)從右到左依次傳遞
??????? 2.參數(shù)個(gè)數(shù)等于函數(shù)聲明時(shí)的個(gè)數(shù)(變參除外)
??????? 3.由調(diào)用者清理堆棧



? 2. __stdcall
??????? __stdcall調(diào)用約定用于調(diào)用Win32 API函數(shù) Win32API的WINAPI/CALLBACK等都是它. 采用__stdcall
??? 約定時(shí),函數(shù)參數(shù)按照 從右到左 的順序入棧,被調(diào)用的函數(shù)在返回前清理參數(shù)堆棧,函數(shù)參數(shù)個(gè)數(shù)固定.
??? 由于函數(shù)體本身知道傳進(jìn)來(lái)的參數(shù)個(gè)數(shù),因此被調(diào)用的函數(shù)可以在返回前用一條ret n指令直接清理傳遞參數(shù)的堆棧.

??? 還是上面那個(gè)例子,將__cdecl約定換成__stdcall:

int __stdcall Add(int a,int b){return a+b;}void main(void){Add(1,2);}

??? 函數(shù)調(diào)用處反匯編代碼(從簡(jiǎn)):

main:;Add(1,2);0131474E 6A 02 push 2 ;參數(shù)依然是從右到左傳遞01314750 6A 01 push 1 01314752 E8 FA CC FF FF call Add (01311451h) 01314757 33 C0 xor eax,eax ;調(diào)用函數(shù)并不清理堆棧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 ;返回時(shí)清理參數(shù)棧

  ??? 由此可見(jiàn),在__stdcall中:
? ? ?????? 1.函數(shù)參數(shù)從右到左傳遞
? ?? ????? 2.參數(shù)個(gè)數(shù)等于函數(shù)聲明時(shí)的個(gè)數(shù)
?? ? ? ??? 3.由被調(diào)用者清理參數(shù)棧

?



??? 3. __fastcall
??????? __fastcall約定用于對(duì)性能要求非常高的場(chǎng)合.
??????? __fastcall約定將函數(shù)的從左邊開(kāi)始的兩個(gè)大小不大于4個(gè)字節(jié)(DWORD)的參數(shù)分別放在ECX和EDX寄存器,
??? 其余的參數(shù)仍舊自右向左壓棧傳送,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的堆棧.
??????? (暫無(wú)代碼)
??????? 1.函數(shù)參數(shù)從右到左傳遞
??????? 2.由被調(diào)用者清理堆棧



  *** C++中的調(diào)用約定 ***
??????? 以上三種C語(yǔ)言中可用的調(diào)用約定均可以用于修飾C++中的非靜態(tài)成員函數(shù),只是會(huì)將this指針當(dāng)作一個(gè)普通的
??? 參數(shù)一樣壓入(__cdecl,__stdcall)/傳遞到ECX(EDX)(__fastcall).
??????? __thiscall調(diào)用約定是C++中專(zhuān)用的,是非靜態(tài)類(lèi)成員函數(shù)的默認(rèn)調(diào)用約定(注:在VC6中沒(méi)有此關(guān)鍵字).
??? 采用 __thiscall約定時(shí),函數(shù)參數(shù)按照從右到左的順序入棧,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的棧,
??? 只是它是通過(guò)ECX寄存器傳送一個(gè)額外的參數(shù):this指針

??? 這次的例子中將定義一個(gè)類(lèi),并在類(lèi)中定義一個(gè)成員函數(shù),代碼如下:

class A{public:int __cdecl Add1(int a,int b){return a+b;}int __stdcall Add2(int a,int b){return a+b;}int __fastcall Add3(int a,int b){return a+b;}int __thiscall Add4(int a,int b){return a+b;}};void main(void){A a;a.Add1(1,2);a.Add2(3,4);a.Add3(5,6);a.Add4(7,8);}

?


??? 再來(lái)看看這個(gè)稍微復(fù)雜一點(diǎn)的C++類(lèi)調(diào)用的反匯編代碼:
??????? 1. 除 Add3 的__fastcall外其余全部通過(guò)從右到左參數(shù)
??????? 2. 除默認(rèn)的函數(shù)需要的參數(shù)個(gè)數(shù)外, 額外傳入一個(gè)this指針
??????? 3. 清理堆棧的方式保持不變

a.Add1(1,2); 001C1528 6A 02 push 2 001C152A 6A 01 push 1 001C152C 8D 45 F7 lea eax,[a] 001C152F 50 push eax ;傳入this指針001C1530 E8 1B FB FF FF call A::Add1 (01C1050h) 001C1535 83 C4 0C add esp,0Ch ;由于多傳入了一個(gè)this,所以是0Ch a.Add2(3,4); 001C1538 6A 04 push 4 001C153A 6A 03 push 3 001C153C 8D 45 F7 lea eax,[a] ;傳入this指針001C153F 50 push eax 001C1540 E8 DD FB FF FF call A::Add2 (01C1122h) a.Add3(5,6); 001C1545 6A 06 push 6 001C1547 BA 05 00 00 00 mov edx,5 ;將參數(shù)保存到edx001C154C 8D 4D F7 lea ecx,[a] ;將this保存到ecx001C154F E8 60 FB FF FF call A::Add3 (01C10B4h) a.Add4(7,8); 001C1554 6A 08 push 8 001C1556 6A 07 push 7 001C1558 8D 4D F7 lea ecx,[a] ;由ecx傳遞this指針001C155B E8 B4 FA FF FF call A::Add4 (01C1014h) 類(lèi)成員反匯編代碼(僅保留返回部分,其余地方同C語(yǔ)言中一樣):__cdecl: 001C141A C3 ret ;__cdecl由調(diào)用者清理堆棧__stdcall: 001C145A C2 0C 00 ret 0Ch ;加上this,總共3個(gè)參數(shù)__fastcall: 001C14A2 C2 04 00 ret 4 ;this和其中一個(gè)由寄存器傳遞__thiscall: 001C14EF C2 08 00 ret 8 ;this通過(guò)ecx傳遞,堆棧不受影響

?

<名稱(chēng)修飾,后面補(bǔ)上>
----------------
女孩不哭 @ 2013-09-11 00:18:11 @ http://www.cnblogs.com/nbsofer

?

轉(zhuǎn)載于:https://www.cnblogs.com/memset/p/calling_convention.html

總結(jié)

以上是生活随笔為你收集整理的C/C++/动态链接库DLL中函数的调用约定与名称修饰的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。