C++代码:
struct AB
{
int ia;
int ib;
int ic;
AB(){}
};
AB su_4() // 返回一个对象
{
AB c;
c.ia=100;
c.ib=150;
c.ic=200;
return c;
}
int main()
{
AB r;
r=su_4(); // 函数调用
getchar();
return 0;
}
51: int main()
52: {
004115F0 55 push ebp % 【1】这一段为main函数干正事之前的准备工作
004115F1 8B EC mov ebp,esp
004115F3 81 EC E8 00 00 00 sub esp,0E8h
004115F9 53 push ebx
004115FA 56 push esi
004115FB 57 push edi
004115FC 8D BD 18 FF FF FF lea edi,[ebp-0E8h]
00411602 B9 3A 00 00 00 mov ecx,3Ah
00411607 B8 CC CC CC CC mov eax,0CCCCCCCCh
0041160C F3 AB rep stos dword ptr es:[edi] % end 【1】
60: AB r;
0041160E 8D 4D F0 lea ecx,[r] % 【2】声明对象,调用构造函数
00411611 E8 30 FA FF FF call AB::AB (411046h)
61: r=su_4();
00411616 8D 85 1C FF FF FF lea eax,[ebp-0E4h] % 【3】调用su_4()函数。该函数没有输入参数,但由于返回值为对象(体积大于64bit),因此要用地址来 % 传递。这里将地址设为ebp-0E4h,是一个栈地址,但离当前栈顶很远。此时将地址值存入eax,再push之
0041161C 50 push eax
0041161D E8 65 FA FF FF call su_4 (411087h) % 调用函数。eip自动入栈。
00411622 83 C4 04 add esp,4 % 由于之前push了eax,现在将esp加回去,实现栈平衡。
00411625 8B 08 mov ecx,dword ptr [eax] % eax保存了函数返回值临时存储区首地址。总共12字节,这里分三次mov完成拷贝。将临时存 % 储区里的数据拷贝到接收变量r中去。
00411627 89 4D F0 mov dword ptr [r],ecx
0041162A 8B 50 04 mov edx,dword ptr [eax+4]
0041162D 89 55 F4 mov dword ptr [ebp-0Ch],edx
00411630 8B 40 08 mov eax,dword ptr [eax+8]
00411633 89 45 F8 mov dword ptr [ebp-8],eax
105: getchar(); % 【4】剩下的是main函数的后处理,不用管。
00411636 8B F4 mov esi,esp
00411638 FF 15 B4 82 41 00 call dword ptr [__imp__getchar (4182B4h)]
0041163E 3B F4 cmp esi,esp
00411640 E8 FB FA FF FF call @ILT+315(__RTC_CheckEsp) (411140h)
106: return 0;
00411645 33 C0 xor eax,eax
107: }
00411647 52 push edx
00411648 8B CD mov ecx,ebp
0041164A 50 push eax
0041164B 8D 15 6C 16 41 00 lea edx,[ (41166Ch)]
00411651 E8 2C FA FF FF call @ILT+125(@_RTC_CheckStackVars@8) (411082h)
00411656 58 pop eax
00411657 5A pop edx
00411658 5F pop edi
00411659 5E pop esi
0041165A 5B pop ebx
0041165B 81 C4 E8 00 00 00 add esp,0E8h
00411661 3B EC cmp ebp,esp
00411663 E8 D8 FA FF FF call @ILT+315(__RTC_CheckEsp) (411140h)
00411668 8B E5 mov esp,ebp
0041166A 5D pop ebp
0041166B C3 ret
构造函数:
26: struct AB
27: {
28: int ia;
29: int ib;
30: int ic;
31: AB(){}
004115B0 55 push ebp
004115B1 8B EC mov ebp,esp
004115B3 81 EC CC 00 00 00 sub esp,0CCh
004115B9 53 push ebx
004115BA 56 push esi
004115BB 57 push edi
004115BC 51 push ecx
004115BD 8D BD 34 FF FF FF lea edi,[ebp-0CCh]
004115C3 B9 33 00 00 00 mov ecx,33h
004115C8 B8 CC CC CC CC mov eax,0CCCCCCCCh
004115CD F3 AB rep stos dword ptr es:[edi]
004115CF 59 pop ecx
004115D0 89 4D F8 mov dword ptr [ebp-8],ecx
004115D3 8B 45 F8 mov eax,dword ptr [this]
004115D6 5F pop edi
004115D7 5E pop esi
004115D8 5B pop ebx
004115D9 8B E5 mov esp,ebp
004115DB 5D pop ebp
004115DC C3 ret
};
函数:
35: AB su_4()
36: {
004114F0 55 push ebp %【1】 函数预处理工作。
004114F1 8B EC mov ebp,esp
004114F3 81 EC D4 00 00 00 sub esp,0D4h
004114F9 53 push ebx
004114FA 56 push esi
004114FB 57 push edi
004114FC 8D BD 2C FF FF FF lea edi,[ebp-0D4h]
00411502 B9 35 00 00 00 mov ecx,35h
00411507 B8 CC CC CC CC mov eax,0CCCCCCCCh
0041150C F3 AB rep stos dword ptr es:[edi]
37: AB c;
0041150E 8D 4D F0 lea ecx,[c] %【2】 定义临时变量,调用构造函数。
00411511 E8 30 FB FF FF call AB::AB (411046h)
38: c.ia=100;
00411516 C7 45 F0 64 00 00 00 mov dword ptr [c],64h % 【3】 给三个成员变量赋值。
39: c.ib=150;
0041151D C7 45 F4 96 00 00 00 mov dword ptr [ebp-0Ch],96h
40: c.ic=200;
00411524 C7 45 F8 C8 00 00 00 mov dword ptr [ebp-8],0C8h
41:
42: return c;
0041152B 8B 45 08 mov eax,dword ptr [ebp+8] %【4】 重点来了。由ebp指向入栈的上级函数ebp,ebp+4指向入栈的eip,eip+8指向入栈的临时返回 % 值地址。这里将该地址值存入eax。
0041152E 8B 4D F0 mov ecx,dword ptr [c] % [c]其实是[ebp-4]。将临时变量c的值拷贝到临时返回值存储区。
00411531 89 08 mov dword ptr [eax],ecx
00411533 8B 55 F4 mov edx,dword ptr [ebp-0Ch]
00411536 89 50 04 mov dword ptr [eax+4],edx
00411539 8B 4D F8 mov ecx,dword ptr [ebp-8]
0041153C 89 48 08 mov dword ptr [eax+8],ecx
0041153F 8B 45 08 mov eax,dword ptr [ebp+8]
43: }
00411542 52 push edx % 【5】 一些后处理。
00411543 8B CD mov ecx,ebp
00411545 50 push eax
00411546 8D 15 68 15 41 00 lea edx,[ (411568h)]
0041154C E8 31 FB FF FF call @ILT+125(@_RTC_CheckStackVars@8) (411082h)
00411551 58 pop eax
00411552 5A pop edx
00411553 5F pop edi
00411554 5E pop esi
00411555 5B pop ebx
00411556 81 C4 D4 00 00 00 add esp,0D4h
0041155C 3B EC cmp ebp,esp
0041155E E8 DD FB FF FF call @ILT+315(__RTC_CheckEsp) (411140h)
00411563 8B E5 mov esp,ebp
00411565 5D pop ebp
00411566 C3 ret
--------------------------------------------------------------------------------------------分割线--------------------------------------------------------------------------------------------
以上为有构造函数的情形,struct解释为一个类。如果没有构造函数,就会多出一次拷贝:
51: int main()
52: {
004115A0 55 push ebp
004115A1 8B EC mov ebp,esp
004115A3 81 EC 00 01 00 00 sub esp,100h
004115A9 53 push ebx
004115AA 56 push esi
004115AB 57 push edi
004115AC 8D BD 00 FF FF FF lea edi,[ebp-100h]
004115B2 B9 40 00 00 00 mov ecx,40h
004115B7 B8 CC CC CC CC mov eax,0CCCCCCCCh
004115BC F3 AB rep stos dword ptr es:[edi]
004115BE A1 00 70 41 00 mov eax,dword ptr [___security_cookie (417000h)]
004115C3 33 C5 xor eax,ebp
004115C5 89 45 FC mov dword ptr [ebp-4],eax
60: AB r;
61: r=su_4();
004115C8 8D 85 18 FF FF FF lea eax,[ebp-0E8h]
004115CE 50 push eax
004115CF E8 AE FA FF FF call su_4 (411082h)
004115D4 83 C4 04 add esp,4
004115D7 8B 08 mov ecx,dword ptr [eax] % 两次拷贝
004115D9 89 8D 04 FF FF FF mov dword ptr [ebp-0FCh],ecx
004115DF 8B 50 04 mov edx,dword ptr [eax+4]
004115E2 89 95 08 FF FF FF mov dword ptr [ebp-0F8h],edx
004115E8 8B 40 08 mov eax,dword ptr [eax+8]
004115EB 89 85 0C FF FF FF mov dword ptr [ebp-0F4h],eax
004115F1 8B 8D 04 FF FF FF mov ecx,dword ptr [ebp-0FCh]
004115F7 89 4D EC mov dword ptr [ebp-14h],ecx
004115FA 8B 95 08 FF FF FF mov edx,dword ptr [ebp-0F8h]
00411600 89 55 F0 mov dword ptr [ebp-10h],edx
00411603 8B 85 0C FF FF FF mov eax,dword ptr [ebp-0F4h]
00411609 89 45 F4 mov dword ptr [ebp-0Ch],eax
62:
104:
105: getchar();
0041160C 8B F4 mov esi,esp
0041160E FF 15 B4 82 41 00 call dword ptr [__imp__getchar (4182B4h)]
00411614 3B F4 cmp esi,esp
00411616 E8 20 FB FF FF call @ILT+310(__RTC_CheckEsp) (41113Bh)
106: return 0;
0041161B 33 C0 xor eax,eax
107: }
0041161D 52 push edx
0041161E 8B CD mov ecx,ebp
00411620 50 push eax
00411621 8D 15 4C 16 41 00 lea edx,[ (41164Ch)]
00411627 E8 51 FA FF FF call @ILT+120(@_RTC_CheckStackVars@8) (41107Dh)
0041162C 58 pop eax
0041162D 5A pop edx
0041162E 5F pop edi
0041162F 5E pop esi
00411630 5B pop ebx
00411631 8B 4D FC mov ecx,dword ptr [ebp-4]
00411634 33 CD xor ecx,ebp
00411636 E8 D9 F9 FF FF call @ILT+15(@__security_check_cookie@4) (411014h)
0041163B 81 C4 00 01 00 00 add esp,100h
00411641 3B EC cmp ebp,esp
00411643 E8 F3 FA FF FF call @ILT+310(__RTC_CheckEsp) (41113Bh)
00411648 8B E5 mov esp,ebp
0041164A 5D pop ebp
0041164B C3 ret
基本上有3次拷贝:在子函数中,将变量拷贝到临时返回值存储区,在调用端(caller),将其拷贝到另一个临时存储区,再从那个存储区中拷贝到接受的变量。
明确下函数调用的过程:
(1)输入参数从右到左push进堆栈,如果是引用的话就push变量的地址;
(2)如果返回值是很大的对象(大于64bit),而且是直接返回,不是返回引用(返回引用的话就直接返回地址),那么找一块很远的内存,将其地址push入栈,将来用来临时存放返回值
(3)指向call func。func为函数名称。此时eip寄存器的内容自动入栈。
(4)从这一步开始就进入函数了!Push ebp,将caller的ebp入栈。
(5)mov ebp, esp。当前栈顶作为栈基址。callee有了自己的栈空间!
(6)sub esp,100.100是随便写的,可能是别的数。系统分配临时变量的空间。
(7)ebx,esi,edi等寄存器入栈。
(8)用0xCC填充以上申请的空间。
(9)干正事。。。
(10)函数要返回了。返回值是一个大的对象。将对象拷贝到ebp+8那个地方的值所指的位置。没错,就是指的第(2)步那么临时存放返回值的地方。
(11)mov esp,ebp。清理栈空间其实很简单,改变栈顶位置即可。
(12)ret。这条命令弹出栈中eip对应的值,这样系统才知道调用这个函数以后下一条指令是什么。
(13)add esp,XXX。因为有输入参数,还有临时存放空间指针,所以这里要做栈平衡。
(14)从刚才说的返回值临时存放空间将返回值拷到另一个临时存放空间;如果有变量接受这个返回值,则从新的临时存放空间再将其拷到该变量上了如果没有变量接受,那么就不用了。
(15)调用结束,该干啥干啥。
注意:栈空间一般默认1M,递归过深会导致栈溢出。
ref:
http://wenku.baidu.com/view/bea115ccda38376baf1faec7.html
http://www.cnblogs.com/zplutor/archive/2011/09/25/2190315.html