cdel调用方式解析

从下面的“对应上面的btnfastcallClick函数,代码从这里开始看”和“从这里开始看”处开始看这边文档

Delphi代码
38 //cdecl
39 function Plus_cdecl(a, b, c, d, e, f, g, h, i: Integer): Integer; cdecl;
40 var
41 Tempa, Tempb, Tempc, Tempd, Tempe, Tempf, Tempg, Temph, Tempi :Integer;
42 begin
43 Tempa:= 1;
44 Tempb:= 2;
45 Tempc:= 3;
46 Tempd:= 4;
47 Tempe:= 5;
48 Tempf:= 6;
49 Tempg:= 7;
50 Temph:= 8;
51 Tempi:= 9;
52 Tempg:= Tempa + Tempb + Tempc + Tempd + Tempe + Tempf + Temph + Tempi;
53 Result:= a + b + c + d + e + f + g + h + i + Tempg;
54 end;
55
56 procedure TFmAsm.btncdeclClick(Sender: TObject); //从这里开始看
57 var
58 btna, btnb, btnc, btnd, btne, btnf, btng, btnh, btni :Integer;
59 begin
60 btna:= 1;
61 btnb:= 2;
62 btnc:= 3;
63 btnd:= 4;
64 btne:= 5;
65 btnf:= 6;
66 btng:= 7;
67 btnh:= 8;
68 btni:= 9;
69 Plus_cdecl(btna, btnb, btnc, btnd, btne, btnf, btng, btnh, btni);
70 end;

//Delphi代码对应的汇编代码与上面的Delphi代码对应
AsmTest.pas.42: begin //对应上面的Plus_fastcall汇编代码,因为Delphi单元代码中Plus_fastcall函数和btnfastcallClick函数是紧挨着的,所以编译完了以后下面的汇编代码也是紧挨着的,顺序也是一样Plus_fastcall在前btnfastcallClick在后
0045AE90 55 push ebp
0045AE91 8BEC mov ebp,esp
0045AE93 83C4D8 add esp,-$28
//为什么分配$28=10 * 4个空间?
答:9:[ebp-$08]至mov [ebp-$28]的9个空间分配给Tempa…Tempi 9个局部变量
1:[ebp-$04]这一个空间用于临时存储Result,有下面的“mov [ebp-$04],eax和mov eax,[ebp-$04]”代码可知
AsmTest.pas.43: Tempa:= 1;
0045AE96 C745F801000000 mov [ebp-$08],$00000001
AsmTest.pas.44: Tempb:= 2;
0045AE9D C745F402000000 mov [ebp-$0c],$00000002
AsmTest.pas.45: Tempc:= 3;
0045AEA4 C745F003000000 mov [ebp-$10],$00000003
AsmTest.pas.46: Tempd:= 4;
0045AEAB C745EC04000000 mov [ebp-$14],$00000004
AsmTest.pas.47: Tempe:= 5;
0045AEB2 C745E805000000 mov [ebp-$18],$00000005
AsmTest.pas.48: Tempf:= 6;
0045AEB9 C745E406000000 mov [ebp-$1c],$00000006
AsmTest.pas.49: Tempg:= 7;
0045AEC0 C745E007000000 mov [ebp-$20],$00000007
AsmTest.pas.50: Temph:= 8;
0045AEC7 C745DC08000000 mov [ebp-$24],$00000008
AsmTest.pas.51: Tempi:= 9;
0045AECE C745D809000000 mov [ebp-$28],$00000009
AsmTest.pas.52: Tempg:= Tempa + Tempb + Tempc + Tempd + Tempe + Tempf + Temph + Tempi;
0045AED5 8B45F8 mov eax,[ebp-$08]
0045AED8 0345F4 add eax,[ebp-$0c]
0045AEDB 0345F0 add eax,[ebp-$10]
0045AEDE 0345EC add eax,[ebp-$14]
0045AEE1 0345E8 add eax,[ebp-$18]
0045AEE4 0345E4 add eax,[ebp-$1c]
0045AEE7 0345DC add eax,[ebp-$24]
0045AEEA 0345D8 add eax,[ebp-$28]
0045AEED 8945E0 mov [ebp-$20],eax
AsmTest.pas.53: Result:= a + b + c + d + e + f + g + h + i + Tempg;
//对于stdcall调用方式而言,跳入Plus_fastcall函数之后,第一个参数地址是[EBP + $08],第二个参数地址是[EBP + $0C],调用函数中的“call Plus_fastcall ”汇编代码的下一行代码地址是[EBP + $04],而上一帧栈的栈顶地址(即上一帧栈的基址地址)是[EBP]

