从汇编源码逐步分析函数调用过程
C源码
#include 'stdio.h'
int test(int x, int y, double& z)
{
z = x + y;
return 0;
}
void main()
{
double sum = 1.2;
int a = 2 ,b = 3 , ret = 0;
ret = test(a, b, sum);
printf('ret:%d', ret);
}
对应的汇编代码
void main()
11: {
寄存器 栈单元地址 栈单元内容或解释
00410990 push ebp //压入之前调用栈的栈底地址,栈元素增加1
00410991 mov ebp,esp //新栈空间的栈底地址EBP设置为增加的这个栈元素地址(即调用者栈栈帧的栈底地址)
00410993 sub esp,54h //分配54h 即84字节栈内存
00410996 push ebx //保存各寄存器内容
00410997 push esi
00410998 push edi
00410999 lea edi,[ebp-54h] //取新分配内存的低地址
0041099C mov ecx,15h //计数器设置15h, 及21个4字节
004109A1 mov eax,0CCCCCCCCh //写入eax, 0CCCCCCCCh表示初始化的内存
004109A6 rep stos dword ptr [edi] //将eax内容写入内存地址,计数,写15h次。
12: double sum = 1.2;
004109A8 mov dword ptr [ebp-8],33333333h
004109AF mov dword ptr [ebp-4],3FF33333h
13: int a = 2 ,b = 3 , ret = 0;
004109B6 mov dword ptr [ebp-0Ch],2
004109BD mov dword ptr [ebp-10h],3
004109C4 mov dword ptr [ebp-14h],0
//变量初始化, EBP-X 表示变量地址,先分配的变量减的少,后分配的减的多
14: ret = test(a, b, sum);
004109CB lea eax,[ebp-8]
004109CE push eax
004109CF mov ecx,dword ptr [ebp-10h]
004109D2 push ecx
004109D3 mov edx,dword ptr [ebp-0Ch]
004109D6 push edx
004109D7 call @ILT+10(test) (0040100f) //跳转到test的函数执行,请先看test部分,test执行完
标记A
004109DC add esp,0Ch //释放调用时压栈参数再看这边。
004109DF mov dword ptr [ebp-14h],eax //将返回值保存到栈临时变量
15: printf('ret:%d', ret); //此段不解释
004109E2 mov eax,dword ptr [ebp-14h]
004109E5 push eax
004109E6 push offset string 'ret:%d' (00426fd8)
004109EB call printf (00410910)
004109F0 add esp,8
16: }
004109F3 pop edi //恢复各寄存器
004109F4 pop esi
004109F5 pop ebx
004109F6 add esp,54h //释放临时变量
004109F9 cmp ebp,esp //此时两者应该相等
004109FB call __chkesp (00401170)
00410A00 mov esp,ebp //恢复上层调用者栈顶
00410A02 pop ebp //恢复上层调用者栈底
00410A03 ret //调转到上层调用者执行
至此主函数执行结束。
main ret后的代码
00401299 add esp,0Ch
0040129C mov dword ptr [mainret],eax
0040129F mov edx,dword ptr [mainret]
004012A2 push edx
004012A3 call exit (00402360)
----------------------------------------------------------------------------------------------------------------------
Test函数源码
3: int test(int x, int y, double& z)
4: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,44h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-44h]
0040102C mov ecx,11h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
5: z = x + y;
00401038 mov eax,dword ptr [ebp+8] //ebp+X引用调用者传入参数变量
0040103B add eax,dword ptr [ebp+0Ch]
0040103E mov dword ptr [ebp-4],eax //ebp-X 引用本函数块分配的变量
00401041 fild dword ptr [ebp-4] //fild是将整数转化为长双精FP80压栈(压到st0
00401044 mov ecx,dword ptr [ebp+10h]
00401047 fstp qword ptr [ecx] //fstp是将弹栈指令,将st0弹出。
6: return 0;
00401049 xor eax,eax
7: }
0040104B pop edi //各寄存器值出栈恢复
0040104C pop esi
0040104D pop ebx
0040104E mov esp,ebp //释放本函数栈空间
00401050 pop ebp //恢复EBP指针,同时ESP指针下移一个 push 指令ESP自动上移,pop自动下移。至此,栈空间又恢复到main函数的栈空间。
00401051 ret //函数调用结束弹出栈顶元素(返回地址),跳转到此地址执行。请回到标记A处继续阅读。