程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 函數調用的匯編碼分析

函數調用的匯編碼分析

編輯:關於C++

這幾天學習匯編,分析了一下 c++ 中函數調用(cdecl 和 fastcall 方式) 過程的匯編碼,記錄如下:

程序例子

struct tagTest 
{
 int n1;
 long n2;
 DWORD n3;
};

long funtest1(tagTest p1,int p2,LPCTSTR lpszP3) 
{  // 普通函數
 p1.n1 = 3;
  LPCTSTR lpszxx = lpszP3;
 p1.n3 = p2;
 
 return 300;
}

long __stdcall funtest2 (tagTest p1,int p2,LPCTSTR lpszP3)
{ // stdcall 函數
 p1.n1 = 3;
 LPCTSTR lpszxx = lpszP3;
 p1.n3 = p2;
 
 return 300;
}

1、普通調用(cdecl)。

調用方 C++ 代碼:

 long ixx = 0;
 tagTest tag1= {34,6,87};
 ixx = funtest1 (tag1,i2,"asdffffffdddddd");

生成的匯編碼:

 long ixx = 0;
0104171E  mov         dword ptr [ixx],0
 tagTest tag1= {34,6,87};
01041738  mov         dword ptr [tag1],22h     ; 成員賦值
0104173F  mov         dword ptr [ebp-10h],6    ; 成員賦值
01041746  mov         dword ptr [ebp-0Ch],57h  ; 成員賦值
 ixx = funtest1(tag1,i2,"asdffffffdddddd");
0104174D  push        offset CAnonymousAsmTestApp::`vftable'+0F4h (11E60B0h) ; 入棧參數
                           ; "asdffffffdddddd" 的地址。這裡顯示似乎有問題(實際地址是對的 )
01041752  mov         edx,dword ptr [i2] 
01041755  push        edx                 ; 入 棧參數 i2
01041756  sub         esp,0Ch             ; 在棧 中分配參數 tag1 的空間
01041759  mov         eax,esp
0104175B  mov         ecx,dword ptr [tag1]
0104175E  mov         dword ptr [eax],ecx  ; 入棧 tag1.n1
01041760  mov         edx,dword ptr [ebp-10h]
01041763  mov         dword ptr [eax+4],edx  ; 入棧 tag1.n2
01041766  mov         ecx,dword ptr [ebp-0Ch]
01041769  mov         dword ptr [eax+8],ecx  ; 入棧 tag1.n3
0104176C  call        funtest1 (1041680h)   ; 調用函數。
                       ;注意:這裡同時將返回地 址(下條指令的地址) 也入棧(這裡是4字節);
                        ;   所以,函數中取得參數時,需要從當前 ESP 中加上 4 字節!
01041771  add         esp,14h              ; 由 調用者清參數棧
01041774  mov         dword ptr [ixx],eax  ; 獲取返回值

調用方代碼總結:

參數從右向左依次入棧。

棧指 針(ESP)從底部向頂部依次減小。也就是說,棧頂地址小;棧底地址大。

如果參數是結構(struct),則直接移動棧頂指針,預留出結構的大小 ;然後用 mov 指令逐步將成員拷貝到預留出來的棧空間!

call 指令調 用目標地址

調用方清棧

獲得返回值

函數中生成的匯編碼

long funtest1(tagTest p1,int p2,LPCTSTR lpszP3)
{
 
00351680  push        ebp    ; 基址指針入棧
00351681  mov         ebp,esp  ; 將函數入口點的棧指針作為當 前基址指針。
                                ;顧名思義,基址指針就是進入函數時的棧頂指針!
00351683  push        ecx     ; 在棧中分配局部變量空間。
                              ;ecx 中的值無關緊要,只是預留一個 4 字節空間而已
 p1.n1 = 3;
00351684  mov         dword ptr [p1],3  ; 賦值
 LPCTSTR lpszxx = lpszP3;
0035168B  mov         eax,dword ptr [lpszP3] ; [lpszP3] 地址 應該就是 ebp - 4
0035168E  mov         dword ptr [lpszxx],eax ; 賦值(通過 eax )
 p1.n3 = p2;
00351691  mov         ecx,dword ptr [p2]
00351694  mov         dword ptr [ebp+10h],ecx ; 賦值(通過 ecx)
 return 300;
00351697  mov         eax,12Ch    ; 返回值放在 eax
}
0035169C  mov         esp,ebp  ; 恢復棧:清除局部變量
0035169E  pop         ebp      ; 恢復基址指針
0035169F  ret                  ; 返回(從棧中彈出返 回地址,然後 jmp )

2、_stdcall調用。

調用方 C++ 代 碼:

 int i2=3;
 long ixx = 0;
 tagTest tag1={34,6,87};
 ixx = funtest2 (tag1,i2,"asdffffffdddddd");

生成的匯編碼:

 int i2=3;
010A1759  mov         dword ptr [i2],3
 long ixx = 0;
010A1760  mov         dword ptr [ixx],0
 tagTest tag1= {34,6,87};
010A1767  mov         dword ptr [tag1],22h
010A176E  mov         dword ptr [ebp-0Ch],6
010A1775  mov         dword ptr [ebp-8],57h
 ixx = funtest2(tag1,i2,"asdffffffdddddd");
010A177C  push        offset CAnonymousAsmTestApp::`vftable'+104h (12460C0h) ; 入棧參數
                           ; "asdffffffdddddd" 的地址。這裡顯示似乎有問題(實際地址是對的 )
                          ; 和 cdecl 一樣,參數從右邊開始入棧
