参考:
http://blog.csdn.net/hudashi/article/details/7820338
http://shitou7630.blog.163.com/blog/static/32699536201342110155436/
http://www.cnblogs.com/52yixin/archive/2011/06/29/2093634.html
http://blog.csdn.net/mniwc/article/details/7993361
http://www.cnblogs.com/coderzh/archive/2008/12/01/1345053.html
http://blog.sina.com.cn/s/blog_6f6769b50100uhzz.html
https://msdn.microsoft.com/zh-cn/library/ms235286.aspx
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源)
ESP是栈顶指针 Extended Stack Pointer
EBP是基址指针 Extend Base Pointer
ESP就是一直指向栈顶的指针,而EBP只是用于存取某时刻的栈顶指针,以方便对栈的操作
l
(win32下) 使用__stdcall的函数定义和函数翻译
void __stdcall TestCall(int a, int b) { return; } 执行 TestCall(1, 2); |
使用__stdcall的函数翻译: (执行时转到”反汇编”窗口)
TestCall(1, 2); 00F013FE push 2 00F01400 push 1 00F01402 call TestCall (0F01177h) void __stdcall TestCall(int a, int b) { return; } 00F013B0 push ebp // 00F013B1 mov ebp,esp // 00F013B3 sub esp,0C0h 00F013B9 push ebx 00F013BA push esi 00F013BB push edi 00F013BC lea edi,[ebp-0C0h] 00F013C2 mov ecx,30h 00F013C7 mov eax,0CCCCCCCCh 00F013CC rep stos dword ptr es:[edi] 00F013CE pop edi 00F013CF pop esi 00F013D0 pop ebx 00F013D1 mov esp,ebp // 00F013D3 pop ebp // 00F013D4 ret 8 // |
(注意,压栈时esp地址变小,出栈时esp地址变大,因为是从后往前分配的。)
l
(win32下) 使用__cdecl或缺省的函数定义和函数翻译
void __stdcall TestCall(int a, int b) { return; } 执行 TestCall(1, 2); |
使用__cdecl或缺省的函数翻译: (执行时转到”反汇编”窗口)
TestCall(1, 2); 012B13FE push 2 012B1400 push 1 012B1402 call TestCall (12B11D6h) 012B1407 add esp,8 // esp退栈8字节,把push 2; push 1;压入的8字节退掉。 void TestCall(int a, int b) { return; } 00F013B0 push ebp // 00F013B1 mov ebp,esp // 00F013B3 sub esp,0C0h 00F013B9 push ebx 00F013BA push esi 00F013BB push edi 00F013BC lea edi,[ebp-0C0h] 00F013C2 mov ecx,30h 00F013C7 mov eax,0CCCCCCCCh 00F013CC rep stos dword ptr es:[edi] 00F013CE pop edi 00F013CF pop esi 00F013D0 pop ebx 00F013D1 mov esp,ebp // 00F013D3 pop ebp // 00F013D4 ret // |
l 从上面可以看出
使用__stdcall的话,调用侧从右往左压栈函数参数,但不退栈,需要函数退栈
(ret 8 -- esp退栈8个字节)
使用__cdecl的话,调用侧从右往左压栈函数参数,并且在调用后,调用侧主动执行退栈 (add esp, 8 -- esp退栈8个字节)
l 不用退栈的情况
有一些情况,由于不用退栈,调用他人声明的函数时用哪种都不会报错
无参数的情况:
这种情况,由于调用侧未压栈函数参数,所以不存在函数参数的退栈问题,退栈也只需退0长度
winX64位情况下编译,当参数少于4个时,未进行压栈:
参考:
https://msdn.microsoft.com/zh-cn/library/ms235286.aspx
http://blog.sina.com.cn/s/blog_6f6769b50100uhzz.html
http://openwares.net/misc/windows_x64_function_call_convention.html
前四个参数被放入到寄存器中,而不是压入堆栈中的,同无参数一样,也不存在退栈情况。
(通常参数在寄存器 RCX、RDX、R8
和 R9 中传递。)
例如
void __stdcall TestCall(int& a, int& b) { return; } int a = 1; int b = 2; TestCall(a, b); TestCall(a, b); 000000013F031247 mov edx,dword ptr [b] 000000013F03124B mov ecx,dword ptr [a] 000000013F03124F call TestCall (13F031028h) void __stdcall TestCall(int& a, int& b) { return; } 000000013F0311B0 mov dword ptr [rsp+10h],edx 000000013F0311B4 mov dword ptr [rsp+8],ecx 000000013F0311B8 push rdi 000000013F0311B9 pop rdi 000000013F0311BA ret |
Gcc编译时,更加不同,这里不作详细介绍:
参考 http://blog.sina.com.cn/s/blog_6f6769b50100uhzz.html
“win_hate”: 发现一般规则为,当参数6个及6个以内时,参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。当参数为
6个以上时,前 6 个与前面一样,但后面的依次从 "右向左放入栈中。
l 其它说明
参考文档中对这两种调用的说明:
http://www.cnblogs.com/coderzh/archive/2008/12/01/1345053.html
1、_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。 int f(void *p) -->> [email protected](在外部汇编语言里可以用这个名字引用这个函数) 2、C调用约定(即用__cdecl关键字说明)(The C default |
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源)