push:
把一个32位的操作数压入堆栈中。这个操作导致esp被减4。esp被形象地称为栈顶。我们认为顶部是地址小的区域,那么,压入堆栈中数据越多,这个堆栈也就越堆越高,esp也就越来越小。在32位平台,esp每次减少4字节。
pop:
相反,esp被加4,一个数据出栈。pop的参数一般是一个寄存器,栈顶的数据被弹出到这个寄存器中。
call 的本质相当于push+jmp ;ret 的本质相当于pop+jmp;
函数调用约定:
1)_cdecl C 调用 规则
参数从右到左进入堆栈
在函数返回后,调用 者要负责清除堆栈,所以这种调用常会生成较大的可执行程序。
2)_stdcall又称为WINAPI,其调用 规则
参数从右到左进入堆栈
被调用的函数在返回前自行清理堆栈,所以生成的代码比cdecl小
3)pascal调用规则主要用在win16函数库中,现在基本不用
参数从左到右进入堆栈
被调用的函数在返回前自行清理堆栈
不支持可变参数的函数调用 。
在Windows中,不管哪种调用 方式都是返回值放在eax中,然后返回。外部从eax中得到返回值。
#include "stdafx.h"
int main()
{
01371650 push ebp
01371651 mov ebp,esp
01371653 sub esp,0C0h
01371659 push ebx
0137165A push esi
0137165B push edi
0137165C lea edi,[ebp-0C0h]
01371662 mov ecx,30h
01371667 mov eax,0CCCCCCCCh
0137166C rep stos dword ptr es:[edi]
return 0;
0137166E xor eax,eax
}
void myfunction(int a, int b)
{
00CA1650 push ebp ;保护ebp,并把esp放入esp中。此时esp=ebp
00CA1651 mov ebp,esp ;
00CA1653 sub esp,0CCh ;把esp往上移动一个范围,等于在堆栈中放出一片新的空间用来存储局部变量
00CA1659 push ebx ;保存三个寄存器
00CA165A push esi
00CA165B push edi
00CA165C lea edi,[ebp-0CCh] ;lea把内容的地址,也就是ebp-0cch加载到edi中。目的是把保存局部变量的区域从ebp-0cch开始的区域,初始化成0cccccccch
00CA1662 mov ecx,33h
00CA1667 mov eax,0CCCCCCCCh ;
00CA166C rep stos dword ptr es:[edi] ;写入0cch指令
int c = a + b;
00CA166E mov eax,dword ptr [a]
00CA1671 add eax,dword ptr [b]
00CA1674 mov dword ptr [c],eax
}
00CA1677 pop edi ;恢复edi、esi、ebx
00CA1678 pop esi
00CA1679 pop ebx
00CA167A mov esp,ebp ;恢复原来的ebp和esp,让上一个调用的函数正常使用
00CA167C pop ebp
00CA167D ret