时间:2021-01-04 16:13:34 | 栏目:C代码 | 点击:次
1. __cdecl
__cdecl 是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,由调用者负责把参数压入栈,最后也是由调用者负责清除栈的内容,一般来说,这是 C/C++ 的默认调用函数的规则,MS VC 编译器采用的规则则是这种规则2. __stdcall
_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,由调用者负责把参数压入栈,最后由被调用者负责清除栈的内容,Windows API 所采用的函数调用规则则是这种规则
另外,采用 __cdecl 和 __stdcall 不同规则的函数所生成的修饰名也各为不同,相同点则是生成的函数修饰名前缀都带有下划线,不同的则是后缀部分,当然,这两者最大的不同点就在于恢复栈的方式不同,而且这点亦是最为重要的。
__cdecl 规则要求调用者本身负责栈的恢复工作,在汇编的角度上说,恢复堆栈的位置是在调用函数内,考虑这样一段 C++ 代码(在 VC 下 Debug)
void __cdecl func(int param1, int param2, int param3) {
int var1 = param1;
int var2 = param2;
int var3 = param3;
printf("%ld\n", long(¶m1));
printf("%ld\n", long(¶m2));
printf("%ld\n", long(¶m3));
printf("----------------\n");
printf("%ld\n", long(&var1));
printf("%ld\n", long(&var2));
printf("%ld\n", long(&var3));
return ;
}
int main() {
func(1, 2, 3);
return 0;
}
............................................... ; 省略了printf的代码
15: return ;
16: }
004010BD pop edi
004010BE pop esi
004010BF pop ebx
004010C0 add esp,4Ch
004010C3 cmp ebp,esp
004010C5 call __chkesp (004011d0)
004010CA mov esp,ebp
004010CC pop ebp
004010CD ret ; 这里是 ret,由调用者(main)恢复堆栈,但如果是 __stdcall 的话,
; 恢复堆栈就在这里进行
*******************************************************************************************************************
18: int main() {
............................................... ; 省略了建立堆栈的代码
19: func(1, 2, 3);
00401118 push 3 ; 将 param3 压入栈
0040111A push 2 ; 将 param2 压入栈
0040111C push 1 ; 将 param1 压入栈
0040111E call @ILT+5(func) (0040100a) ; @ILT+5(func) 是函数func的修饰名,而0040100a则是他的地址
00401123 add esp,0Ch ; 恢复堆栈,__cdecl 规则由调用者(这里是main)恢复堆栈
20: return 0;
00401126 xor eax,eax
21: }
00401128 pop edi
00401129 pop esi
0040112A pop ebx
0040112B add esp,40h
0040112E cmp ebp,esp
00401130 call __chkesp (004011d0)
00401135 mov esp,ebp
00401137 pop ebp
00401138 ret
结果截图
程序中的栈结构如下图示:
__stdcall 规则由被调用者本身去调整堆栈,在汇编的角度上说,恢复堆栈的行为发生在调用者函数内,考虑这样一段代码(VC 下Debug):
void __stdcall func(int param1, int param2, int param3) {
int var1 = param1;
int var2 = param2;
int var3 = param3;
printf("%ld\n", long(¶m1));
printf("%ld\n", long(¶m2));
printf("%ld\n", long(¶m3));
printf("----------------\n");
printf("%ld\n", long(&var1));
printf("%ld\n", long(&var2));
printf("%ld\n", long(&var3));
return ;
}
int main() {
func(1, 2, 3);
return 0;
}
.............................................. ; 省略 printf 代码
15: return ;
16: }
004010BD pop edi
004010BE pop esi
004010BF pop ebx
004010C0 add esp,4Ch
004010C3 cmp ebp,esp
004010C5 call __chkesp (004011d0)
004010CA mov esp,ebp
004010CC pop ebp
004010CD ret 0Ch ; __stdcall 在这里(被调用函数)恢复堆栈,但如果是 __cdecl 的话,这里是 ret,
; 堆栈的恢复由调用者(这里是 main)来负责
*******************************************************************************************************************
18: int main() {
........................................... ; 省略建立堆栈代码
19: func(1, 2, 3);
00401118 push 3 ; param3 压入堆栈
0040111A push 2 ; param2 压入堆栈
0040111C push 1 ; param1 压入堆栈
0040111E call @ILT+0(func) (00401005) ; @ILT+0(func) 是函数的修饰名,而 00401005 则是调用函数func的地址
20: return 0;
00401123 xor eax,eax
21: }
00401125 pop edi
00401126 pop esi
00401127 pop ebx
00401128 add esp,40h
0040112B cmp ebp,esp
0040112D call __chkesp (004011d0)
00401132 mov esp,ebp
00401134 pop ebp
00401135 ret