• <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>

            兔子的技術(shù)博客

            兔子

               :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

            留言簿(10)

            最新評論

            閱讀排行榜

            評論排行榜

            作者:上海偉功通信 roc

            下載源代碼

            讀了老羅的“僅通過崩潰地址找出源代碼的出錯行”(下稱"羅文")一文后,感覺該文還是可以學(xué)到不少東西的。不過文中尚存在有些說法不妥,以及有些操作太繁瑣的地方。為此,本人在學(xué)習(xí)了此文后,在多次實(shí)驗(yàn)實(shí)踐基礎(chǔ)上,把該文中的一些內(nèi)容進(jìn)行補(bǔ)充與改進(jìn),希望對大家調(diào)試程序,尤其是release版本的程序有幫助。歡迎各位朋友批評指正。


            一、該方法適用的范圍
            在windows程序中造成程序崩潰的原因很多,而文中所述的方法僅適用與:由一條語句當(dāng)即引起的程序崩潰。如原文中舉的除數(shù)為零的崩潰例子。而筆者在實(shí)際工作中碰到更多的情況是:指針指向一非法地址,然后對指針的內(nèi)容進(jìn)行了,讀或?qū)懙牟僮?。例如?br>

            void Crash1()
            {
            char * p =(char*)100;
            *p=100;
            }
            這些原因造成的崩潰,無論是debug版本,還是release版本的程序,使用該方法都可找到造成崩潰的函數(shù)或子程序中的語句行,具體方法的下面還會補(bǔ)充說明。另外,實(shí)踐中另一種常見的造成程序崩潰的原因:函數(shù)或子程序中局部變量數(shù)組越界付值,造成函數(shù)或子程序返回地址遭覆蓋,從而造成函數(shù)或子程序返回時崩潰。例如:
            #include 
            void Crash2();
            int main(int argc,char* argv[])
            {
            Crash2();
            return 0;
            }

            void Crash2()
            {
            char p[1];
            strcpy(p,"0123456789");
            }
            在vc中編譯運(yùn)行此程序的release版本,會跳出如下的出錯提示框。


            圖一 上面例子運(yùn)行結(jié)果

            這里顯示的崩潰地址為:0x34333231。這種由前面語句造成的崩潰根源,在后續(xù)程序中方才顯現(xiàn)出來的情況,顯然用該文所述的方法就無能為力了。不過在此例中多少還有些蛛絲馬跡可尋找到崩潰的原因:函數(shù)Crash2中的局部數(shù)組p只有一個字節(jié)大小,顯然拷貝"0123456789"這個字符串會把超出長度的字符串拷貝到數(shù)組p的后面,即*(p+1)=''1'',*(p+2)=''2'',* (p+3)=''3'',*(p+4)=4。。。。。。而字符''1''的ASC碼的值為0x31,''2''為0x32,''3''為 0x33,''4''為0x34。。。。。,由于intel的cpu中int型數(shù)據(jù)是低字節(jié)保存在低地址中,所以保存字符串''1234''的內(nèi)存,顯示為一個4字節(jié)的int型數(shù)時就是0x34333231。顯然拷貝"0123456789"這個字符串時,"1234"這幾個字符把函數(shù)Crash2的返回地址給覆蓋,從而造成程序崩潰。對于類似的這種造成程序崩潰的錯誤朋友們還有其他方法排錯的話,歡迎一起交流討論。


            二、設(shè)置編譯產(chǎn)生map文件的方法
            該文中產(chǎn)生map文件的方法是手工添加編譯參數(shù)來產(chǎn)生map文件。其實(shí)在vc6的IDE中有產(chǎn)生map文件的配置選項(xiàng)的。操作如下:先點(diǎn)擊菜單"Project"->"Settings。。。",彈出的屬性頁中選中"Link"頁,確保在"category"中選中"General",最后選中"Generate mapfile"的可選項(xiàng)。若要在在map文件中顯示Line numbers的信息的話,還需在project options 中加入/mapinfo:lines 。Line numbers信息對于"羅文"所用的方法來定位出錯源代碼行很重要,但筆者后面會介紹更加好的方法來定位出錯代碼行,那種方法不需要Line numbers信息。


            圖二 設(shè)置產(chǎn)生MAP文件


            三、定位崩潰語句位置的方法
            "羅文"所述的定位方法中,找到產(chǎn)生崩潰的函數(shù)位置的方法是正確的,即在map文件列出的每個函數(shù)的起始地址中,最近的且不大于崩潰地址的地址即為包含崩潰語句的函數(shù)的地址。但之后的再進(jìn)一步的定位出錯語句行的方法不是最妥當(dāng),因?yàn)槟欠N方法前提是,假設(shè)基地址的值是 0x00400000 ,以及一般的 PE 文件的代碼段都是從 0x1000偏移開始的。雖然這種情況很普遍,但在vc中還是可以基地址設(shè)置為其他數(shù),比如設(shè)置為0x00500000,這時仍舊套用
             崩潰行偏移 = 崩潰地址 - 0x00400000 - 0x1000 
            的公式顯然無法找到崩潰行偏移。 其實(shí)上述公式若改為
            崩潰行偏移 = 崩潰地址 - 崩潰函數(shù)絕對地址 + 函數(shù)相對偏移
            即可通用了。仍以"羅文"中的例子為例:"羅文"中提到的在其崩潰程序的對應(yīng)map文件中,崩潰函數(shù)的編譯結(jié)果為
            0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo。obj 
            對與上述結(jié)果,在使用我的公式時,"崩潰函數(shù)絕對地址"指00401020, 函數(shù)相對偏移指 00000020,當(dāng)崩潰地址= 0x0040104a時, 則 崩潰行偏移 = 崩潰地址 - 崩潰函數(shù)起始地址+ 函數(shù)相對偏移 = 0x0040104a - 0x00401020 + 0x00000020= 0x4a,結(jié)果與"羅文"計(jì)算結(jié)果相同。但這個公式更通用。


            四、更好的定位崩潰語句位置的方法。
            其實(shí)除了依靠map文件中的Line numbers信息最終定位出錯語句行外,在vc6中我們還可以通過編譯程序產(chǎn)生的對應(yīng)的匯編語句,二進(jìn)制碼,以及對應(yīng)c/c++語句為一體的"cod"文件來定位出錯語句行。先介紹一下產(chǎn)生這種包含了三種信息的"cod"文件的設(shè)置方法:先點(diǎn)擊菜單"Project"->"Settings。。。",彈出的屬性頁中選中"C/C++"頁,然后在"Category"中選則"Listing Files",再在"Listing file type"的組合框中選擇"Assembly,Machine code, and source"。接下去再通過一個具體的例子來說明這種方法的具體操作。


            圖三 設(shè)置產(chǎn)生"cod"文件

            準(zhǔn)備步驟1)產(chǎn)生崩潰的程序如下:
            01 //****************************************************************
            02 //文件名稱:crash。cpp
            03 //作用: 演示通過崩潰地址找出源代碼的出錯行新方法
            04 //作者: 偉功通信 roc
            05 //日期: 2005-5-16
            06//****************************************************************
            07 void Crash1();
            08 int main(int argc,char* argv[])
            09 {
            10 Crash1();
            11 return 0;
            12 }
            13
            14 void Crash1()
            15 {
            16 char * p =(char*)100;
            17 *p=100;
            18 }
            準(zhǔn)備步驟2)按本文所述設(shè)置產(chǎn)生map文件(不需要產(chǎn)生Line numbers信息)。
            準(zhǔn)備步驟3)按本文所述設(shè)置產(chǎn)生cod文件。
            準(zhǔn)備步驟4)編譯。這里以debug版本為例(若是release版本需要將編譯選項(xiàng)改為不進(jìn)行任何優(yōu)化的選項(xiàng),否則上述代碼會因?yàn)閮?yōu)化時看作廢代碼而不被編譯,從而看不到崩潰的結(jié)果),編譯后產(chǎn)生一個"exe"文件,一個"map"文件,一個"cod"文件。
            運(yùn)行此程序,產(chǎn)生如下如下崩潰提示:


            圖四 上面例子運(yùn)行結(jié)果

            排錯步驟1)定位崩潰函數(shù)??梢圆樵僲ap文件獲得。我的機(jī)器編譯產(chǎn)生的map文件的部分如下:
             Crash

            Timestamp is 42881a01 (Mon May 16 11:56:49 2005)

            Preferred load address is 00400000

            Start Length Name Class
            0001:00000000 0000ddf1H .text CODE
            0001:0000ddf1 0001000fH .textbss CODE
            0002:00000000 00001346H .rdata DATA
            0002:00001346 00000000H .edata DATA
            0003:00000000 00000104H .CRT$XCA DATA
            0003:00000104 00000104H .CRT$XCZ DATA
            0003:00000208 00000104H .CRT$XIA DATA
            0003:0000030c 00000109H .CRT$XIC DATA
            0003:00000418 00000104H .CRT$XIZ DATA
            0003:0000051c 00000104H .CRT$XPA DATA
            0003:00000620 00000104H .CRT$XPX DATA
            0003:00000724 00000104H .CRT$XPZ DATA
            0003:00000828 00000104H .CRT$XTA DATA
            0003:0000092c 00000104H .CRT$XTZ DATA
            0003:00000a30 00000b93H .data DATA
            0003:000015c4 00001974H .bss DATA
            0004:00000000 00000014H .idata$2 DATA
            0004:00000014 00000014H .idata$3 DATA
            0004:00000028 00000110H .idata$4 DATA
            0004:00000138 00000110H .idata$5 DATA
            0004:00000248 000004afH .idata$6 DATA

            Address Publics by Value Rva+Base Lib:Object

            0001:00000020 _main 00401020 f Crash.obj
            0001:00000060 ?Crash1@@YAXXZ 00401060 f Crash.obj
            0001:000000a0 __chkesp 004010a0 f LIBCD:chkesp.obj
            0001:000000e0 _mainCRTStartup 004010e0 f LIBCD:crt0.obj
            0001:00000210 __amsg_exit 00401210 f LIBCD:crt0.obj
            0001:00000270 __CrtDbgBreak 00401270 f LIBCD:dbgrpt.obj
            ...
            對于崩潰地址0x00401082而言,小于此地址中最接近的地址(Rva+Base中的地址)為00401060,其對應(yīng)的函數(shù)名為?Crash1@@YAXXZ,由于所有以問號開頭的函數(shù)名稱都是 C++ 修飾的名稱,"@@YAXXZ"則為區(qū)別重載函數(shù)而加的后綴,所以?Crash1@@YAXXZ就是我們的源程序中,Crash1() 這個函數(shù)。
            排錯步驟2)定位出錯行。打開編譯生成的"cod"文件,我機(jī)器上生成的文件內(nèi)容如下:
            	TITLE	E:\Crash\Crash。cpp
            .386P
            include listing.inc
            if @Version gt 510
            .model FLAT
            else
            _TEXT SEGMENT PARA USE32 PUBLIC ''CODE''
            _TEXT ENDS
            _DATA SEGMENT DWORD USE32 PUBLIC ''DATA''
            _DATA ENDS
            CONST SEGMENT DWORD USE32 PUBLIC ''CONST''
            CONST ENDS
            _BSS SEGMENT DWORD USE32 PUBLIC ''BSS''
            _BSS ENDS
            $$SYMBOLS SEGMENT BYTE USE32 ''DEBSYM''
            $$SYMBOLS ENDS
            $$TYPES SEGMENT BYTE USE32 ''DEBTYP''
            $$TYPES ENDS
            _TLS SEGMENT DWORD USE32 PUBLIC ''TLS''
            _TLS ENDS
            ; COMDAT _main
            _TEXT SEGMENT PARA USE32 PUBLIC ''CODE''
            _TEXT ENDS
            ; COMDAT ?Crash1@@YAXXZ
            _TEXT SEGMENT PARA USE32 PUBLIC ''CODE''
            _TEXT ENDS
            FLAT GROUP _DATA, CONST, _BSS
            ASSUME CS: FLAT, DS: FLAT, SS: FLAT
            endif
            PUBLIC ?Crash1@@YAXXZ ; Crash1
            PUBLIC _main
            EXTRN __chkesp:NEAR
            ; COMDAT _main
            _TEXT SEGMENT
            _main PROC NEAR ; COMDAT

            ; 9 : {

            00000 55 push ebp
            00001 8b ec mov ebp, esp
            00003 83 ec 40 sub esp, 64 ; 00000040H
            00006 53 push ebx
            00007 56 push esi
            00008 57 push edi
            00009 8d 7d c0 lea edi, DWORD PTR [ebp-64]
            0000c b9 10 00 00 00 mov ecx, 16 ; 00000010H
            00011 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
            00016 f3 ab rep stosd

            ; 10 : Crash1();

            00018 e8 00 00 00 00 call ?Crash1@@YAXXZ ; Crash1

            ; 11 : return 0;

            0001d 33 c0 xor eax, eax

            ; 12 : }

            0001f 5f pop edi
            00020 5e pop esi
            00021 5b pop ebx
            00022 83 c4 40 add esp, 64 ; 00000040H
            00025 3b ec cmp ebp, esp
            00027 e8 00 00 00 00 call __chkesp
            0002c 8b e5 mov esp, ebp
            0002e 5d pop ebp
            0002f c3 ret 0
            _main ENDP
            _TEXT ENDS
            ; COMDAT ?Crash1@@YAXXZ
            _TEXT SEGMENT
            _p$ = -4
            ?Crash1@@YAXXZ PROC NEAR ; Crash1, COMDAT

            ; 15 : {

            00000 55 push ebp
            00001 8b ec mov ebp, esp
            00003 83 ec 44 sub esp, 68 ; 00000044H
            00006 53 push ebx
            00007 56 push esi
            00008 57 push edi
            00009 8d 7d bc lea edi, DWORD PTR [ebp-68]
            0000c b9 11 00 00 00 mov ecx, 17 ; 00000011H
            00011 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
            00016 f3 ab rep stosd

            ; 16 : char * p =(char*)100;

            00018 c7 45 fc 64 00
            00 00 mov DWORD PTR _p$[ebp], 100 ; 00000064H

            ; 17 : *p=100;

            0001f 8b 45 fc mov eax, DWORD PTR _p$[ebp]
            00022 c6 00 64 mov BYTE PTR [eax], 100 ; 00000064H

            ; 18 : }

            00025 5f pop edi
            00026 5e pop esi
            00027 5b pop ebx
            00028 8b e5 mov esp, ebp
            0002a 5d pop ebp
            0002b c3 ret 0
            ?Crash1@@YAXXZ ENDP ; Crash1
            _TEXT ENDS
            END
            其中
            ?Crash1@@YAXXZ PROC NEAR				; Crash1, COMDAT
            為Crash1匯編代碼的起始行。產(chǎn)生崩潰的代碼便在其后的某個位置。接下去的一行為:
            ; 15   : {
            冒號后的"{"表示源文件中的語句,冒號前的"15"表示該語句在源文件中的行數(shù)。這之后顯示該語句匯編后的偏移地址,二進(jìn)制碼,匯編代碼。如
            00000	55		 push	 ebp
            其中"0000"表示相對于函數(shù)開始地址后的偏移,"55"為編譯后的機(jī)器代碼," push ebp"為匯編代碼。從"cod"文件中我們可以看出,一條(c/c++)語句通常需要編譯成數(shù)條匯編語句。此外有些匯編語句太長則會分兩行顯示如:
            00018	c7 45 fc 64 00
            00 00 mov DWORD PTR _p$[ebp], 100 ; 00000064H
            其中"0018"表示相對偏移,在debug版本中,這個數(shù)據(jù)為相對于函數(shù)起始地址的偏移(此時每個函數(shù)第一條語句相對偏移為0000);release版本中為相對于代碼段第一條語句的偏移(即代碼段第一條語句相對偏移為0000,而以后的每個函數(shù)第一條語句相對偏移就不為0000了)。"c7 45 fc 64 00 00 00 "為編譯后的機(jī)器代碼 ,"mov DWORD PTR _p$[ebp], 100"為匯編代碼,匯編語言中";"后的內(nèi)容為注釋,所以";00000064H",是個注釋這里用來說明100轉(zhuǎn)換成16進(jìn)制時為"00000064H"。
            接下去,我們開始來定位產(chǎn)生崩潰的語句。
            第一步,計(jì)算崩潰地址相對于崩潰函數(shù)的偏移,在本例中已經(jīng)知道了崩潰語句的地址(0x00401082),和對應(yīng)函數(shù)的起始地址(0x00401060),所以崩潰地址相對函數(shù)起始地址的偏移就很容易計(jì)算了:
              崩潰偏移地址 = 崩潰語句地址 - 崩潰函數(shù)的起始地址 = 0x00401082 - 0x00401060 = 0x22。
            第二步,計(jì)算出錯的匯編語句在cod文件中的相對偏移。我們可以看到函數(shù)Crash1()在cod文件中的相對偏移地址為0000,則
            崩潰語句在cod文件中的相對偏移 =  崩潰函數(shù)在cod文件中相對偏移 + 崩潰偏移地址 = 0x0000 + 0x22 = 0x22
            第三步,我們看Crash1函數(shù)偏移0x22除的代碼是什么?結(jié)果如下
             00022	c6 00 64	 mov	 BYTE PTR [eax], 100	; 00000064H
            這句匯編語句表示將100這個數(shù)保存到寄存器eax所指的內(nèi)存單元中去,保存空間大小為1個字節(jié)(byte)。程序正是執(zhí)行這條命令時產(chǎn)生了崩潰,顯然這里eax中的為一個非法地址,所以程序崩潰了!
            第四步,再查看該匯編語句在其前面幾行的其對應(yīng)的源代碼,結(jié)果如下:
            ; 17   :  *p=100;
            其中17表示該語句位于源文件中第17行,而“*p=100;”這正是源文件中產(chǎn)生崩潰的語句。
            至此我們僅從崩潰地址就查找出了造成崩潰的源代碼語句和該語句所在源文件中的確切位置,甚至查找到了造成崩潰的編譯后的確切匯編代碼!
            怎么樣,是不是感覺更爽啊?


            五、小節(jié)

            1、新方法同樣要注意可以適用的范圍,即程序由一條語句當(dāng)即引起的崩潰。另外我不知道除了VC6外,是否還有其他的編譯器能夠產(chǎn)生類似的"cod"文件。
            2、我們可以通過比較 新方法產(chǎn)生的debug和releae版本的"cod"文件,查找那些僅release版本(或debug版本)有另一個版本沒有的bug(或其他性狀)。例如"羅文"中所舉的那個用例,只要打開release版本的"cod"文件,就明白了為啥debug版本會產(chǎn)生崩潰而release版本卻沒有:原來release版本中產(chǎn)生崩潰的語句其實(shí)根本都沒有編譯。同樣本例中的release版本要看到崩潰的效果,需要將編譯選項(xiàng)改為為不優(yōu)化的配置。


            轉(zhuǎn)自:http://www.cnblogs.com/xxrl/archive/2008/05/06/1185337.html
            posted on 2011-04-06 15:16 會飛的兔子 閱讀(427) 評論(0)  編輯 收藏 引用 所屬分類: C++及開發(fā)環(huán)境
            国内精品久久久久影院免费| 久久婷婷五月综合色99啪ak| 97超级碰碰碰碰久久久久| 久久久久这里只有精品 | 亚洲AV日韩精品久久久久久久| 亚洲AV日韩精品久久久久| 国产精品久久精品| 久久久久久久久波多野高潮| 国产精品毛片久久久久久久| 久久这里只精品99re66| 久久精品国产只有精品2020| 久久精品日日躁夜夜躁欧美| 久久天堂电影网| 亚洲va国产va天堂va久久| 久久久久久青草大香综合精品| 久久A级毛片免费观看| 一级A毛片免费观看久久精品| 好属妞这里只有精品久久| 久久99热这里只有精品66| 色综合久久88色综合天天| 久久这里只有精品18| 麻豆精品久久久久久久99蜜桃| 国产精品一区二区久久精品无码| 久久久无码精品亚洲日韩蜜臀浪潮| 一本色道久久综合狠狠躁篇 | 国产免费久久精品99久久| 久久精品麻豆日日躁夜夜躁| 久久精品国产免费观看三人同眠| 久久久久亚洲AV无码去区首| 大蕉久久伊人中文字幕| 狠狠色噜噜狠狠狠狠狠色综合久久 | 久久人爽人人爽人人片AV| 久久国内免费视频| 亚洲欧美一区二区三区久久| 久久夜色撩人精品国产| 三级片免费观看久久| 亚洲欧洲精品成人久久曰影片| 久久久久18| 日本国产精品久久| 欧美日韩精品久久久久| 久久精品国产日本波多野结衣|