免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
函數(shù)是如何被調(diào)用的?

C/C++語(yǔ)言中,函數(shù)是如何被調(diào)用的呢?本文就實(shí)際的例子,走進(jìn)匯編代碼來(lái)看下函數(shù)調(diào)用的過(guò)程。

首先看一個(gè)簡(jiǎn)單的代碼例子:

void test(int i)

{

    int j = i;

}

 

void test1()

{

 

}

 

int test2()

{

    return 1;

}

 

void test3(int a,int b,int c)

{

}

 

void test4()

{

    int i,j;

}

 

void test5()

{

    int i,j,k,l;

}

 

int main()

{  

    int i =0;

    test1();

   

    test(10);

   

    test3(1,2,3);

 

    i=test2();

   

    test4();

   

    test5();

 

    return 0;

}

 

這段代碼很簡(jiǎn)單,mian函數(shù)調(diào)用幾個(gè)被測(cè)試的函數(shù),分別是:

1.  沒(méi)有參數(shù)

2.  有一個(gè)參數(shù)

3.  3個(gè)參數(shù)

4.  有返回值

5.  有兩個(gè)臨時(shí)變量

6.  有多個(gè)臨時(shí)變量

 

VC7中,我們將斷點(diǎn)設(shè)置到main函數(shù)入口的地方;然后F5運(yùn)行程序。再按ALT+8反匯編,我們看到下面的代碼:

Main函數(shù)變成這樣了:

int main()

{  

00401120  push        ebp 

00401121  mov         ebp,esp

00401123  sub         esp,0CCh

00401129  push        ebx 

0040112A  push        esi 

0040112B  push        edi 

0040112C  lea         edi,[ebp-0CCh]

00401132  mov         ecx,33h

00401137  mov         eax,0CCCCCCCCh

0040113C  rep stos    dword ptr [edi]

    int i =0;

0040113E  mov         dword ptr [i],0 //直接將數(shù)據(jù)0放到指定地址中

    test1();

00401145  call        test1 (401030h)

   

    test(10);

0040114A  push        0Ah 

0040114C  call        test (401000h)

00401151  add         esp,4

   

    test3(1,2,3);

00401154  push        3   

00401156  push        2   

00401158  push        1   

0040115A  call        test3 (401090h)

0040115F  add         esp,0Ch

 

    i=test2();

00401162  call        test2 (401060h)

00401167  mov         dword ptr [i],eax

 

    test4();

0040116A  call        test4 (4010C0h)

   

    test5();

0040116F  call        test5 (4010F0h)

 

    return 0;

00401174  xor         eax,eax

}

00401176  pop         edi 

00401177  pop         esi 

00401178  pop         ebx 

00401179  add         esp,0CCh

0040117F  cmp         ebp,esp

00401181  call        _RTC_CheckEsp (4011E0h)

00401186  mov         esp,ebp

00401188  pop         ebp 

00401189  ret             

 

函數(shù)入口部分:

00401120  push        ebp  //保存ebp的值

00401121  mov         ebp,esp //將當(dāng)前棧頂指針?biāo)偷?/span>ebp

00401123  sub         esp,0CCh //將棧頂指針下移0XCC個(gè)字節(jié),為臨時(shí)變量留出空間

00401129  push        ebx  //保存ebx

0040112A  push        esi  //保存esi

0040112B  push        edi  //保存edi

0040112C  lea         edi,[ebp-0CCh] //edp-0CC地址送EAX

00401132  mov         ecx,33h //CC/4得到的

00401137  mov         eax,0CCCCCCCCh //初始化為0XCCCCCCCCH

0040113C  rep stos    dword ptr [edi]//復(fù)制

這寫(xiě)匯編是編譯器為我們生成的函數(shù)入口部分,基本的含義是為臨時(shí)變量分配空間,并且初始化臨時(shí)變量。

這里需要說(shuō)明幾點(diǎn):

1.  函數(shù)調(diào)用是通過(guò)堆棧來(lái)完成的。

2.  函數(shù)入口的地方必須為臨時(shí)變量分配一定空間;實(shí)際上如果沒(méi)有臨時(shí)變量,也要留出C0個(gè)字節(jié)。

3.  堆棧棧頂指針隨數(shù)據(jù)的進(jìn)入逐漸減小。因此sub esp,0CCh實(shí)際上是留出了CC個(gè)自己的堆??臻g。

我們看到實(shí)現(xiàn)將棧頂指針保存在ebp中,然后對(duì)該段空間設(shè)置初始值。而0XCCCCCCH是由堆棧的性質(zhì)決定,可以看MSDN。

如果開(kāi)始的時(shí)候假設(shè)ESP等于0X12FEE0,那么在保存EBP之后,ESP變成0X12FEDC,那么后來(lái)EBP中的值就是這個(gè)值,在保存的空間(從0X12FE100X12FEDC)上將所有的內(nèi)存都初始化為0XCC。而i被分配在0X12FED4處,也就是第一個(gè)預(yù)留的位置)。

 

 

call        test1 (401030h)

由于已經(jīng)知道i的地址了,對(duì)i的賦值就很簡(jiǎn)單了。這里看調(diào)用第一個(gè)沒(méi)有參數(shù)沒(méi)有返回值的test1函數(shù);僅僅一條語(yǔ)句,將test1的函數(shù)地址給call指令。

EAX = CCCCCCCC EBX = 7FFDE000 ECX = 00000000 EDX = 00000001

ESI = 00000040 EDI = 0012FEDC EIP = 00401145 ESP = 0012FE04

EBP = 0012FEDC EFL = 00000202

上面是Call指令調(diào)用前各寄存器的值;下面是調(diào)用后的值:

EAX = CCCCCCCC EBX = 7FFD7000 ECX = 00000000 EDX = 00000001

ESI = 00000040 EDI = 0012FEDC EIP = 00401030 ESP = 0012FE00

EBP = 0012FEDC EFL = 00000202

主要變化在于EIPESP;前者是指令指針寄存器,而后者是堆棧指針寄存器。調(diào)用前指令的位置在00401145位置,而call指定將EIP改為test1的地址;同時(shí)將返回地址入棧;可以看到當(dāng)前棧頂?shù)闹凳?/span>0040114A,實(shí)際上是test1的下條指令。

因此我們說(shuō)Call指定做了兩件事情:

1.  EIP從當(dāng)前值改為被調(diào)用函數(shù)的值。

2.  將返回地址,也就是當(dāng)前地址的下條指令放入堆棧。

 

現(xiàn)在進(jìn)入test1中看個(gè)究竟。

void test1()

{

00401030  push        ebp 

00401031  mov         ebp,esp

00401033  sub         esp,0C0h

00401039  push        ebx 

0040103A  push        esi 

0040103B  push        edi 

0040103C  lea         edi,[ebp-0C0h]

00401042  mov         ecx,30h

00401047  mov         eax,0CCCCCCCCh

0040104C  rep stos    dword ptr [edi]

 

}

0040104E  pop         edi 

0040104F  pop         esi 

00401050  pop         ebx 

00401051  mov         esp,ebp

00401053  pop         ebp 

00401054  ret            

上面的命令基本相同,主要區(qū)別在于test1內(nèi)部沒(méi)有臨時(shí)變量,因此這里只保留了C0個(gè)自己的空間。

 

繼續(xù)回到主程序:

    test(10);

0040114A  push        0Ah 

0040114C  call        test (401000h)

00401151  add         esp,4

由于test函數(shù)有一個(gè)參數(shù),因此需要首先將參數(shù)壓入堆棧中,然后執(zhí)行與前面相似的操作。

這里有一點(diǎn)需要注意:函數(shù)返回之后需要將壓入的參數(shù)彈出;可以使用pop命令,也可以使用add命令來(lái)執(zhí)行。

 

對(duì)于test3的調(diào)用:

    test3(1,2,3);

00401154  push        3   

00401156  push        2   

00401158  push        1   

0040115A  call        test3 (401090h)

0040115F  add         esp,0Ch

 

由于它需要三個(gè)參數(shù),因此都必須壓入棧,返回的時(shí)候一次性彈出。

 

下面看如何調(diào)用帶有返回值的參數(shù):

    i=test2();

00401162  call        test2 (401060h)

00401167  mov         dword ptr [i],eax

其他的相同,但重要的一點(diǎn)是函數(shù)的返回值是通過(guò)eax寄存器來(lái)返回的。

 

其他幾個(gè)函數(shù)的調(diào)用不同的是臨時(shí)變量數(shù)目的不同,僅僅在初始化預(yù)留空間的時(shí)候不同,基本上是每增加一個(gè)變量多出12個(gè)字節(jié)的堆??臻g。

 

mian函數(shù)的返回值,有點(diǎn)特別:

    return 0;

00401174  xor         eax,eax

特別的不在于通過(guò)eax返回,而是自己和自己異或,大部分返回0的函數(shù)都這么做。

 

mian函數(shù)退出的時(shí)候有這段代碼:

00401176  pop         edi 

00401177  pop         esi 

00401178  pop         ebx 

00401179  add         esp,0CCh

0040117F  cmp         ebp,esp

00401181  call        _RTC_CheckEsp (4011E0h)

00401186  mov         esp,ebp

00401188  pop         ebp 

00401189  ret             

前面幾行是將寄存器的值恢復(fù),而add esp0CCh是將保留的堆??臻g釋放,同時(shí)比較ebp是否與esp相等,如果不相等就提示相應(yīng)的錯(cuò)誤,說(shuō)明有內(nèi)存泄露等。最后將ebp彈出然后返回。

 

從上面的分析我們可以看到編譯器為我們做了很多事情,包括:堆??臻g分配和釋放、寄存器狀態(tài)保存、參數(shù)傳遞等。當(dāng)然這些事情也可以完全由我們自己來(lái)完成,那么需要做的是使用關(guān)鍵字naked來(lái)聲明函數(shù)。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
【代碼真相】函數(shù)調(diào)用 堆棧 轉(zhuǎn)載 - liangxiufei - 博客園
從匯編源碼逐步分析函數(shù)調(diào)用過(guò)程
Ted's Blog
一個(gè)簡(jiǎn)單的C++程序反匯編解析
DLL中調(diào)用約定和名稱(chēng)修飾(一)
函數(shù)調(diào)用方式介紹
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服