轉載 linux/unix 段錯誤捕獲【續】

原文地址 http://blog.chinaunix.net/uid-26009500-id-1585783.html


一、出錯代碼在動態鏈接庫內時,原方法的輸出
 
    有些情況下,我們會採用動態鏈接庫,如果出錯代碼行恰巧在動態鏈接庫內,原方法仍可得到出錯時的地址。例如:
  1. signal[8] catched when running code at 8048ab3

  2. signal[8] catched when running code at 4001771b

  3. signal[8] catched when running code at 400176fd
    此例中,調用addr2line小工具的輸出爲
  1. [root@redhat tcpBreak]# addr2line 8048ab3 4001771b 400176fd -s -C -f -e a.out

  2. main

  3. test.cpp:15

  4. ??

  5. ??:0

  6. ??

  7. ??:0
    顯然,後面兩個地址翻譯不出來了,因爲其實出錯代碼根本不在可執行文件 a.out 內,而是位於一個動態鏈接庫內。
 
二、動態鏈接庫的偏移地址
 
     動態鏈接庫無非就是編譯後的代碼,裏面有一些基本的段、符號信息。如果出錯代碼行在動態鏈接庫內,那必然可以從動態鏈接庫內找到出錯時的代碼行號。
     好吧,那就讓我們試一下:
  1. [root@redhat tcpBreak]# addr2line 4001771b 400176fd -s -C -f -e libtest.so

  2. ??

  3. ??:0

  4. ??

  5. ??:0
    還是翻譯不出來。當然出不來了,因爲進程掛掉時輸出的地址,和動態鏈接庫文件內的靜態偏移地址根本就不是一回事。所以我們需要知道出錯時,所輸出的代碼地址與動態鏈接庫偏移地址之間的關係。
    事實上,每一個進程都對應了一個 /proc/pid 目錄,下面記載了諸多與該進程相關的信息,其中有一個maps文件,裏面記錄了各個動態鏈接庫的加載地址。我們只需要根據所得到的出錯地址,以及這個maps文件,就可以得出具體是哪一個庫,相應的偏移地址是多少。本文用例產生的輸出爲:
  1. -------------------------- 進程掛掉時的MAPS文件 --------------------------

  2. 08048000-08049000 r-xp 00000000 00:09 17256 /mnt/hgfs/share/net/tcpBreak/a.out

  3. 08049000-0804a000 rw-p 00001000 00:09 17256 /mnt/hgfs/share/net/tcpBreak/a.out

  4. 0804a000-0804b000 rwxp 00000000 00:00 0

  5. 40000000-40015000 r-xp 00000000 08:02 271023 /lib/ld-2.3.2.so

  6. 40015000-40016000 rw-p 00014000 08:02 271023 /lib/ld-2.3.2.so

  7. 40016000-40017000 rw-p 00000000 00:00 0

  8. 40017000-40018000 r-xp 00000000 00:09 17255 /mnt/hgfs/share/net/tcpBreak/libtest.so

  9. 40018000-40019000 rw-p 00000000 00:09 17255 /mnt/hgfs/share/net/tcpBreak/libtest.so

  10. 40019000-4001b000 rw-p 00000000 00:00 0

  11. 40026000-400cf000 r-xp 00000000 08:02 350892 /usr/lib/libstdc++.so.5.0.3

  12. 400cf000-400d4000 rw-p 000a9000 08:02 350892 /usr/lib/libstdc++.so.5.0.3

  13. 400d4000-400d9000 rw-p 00000000 00:00 0

  14. 400d9000-400fa000 r-xp 00000000 08:02 286922 /lib/tls/libm-2.3.2.so

  15. 400fa000-400fb000 rw-p 00020000 08:02 286922 /lib/tls/libm-2.3.2.so

  16. 400fb000-40102000 r-xp 00000000 08:02 271272 /lib/libgcc_s-3.2.2-20030225.so.1

  17. 40102000-40103000 rw-p 00007000 08:02 271272 /lib/libgcc_s-3.2.2-20030225.so.1

  18. 40103000-40104000 rw-p 00000000 00:00 0

  19. 42000000-4212e000 r-xp 00000000 08:02 286920 /lib/tls/libc-2.3.2.so

  20. 4212e000-42131000 rw-p 0012e000 08:02 286920 /lib/tls/libc-2.3.2.so

  21. 42131000-42133000 rw-p 00000000 00:00 0

  22. bfffd000-c0000000 rwxp ffffe000 00:00 0

  23. -------------------------------------------------------------------------





  24. --------------------------- 進程掛掉時的棧幀 --------------------------

  25. signal[8] catched when running code at 8048ab3

  26. signal[8] catched when running code at 4001771b

  27. signal[8] catched when running code at 400176fd

  28. -------------------------------------------------------------------------
    顯然 4001771b 400176fd 對應的庫是 libtest.so,偏移地址分別爲 71b 6fd。
 
三、臨門一腳
 
    知道了對應的動態鏈接庫和偏移地址後,我們進一步用 addr2line 將這個偏移地址翻譯一下就可以了。
  1. [root@redhat tcpBreak]# addr2line 71b 6fd -s -C -f -e libtest.so

  2. a()

  3. lib.cpp:14

  4. b()

  5. lib.cpp:10
    至此,大功告成。
 
四、簡而言之
 
    不管是否有用到動態鏈接庫,我們將原方法得到的輸出,結合進程掛掉時maps文件的內容,就可以得到代碼出錯時的執行路徑。根據代碼所在部分,指定相應的文件給 addr2line 的 -e 參數即可。對於上面那個例子:
  1. [root@redhat tcpBreak]# addr2line 8048ab3 -s -C -f -e a.out

  2. main

  3. test.cpp:15

  4. [root@redhat tcpBreak]# addr2line 71b 6fd -s -C -f -e libtest.so

  5. a()

  6. lib.cpp:14

  7. b()

  8. lib.cpp:10
    本文發佈的捕獲出錯執行路徑的方法:
        1 在含有main函數的那個源碼文件裏,包含segvCatch_ext.h這個頭文件
        2 具體如何解析出錯時代碼的執行路徑,閱讀segvCatch_ext.h頭部的說明
    適用場景已經在前一篇文章裏面描述過了,有問題可以給我發郵件([email protected])。
 
 
五、似有餘味
 
    一個程序啓動後,地址是如何進行映射的,MAPS文件是怎麼生成的,庫又是怎麼加載的,自行編寫動態鏈接庫時,有什麼注意事項...
    這些問題我也不甚明瞭,因爲我自己也沒深究過,以後有時間可能會陸續補到博客裏面。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章