运行环境:VS2010
int add(int a,int b)
{
return a + b;
}
int main()
{
add(1, 2);
}
int add(int a,int b)
{
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
return a + b;
mov eax,dword ptr [a]
add eax,dword ptr [b]
}
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
int main()
{
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
add(1, 2);
push 2
push 1
call @ILT+130(_add)
add esp,8
}
xor eax,eax
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
问题1:改成int _cdecl add(int a,int b)后观察修改前后的反汇编代码是否有区别?
答:没有区别,可以得出结论:Windows平台下,如果函数省略调用约定,则默认为_cdecl调用约定
问题2:将示例代码1的add函数改成int _stdcall add(int a,int b)后观察修改前后的反汇编代码是否有区别?
int _stdcall add(int a,int b)
{
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
return a + b;
mov eax,dword ptr [a]
add eax,dword ptr [b]
}
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 8
int main()
{
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
add(1, 2);
push 2
push 1
call @ILT+290(_add) (0CA1127h)
}
xor eax,eax
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
答:有区别,区别在下图
先引入两个名词:调用者和被调用者,例如本代码的main函数为调用者,其调用add函数,则add函数为被调用者
区别在:清理栈(防止栈溢出)的方式不同,其中add esp,8和ret 8均为清理栈的指令,其中_cdecl调用约定为调用者清栈,而_stdcall为被调用者清栈(即两者清栈的执行对象不同)
int add(int a,int b)
{
return a + b;
}
int main()
{
add(1, 2);
add(3, 4);
add(5, 6);
add(7, 8);
add(9, 10);
}
观察反汇编示例代码2后的main函数的汇编代码
对比_stdcall调用约定,_cdecl在多次调用同一个函数有缺点:add esp,8会多次重复,占用栈帧空间
而_stdcall是被调用者清栈,一劳永逸,不会多次重复
结论:如果多次调用同一个函数,_stdcall较_cdecl节省栈帧空间
顾名思义,为快速调用
问题3:将示例代码1的add函数改成int _fastcall add(int a,int b)后观察修改前后的反汇编代码是否有区别?
int _fastcall add(int a,int b)
{
push ebp
mov ebp,esp
sub esp,48h
push ebx
push esi
push edi
mov dword ptr [ebp-8],edx
mov dword ptr [ebp-4],ecx
return a + b;
mov eax,dword ptr [a]
add eax,dword ptr [b]
}
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
int main()
{
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
add(1, 2);
mov edx,2
mov ecx,1
call @ILT+295(_add@8) (0EF112Ch)
}
xor eax,eax
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
答:有区别,区别在下图
对比后发现:_fastcall在传参时使用寄存器,而_cdecl在传参时使用栈
之前在文章讲过,寄存器的速度是快的,因此用寄存器传参比用栈传参要快
(注;图来自《深入了解计算机系统》)
int _fastcall add(int a,int b,int c,int d)
{
return a + b+c+d;
}
int main()
{
add(1, 2, 3, 4);
}
问题4:反汇编以上代码,观察传参指令有什么特点
push 4
push 3
mov edx,2
mov ecx,1
答:显然当参数过多,寄存器不够用时,会用栈来辅助传参,而且一般情况下,最左边的两个参数(add(1,2,3,4)的1和2)交给寄存器去传参,其余交给栈传参
因篇幅问题不能全部显示,请点此查看更多更全内容