0045AEF0 8B4508 mov eax,[ebp+$08]
0045AEF3 03450C add eax,[ebp+$0c]
0045AEF6 034510 add eax,[ebp+$10]
0045AEF9 034514 add eax,[ebp+$14]
0045AEFC 034518 add eax,[ebp+$18]
0045AEFF 03451C add eax,[ebp+$1c]
0045AF02 034520 add eax,[ebp+$20]
0045AF05 034524 add eax,[ebp+$24]
0045AF08 034528 add eax,[ebp+$28]
0045AF0B 0345E0 add eax,[ebp-$20]
0045AF0E 8945FC mov [ebp-$04],eax
0045AF11 8B45FC mov eax,[ebp-$04]
AsmTest.pas.54: end;
0045AF14 8BE5 mov esp,ebp
//mov esp,ebp函数结束时基本都是这个写法,目的是将当前函数的局部变量全都回退,恢复,当然,不包括参数栈,参数栈根据调用约定的不同,会由调用函数或者被调用函数回退,这里是stdcall调用的,被调用函数回退,下面的“ret $0024”处会讲到参数栈释放

0045AF16 5D pop ebp
0045AF17 C3 ret
//这里为什么是ret 而不是 ret $0024?
答:因为这个函数使用cdecl调用约定,参数栈由父函数(调用函数)清理,所以直接用ret,不用在函数内部回退
AsmTest.pas.59: begin//对应上面的btnstdcallClick函数,代码从这里开始看
0045AF18 55 push ebp
0045AF19 8BEC mov ebp,esp
0045AF1B 83C4D4 add esp,-$2c
0045AF1E 8955D4 mov [ebp-$2c],edx
0045AF21 8945FC mov [ebp-$04],eax
AsmTest.pas.60: btna:= 1;
0045AF24 C745F801000000 mov [ebp-$08],$00000001
AsmTest.pas.61: btnb:= 2;
0045AF2B C745F402000000 mov [ebp-$0c],$00000002
AsmTest.pas.62: btnc:= 3;
0045AF32 C745F003000000 mov [ebp-$10],$00000003
AsmTest.pas.63: btnd:= 4;
0045AF39 C745EC04000000 mov [ebp-$14],$00000004
AsmTest.pas.64: btne:= 5;
0045AF40 C745E805000000 mov [ebp-$18],$00000005
AsmTest.pas.65: btnf:= 6;
0045AF47 C745E406000000 mov [ebp-$1c],$00000006
AsmTest.pas.66: btng:= 7;
0045AF4E C745E007000000 mov [ebp-$20],$00000007
AsmTest.pas.67: btnh:= 8;
0045AF55 C745DC08000000 mov [ebp-$24],$00000008
AsmTest.pas.68: btni:= 9;
0045AF5C C745D809000000 mov [ebp-$28],$00000009
AsmTest.pas.69: Plus_cdecl(btna, btnb, btnc, btnd, btne, btnf, btng, btnh, btni);
0045AF63 8B45D8 mov eax,[ebp-$28] //将第9个参数压入栈
0045AF66 50 push eax
0045AF67 8B45DC mov eax,[ebp-$24] //将第8个参数压入栈
0045AF6A 50 push eax
0045AF6B 8B45E0 mov eax,[ebp-$20] //将第7个参数压入栈
0045AF6E 50 push eax
0045AF6F 8B45E4 mov eax,[ebp-$1c] //将第6个参数压入栈
0045AF72 50 push eax
0045AF73 8B45E8 mov eax,[ebp-$18] //将第5个参数压入栈
0045AF76 50 push eax
0045AF77 8B45EC mov eax,[ebp-$14] //将第4个参数压入栈
0045AF7A 50 push eax
0045AF7B 8B45F0 mov eax,[ebp-$10] //将第3个参数压入栈
0045AF7E 50 push eax
0045AF7F 8B45F4 mov eax,[ebp-$0c] //将第2个参数压入栈
0045AF82 50 push eax
0045AF83 8B45F8 mov eax,[ebp-$08] //将第1个参数压入栈
0045AF86 50 push eax
//由上面这个入参顺序和注释可知:cdecl
参数调用顺序:1.使用栈传参,并且顺序是从右往左

0045AF87 E804FFFFFF call Plus_cdecl
0045AF8C 83C424 add esp,$24
//为什么这里使用 add esp,$24,而stdcall、register则没有该退栈操作?
答:因为cdecl调用约定,参数栈由父函数(调用函数)清理,所以需要在调用函数中使用add esp,$24,来回退参数栈,而stdcall、register调用约定由被调用函数自己清空,所以不需要在调用函数中加add esp,$24

AsmTest.pas.70: end;
0045AF8F 8BE5 mov esp,ebp
//为什么先使用add esp,$24回退参数栈,然后才使用mov esp,ebp回退局部变量栈?
答:因为局部变量栈是调用函数执行一开始便分配了,而参数栈在调用子函数call Plus_cdecl之前之前便已分配,按照栈后进先出的特性,所以先回退参数栈,然后回退局部变量栈
0045AF91 5D pop ebp
0045AF92 C3 ret
0045AF93 90 nop

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值