在傳統C中,函數的參數和返回值都是以複製傳送的.
看這段代碼
struct Big
{
char buf[1024];
}B,B2;
Big bigfun(Big b)
{
return b;
}
int main()
{
B2 = bigfun(B);
return 0;
}
其中B2 = bigfun(B)要被由以下幾個過程組成
1.B要以傳值方式傳送到函數的參數表中中
2.如果返回值size很小,可以返回eax中
但是在這裏,返回值size很大,要建立臨時變量
然後將臨時變量的地址入棧,此時非常象函數參數入棧
返回eax指向這個臨時變量
3.將這個臨時變量拷貝到B2上
B2 = bigfun(B);
展開爲
;構造臨時堆棧
004017AE sub esp,400h ;堆棧大小1024(400h)
;由於函數調用是傳值方式,所以將B複製到bigfun中的參數中
;相當於將B push到堆棧中
004017B4 mov ecx,100h ;傳送大小1024(100h*4)
004017B9 mov esi,offset B (421138h) ;源是B首地址
004017BE mov edi,esp ;目的爲bigfun中的參數地址
004017C0 rep movs dword ptr [edi],dword ptr [esi] ;複製
;調用函數,此時push eax中的eax並非是函數參數壓棧,而是將一個臨時對象的指針壓棧
004017C2 lea eax,[ebp-4C4h]
004017C8 push eax ;將堆棧中一個臨時對象地址壓堆棧
004017C9 call bigfun (401750h) ;調用函數
004017CE add esp,404h ;清除堆棧(400h+4),堆棧跳過函數參數表和函數返回地址
;臨時對象爲返回變量,eax指向這個地址
;將這個臨時對象複製另一個臨時對象上
004017D4 mov ecx,100h ;傳送大小1024(100h*4)
004017D9 mov esi,eax ;目的上面臨時對象的地址
004017DB lea edi,[ebp-8CCh] ;堆棧中第三個臨時對象
004017E1 rep movs dword ptr [edi],dword ptr [esi] ;複製
;將第二個臨時對象複製到B2上
004017E3 mov ecx,100h ;傳送大小1024(100h*4)
004017E8 lea esi,[ebp-8CCh] ;堆棧中第三個臨時對象
004017EE mov edi,offset B2 (421538h) ;目的爲首地址
004017F3 rep movs dword ptr [edi],dword ptr [esi] ;傳送
Big bigfun(Big b) {
展開爲
;初始化堆棧,以ebp爲基準,ebp+4指向爲return address
;ebp+8爲剛纔壓入堆棧的上層函數中的臨時對象的地址
;ebp-4爲臨時堆棧中第一個局部變量
00401750 push ebp
00401751 mov ebp,esp ;ebp此時指向以前保存bp
00401753 sub esp,0C0h ;建立臨時堆棧,大小0C0h
00401759 push ebx
0040175A push esi
0040175B push edi
;初始化堆棧全部爲0xcc
0040175C lea edi,[ebp-0C0h]
00401762 mov ecx,30h
00401767 mov eax,0CCCCCCCCh
0040176C rep stos dword ptr [edi]
return b;
;複製b到那個臨時對象上
0040176E mov ecx,100h
00401773 lea esi,[b]
00401776 mov edi,dword ptr [ebp+8] ;ebp+8爲參數表中的參數地址
00401779 rep movs dword ptr [edi],dword ptr [esi]
;返回那個臨時變量的地址
0040177B mov eax,dword ptr [ebp+8]
}