VC++代碼是最接近匯編指令的高級語言,為了更加准確和深刻理解VC++編碼中所涉及的很多技術概念和編譯器參數的含義,從匯編指令層面進行剖析和解讀,有助於開發者更加准確、直觀、深刻理解高級語言中很多概念和技術的真正含義,對程序優化和編碼都有非常實用的重要價值。由於內容很多,我會分解為很多篇章進行解讀實例。
從main入口開始分析,使用古老的VC6.0編譯器編譯,先從最簡單的例子開始逐步擴展,便於大家逐步入門。
VC++源代碼:
int main(int argc, char* argv[])
{
printf("Hello World!\n");
return 0;
}
最簡單不過的代碼了,看看匯編代碼是什麼呢?關於32位X86寄存器的基礎知識,大家可以自己先上網看啦。
debug狀態下無優化的匯編代碼:
int main(int argc, char* argv[])
20: {
00401030 55 push ebp ==保存EBP
00401031 8B EC mov ebp,esp ==保存ESP
00401033 83 EC 40 sub esp,40h ==沒有定義任何臨時變量的情況下,默認預留64字節的棧內存空間
00401036 53 push ebx ==保存EBX
00401037 56 push esi ==保存ESI
00401038 57 push edi ==保存edi
00401039 8D 7D C0 lea edi,[ebp-40h] ==將棧頂地址放入EDI中
0040103C B9 10 00 00 00 mov ecx,10h ==計數器16放入ECX
00401041 B8 CC CC CC CC mov eax,0CCCCCCCCh ===初始值為指向INT3地址
00401046 F3 AB rep stos dword ptr [edi] ===初始化16個DWORD的棧空間
26: printf("Hello World!\n");
00401048 68 1C 20 42 00 push offset string "Hello World!\n" (0042201c) ==將字符串參數地址入棧
0040104D E8 EE 00 00 00 call printf (00401140) ==調用函數
00401052 83 C4 04 add esp,4 ==恢復ESP
27: return 0;
00401055 33 C0 xor eax,eax ==EAX清零
28: }
00401057 5F pop edi ==恢復EDI
00401058 5E pop esi ==恢復ESI
00401059 5B pop ebx ==恢復EBX
0040105A 83 C4 40 add esp,40h ==恢復ESP
0040105D 3B EC cmp ebp,esp ==檢查ESP和EBP
0040105F E8 5C 01 00 00 call __chkesp (004011c0) ==比較檢查結果,若不相等會拋出異常
00401064 8B E5 mov esp,ebp ==恢復esp
00401066 5D pop ebp ==恢復ebp
00401067 C3 ret
總結:上面匯編指令序列的模式實際是典型的函數調用過程,簡單來說就是保存堆棧指針,保存寄存器,根據臨時變量的聲明,開辟堆棧空間並初始化,壓棧傳參,調用函數,恢復寄存器,恢復堆棧指針,退出程序。
使用更高版本的VC編譯,默認堆棧大小會不同,若編譯位64位,差別更大,以後再討論。
下一篇,我會在程序中增加更多內容,展示相應的匯編代碼底層實現過程。