進行C++調用時遇到了棘手的問題,Naked Call這是一個很少見的C++調用約定,建議程序設計者不要使用。編譯器不會給這種函數增加初始化和清理代碼,省的變成了白用工。
調用處push 1push 2call functionadd esp,8 注意:這裡C++調用者在恢復堆棧被調用函數_function處push ebp 保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復mov ebp,esp 保存堆棧指針mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向aadd eax,[ebp + 0CH] 堆棧中ebp + 12處保存了bmov esp,ebp 恢復esppop ebpret 注意,這裡沒有修改堆棧
MSDN中說,該修飾自動在函數名前加前導的下劃線,因此函數名在符號表中被記錄為_function,但是我在編譯時似乎沒有看到這種變化。由於參數按照從右向左順序壓棧,因此最開始的參數在最接近棧頂的位置,因此當采用不定個數參數時,第一個參數在棧中的位置肯定能知道,只要不定的參數個數能夠根據第一個後者後續的明確的參數確定下來,就可以使用不定參數,例如對於CRT中的sprintf函數,定義為:
- class A{public: int function1(int a,int b);
- int function2(int a,...);};
- int A::function1 (int a,int b){
- return a+b;
- }#include int A::function2(int a,...)
- { va_list ap;
- va_start(ap,a);
- int i;
- int result = 0;
- for(i = 0 ; i < a ; i ++)
- {
- result += va_arg(ap,int); }
函數的第一個和第二個DWORD參數或者尺寸更小的)通過ecx和edx傳遞,其他參數通過從右向左的順序壓棧被調用函數清理堆棧函數名修改規則同這是一個很少見的調用約定,一般程序設計者建議不要使用。
編譯器不會給這種函數增加初始化和清理代碼,更特殊的是,你不能用return返回返回值,只能用插入匯編返回結果。這一般用於實模式驅動程序設計,假設定義一個求和的加法程序,可以定義為:
- __declspec(naked) int add(int a,int b){ __asm mov eax,a __asm add eax,b __asm ret }
注意,這個函數沒有顯式的return返回值,返回通過修改eax寄存器實現,而且連退出函數的ret指令都必須顯式插入。上面代碼被翻譯成匯編以後變成:
- declspec(naked) int add(int a,int b){ __asm mov eax,a __asm add eax,b __asm ret }
由於調用者沒有理解WINAPI的含義錯誤的增加了這個修飾,上述代碼必然導致堆棧被破壞,MFC在編譯時插入的checkesp函數將告訴你,堆棧被破壞了。