010A1781  mov         eax,dword ptr [i2]
010A1784  push        eax
010A1785  sub         esp,0Ch
010A1788  mov         ecx,esp
010A178A  mov         edx,dword ptr [tag1]
010A178D  mov         dword ptr [ecx],edx
010A178F  mov         eax,dword ptr [ebp-0Ch]
010A1792  mov         dword ptr [ecx+4],eax
010A1795  mov         edx,dword ptr [ebp-8]
010A1798  mov         dword ptr [ecx+8],edx
010A179B  call        funtest2 (10A16B0h)
010A17A0  mov         dword ptr [ixx],eax  ; 取得返回值。
                                            ; 注意:這裡沒有清棧的代碼,由函數自己清棧!

注意:已經關閉了編譯器的某些優化選項,使匯編碼更易讀!

調用方代碼總結:

參數從右向左依次入棧。

棧指針(ESP )從底部向頂部依次減小。也就是說,棧頂地址小;棧底地址大。

如果 參數是結構(struct),則直接移動棧頂指針,預留出結構的大小;然後用 mov 指令逐步將成員拷貝到預留出來的棧空間!

call 指令調用目標地址

返回時,由函數自己清棧(通過 ret 指令)

獲得返回值

函數中生成的匯編碼

long __stdcall funtest2(tagTest p1,int p2,LPCTSTR lpszP3)
{
010A16B0  push        ebp
010A16B1  mov         ebp,esp
010A16B3  push        ecx
 p1.n1 = 3;
010A16BD  mov         dword ptr [p1],3
 LPCTSTR lpszxx = lpszP3;
010A16C4  mov         eax,dword ptr [lpszP3]
010A16C7  mov         dword ptr [lpszxx],eax
 p1.n3 = p2;
010A16CA  mov         ecx,dword ptr [p2]
010A16CD  mov         dword ptr [ebp+10h],ecx
 return 300;
010A16D0  mov         eax,12Ch
}
010A16D5  mov         esp,ebp
010A16D7  pop         ebp
010A16D8  ret         14h    ; 返回並清棧。
                             ;(從棧中彈出返回地址, 返回;然後ESP增加14H,14H為參數棧的字節數)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved