快捷搜索:

函数调用的汇编码分析

这几天进修汇编,阐发了一下 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 )

留意:已经关闭了编译器的某些优化选项,使汇编码更易读!

调用方代码总结:

参数从右向左依次入栈。

栈指针(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为参数栈的字节数)

您可能还会对下面的文章感兴趣: