• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            沒畫完的畫

            喂馬 劈柴 BBQ~
            posts - 37, comments - 55, trackbacks - 0, articles - 0
              C++博客 ::  :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
            注: 本文摘自互聯(lián)網(wǎng),本人看了按理解加了些東西

            為了弄清楚函數(shù)的堆棧,Google了一下,找到下面代碼作為實(shí)驗(yàn)
             1#include <stdio.h>
             2#include <string.h>
             3
             4void func1(int input1, int input2)
             5{
             6    int j;
             7    char c;
             8    short k;
             9
            10    j = 0;
            11    c = 'a';
            12    k = 1;
            13
            14    printf("sum=%d\n", input1+input2);
            15
            16    return;
            17}

            18
            19int main()
            20{
            21    char output[8= "abcdef";
            22    int i, j;
            23
            24    i=2;
            25    j=3;
            26    func1(i,j);
            27
            28    printf("%s\r\n", output);
            29
            30    return 0;
            31}

            32


            本人在 vc6 + winxp 進(jìn)行調(diào)試
            vc6 中在 fun1 設(shè)置斷點(diǎn),按F5,  到達(dá)斷點(diǎn)后,選擇菜單 view -> Disassembly 就可以看到代碼對應(yīng)的匯編語句

            調(diào)用 func1() 之前

            26:       func1(i,j);
            004010D7   mov         ecx,dword ptr [ebp-10h]
            004010DA   push        ecx
            004010DB   mov         edx,dword ptr [ebp-0Ch]
            004010DE   push        edx
            004010DF   call        @ILT+0(func1) (00401005)
            004010E4   add         esp,8
            ------------------------------------------------
            EAX = 00000000 EBX = 7FFDE000 ECX = 00006665 EDX = 00370E00
            ESI = 00000000 EDI = 0012FF80
            EIP = 004010D7 ESP = 0012FF24 EBP = 0012FF80 EFL = 00000246
            ------------------------------------------------
            i,j 分別存放在棧中,地址分別是
            ------------------------------------------------
            ebp-10h = 0x0012FF80h - 0x10h = 0x0012FF70h
            ebp-0Ch = 0x0012FF80h - 0x0Ch = 0x0012FF74h

            0012FF70  03 00 00 00 02 00 00  .......
            0012FF77  00 61 62 63 64 65 66  .abcdef

            0012FF6D  CC CC CC 03 00 00 00  燙.....
            0012FF74  02 00 00 00 61 62 63  ....abc

            從內(nèi)存存放的內(nèi)容可知, i, j 分別存放于 0x0012FF70H, 0x0012FF74H
            分別占了四個(gè)字節(jié)

            在調(diào)用 func1() 之前, 先將 i, j 壓入堆棧, 壓入堆棧的順序是 i, j
            (故出棧的順序是j, i, 請記住, 這里的 傳遞函數(shù)參數(shù) 默認(rèn)是 __cdecl) 

            接著調(diào)用了 call 指令
            執(zhí)行 call 指令之前的寄存器狀態(tài)
            ------------------------------------------------
            EAX = 00000000 EBX = 7FFDE000 ECX = 00000003 EDX = 00000002
            ESI = 00000000 EDI = 0012FF80
            EIP = 004010DF ESP = 0012FF1C EBP = 0012FF80 EFL = 00000246
            ------------------------------------------------
            執(zhí)行 call 指令之后的寄存器狀態(tài)
            EAX = 00000000 EBX = 7FFDE000 ECX = 00000003 EDX = 00000002
            ESI = 00000000 EDI = 0012FF80
            EIP = 00401005 ESP = 0012FF18 EBP = 0012FF80 EFL = 00000246
            ------------------------------------------------
            變化的是 EIP 與 ESP
            棧頂指針寄存器ESP:保存棧頂?shù)刂?指針)
            此時(shí) ESP 向低地址偏移了四個(gè)字節(jié)
            可見 call 指令執(zhí)行了一個(gè) push 操作

            查看 ESP 對象的地址 0x0012FF18 的內(nèi)容
            0012FF18  E4 10 40 00 02 00 00  ..@....
            0012FF1F  00 03 00 00 00 00 00  .......

            push 進(jìn)去的數(shù)是 0x004010E4, 再看回調(diào)用 call 之后的代碼

            26:       func1(i,j);
            004010D7   mov         ecx,dword ptr [ebp-10h]
            004010DA   push        ecx
            004010DB   mov         edx,dword ptr [ebp-0Ch]
            004010DE   push        edx
            004010DF   call        @ILT+0(func1) (00401005)
            004010E4   add         esp,8

            push 進(jìn)去的數(shù)剛好是 call 指令后面的指令地址 0x004010E4  

            為什么沒有看到push指令呢?
            原來在CALL操作中,隱式地把call指令后續(xù)第一條指令的地址0x004010E4 入棧,
            然后再無條件地跳轉(zhuǎn)到func1()函數(shù)繼續(xù)執(zhí)行。

            @ILT+0(?func1@@YAXHH@Z):
            00401005   jmp         func1 (00401020)

            004010DF   call        @ILT+0(func1) (00401005)

            奇怪的事情是 call 后面的操作數(shù)應(yīng)該是 func1 的地址 0x00401020 才對, 為何是 0x00401005?
            還是一個(gè) @ILT+0 的東東是什么?

            在DEBUG版本中,VC匯編程序會產(chǎn)生一個(gè)函數(shù)跳轉(zhuǎn)指令表,
            該表的每個(gè)表項(xiàng)存放一個(gè)函數(shù)的跳轉(zhuǎn)指令。
            程序中的函數(shù)調(diào)用就是利用這個(gè)表來實(shí)現(xiàn)跳轉(zhuǎn)到相應(yīng)函數(shù)的入口地址。

            ILT就是函數(shù)跳轉(zhuǎn)指令表的名稱,是Import Lookup Table的縮寫;
            @ILT就是函數(shù)跳轉(zhuǎn)指令表的首地址。
            在DEBUG版本中增加函數(shù)跳轉(zhuǎn)指令表,其目的是加快編譯速度,當(dāng)某函數(shù)的地址發(fā)生變化時(shí),只需要修改ILT相應(yīng)表項(xiàng)即可,而不需要修改該函數(shù)的每一處引用。
            注意:在RELEASE版本中,不會生成ILT,也就是說call指令的操作數(shù)直接是函數(shù)的入口地址,例如在本例中是這樣的:call 00401020


            接下來, 應(yīng)該看一下 jmp func1 后, 做了哪些東西

            4:    void func1(int input1, int input2)
            5:    {
            00401020   push        ebp
            00401021   mov         ebp,esp
            00401023   sub         esp,4Ch
            00401026   push        ebx
            00401027   push        esi
            00401028   push        edi
            00401029   lea         edi,[ebp-4Ch]
            0040102C   mov         ecx,13h
            00401031   mov         eax,0CCCCCCCCh
            00401036   rep stos    dword ptr [edi]
            ------------------------------------------------
            此時(shí)各寄存器的值為
            EAX = 00000000 EBX = 7FFDE000 ECX = 00000003 EDX = 00000002
            ESI = 00000000 EDI = 0012FF80
            EIP = 00401020 ESP = 0012FF18 EBP = 0012FF80 EFL = 00000246

            ebp 為 main 函數(shù)層的 棧內(nèi)存基地址
            esp 為 當(dāng)前的棧頂?shù)刂?/p>

            00401020   push        ebp
            00401021   mov         ebp,esp

            這兩個(gè)語句 先把 ebp 壓入堆棧, 再把 esp 賦值給 ebp
            這兩句的作用將在后面說明

            00401023   sub         esp,4Ch
            然后,棧頂指針esp向低地址偏移76(0x4C)字節(jié)。這里相當(dāng)于為func1()函數(shù)層分配了棧內(nèi)存。
            (為什么偏偏是 76 是字節(jié)?)
            (題外話: 平時(shí)程序調(diào)試的 stack over 又是如何造成的?)

            接著又把 ebx, esi, edi 分別入棧, 目的是為了保存 main 函數(shù)層的相關(guān)內(nèi)容
            00401026   push        ebx
            00401027   push        esi
            00401028   push        edi

            最后用0xCC初始化上述為func1()函數(shù)層所分配的棧內(nèi)存的每個(gè)字節(jié)。這里每一步用F11單步跟蹤,棧內(nèi)存的變化你會看得更清楚。
            00401029   lea         edi,[ebp-4Ch]     ; 將有效的地址 [ebp-0x4Ch] 賦值到 edi
            0040102C   mov         ecx,13h           ;
            00401031   mov         eax,0CCCCCCCCh    ;
            00401036   rep stos    dword ptr [edi]   ;

            stos指令:
            字符串存儲指令 STOS
            格式: STOS OPRD
            其中OPRD為目的串符號地址.
            功能: 把AL(字節(jié))或AX(字)中的數(shù)據(jù)存儲到DI為目的串地址指針?biāo)鶎ぶ返拇鎯ζ鲉卧腥?指針DI將根據(jù)DF的值進(jìn)行自動調(diào)整.
            由于上面的指令是 dword ptr 類型
            dword 表示雙字 ptr 表示取首地址
            那么 stos    dword ptr [edi] 執(zhí)行的操作就是
            將 ES:[DI]←AX,DI←DI±4   (DI 加或減是由 DF 標(biāo)志位確定的)
            如果是 那么 stos word ptr [edi] 的話那么就是
            將 ES:[DI]←AL,DI←DI±2   (DI 加或減是由 DF 標(biāo)志位確定的)
            不然推出 stos BYTE ptr [edi]

            注:
            DF:方向標(biāo)志DF位用來決定在串操作指令執(zhí)行時(shí)有關(guān)指針寄存器發(fā)生調(diào)整的方向。

            重復(fù)前綴
            格式: REP           ;CX<>0 重復(fù)執(zhí)行字符串指令

            REP 每執(zhí)行一次后面的字符串指令后, cx減1, 直至 cx 為0
            在本例中, 每次拷貝 sizeof(DWORD) 四個(gè)字節(jié), 而堆棧大小是 76(0x4C) 個(gè)字節(jié), 故 只需要重復(fù)執(zhí)行 76 / 4 = 19(0x13) 次就可以了

            0040102C   mov         ecx,13h           ;

            現(xiàn)在終于清楚

            00401029   lea         edi,[ebp-4Ch]     ; 將有效的地址 [ebp-0x4Ch] 賦值到 edi
            0040102C   mov         ecx,13h           ;
            00401031   mov         eax,0CCCCCCCCh    ;
            00401036   rep stos    dword ptr [edi]   ;

            的作用就是把堆棧的數(shù)據(jù)置為 0xCC;


            困了,先去睡下再寫第2集~~

            Feedback

            # re: 函數(shù)堆棧是這么回事 第1集[未登錄]  回復(fù)  更多評論   

            2008-09-26 10:43 by flyswift
            很好很強(qiáng)大。LZ繼續(xù)。

            # re: 函數(shù)堆棧是這么回事 第1集  回復(fù)  更多評論   

            2008-09-26 13:55 by ljbxc
            支持,繼續(xù)

            # re: 函數(shù)堆棧是這么回事 第1集  回復(fù)  更多評論   

            2008-09-26 15:23 by luke
            入棧的順序應(yīng)該是j,i吧?

            # re: 函數(shù)堆棧是這么回事 第1集  回復(fù)  更多評論   

            2008-09-27 16:03 by 908971
            mark
            伊人久久免费视频| 久久天天躁狠狠躁夜夜2020| 久久久无码精品亚洲日韩蜜臀浪潮| 久久久久国色AV免费观看| 伊色综合久久之综合久久| 精品一二三区久久aaa片| 久久精品免费一区二区三区| 久久综合久久综合九色| 亚洲精品乱码久久久久久不卡| 久久这里只有精品18| 精品无码人妻久久久久久| 久久精品国产亚洲av麻豆蜜芽| 国产Av激情久久无码天堂| 久久精品成人欧美大片| 精品久久无码中文字幕| 色综合久久88色综合天天 | 97r久久精品国产99国产精| 亚洲日本久久久午夜精品| 久久亚洲国产午夜精品理论片| 欧美一区二区久久精品| 国产成人久久精品二区三区| 日韩人妻无码一区二区三区久久 | 久久中文字幕一区二区| 伊人久久五月天| 久久精品国产亚洲5555| 久久Av无码精品人妻系列| 国产精品美女久久福利网站| 久久99精品久久久久久齐齐| 精品久久久久久国产潘金莲 | 久久国产色AV免费看| 亚洲?V乱码久久精品蜜桃 | 亚洲国产另类久久久精品小说| 国产成人香蕉久久久久| 久久久久高潮毛片免费全部播放| 无码精品久久一区二区三区| 国内精品久久久久久中文字幕| 精品国产乱码久久久久久1区2区| 亚洲熟妇无码另类久久久| 亚洲精品乱码久久久久久| 日韩精品无码久久久久久| 日韩精品久久无码人妻中文字幕|