Linux下關(guān)于coredump的定位方法說明
一. Linux 下 cordump 機(jī)制
Linux 系統(tǒng)下存在一種 coredump 機(jī)制。
當(dāng)程序運(yùn)行過程中異常終止或崩潰,Linux 操作系統(tǒng)會(huì)將程序當(dāng)時(shí)的堆棧等內(nèi)存狀態(tài)信息記錄下來,會(huì)在指定文件下生成一個(gè) coredump 相關(guān)的 Log 文件。
軟件開發(fā)人員可以通過對(duì) coredump文件進(jìn)行分析,即可定位到導(dǎo)致程序運(yùn)行崩潰的 bug。
當(dāng)程序訪問的內(nèi)存超過了系統(tǒng)給定的內(nèi)存空間,就會(huì)產(chǎn)生 Segmentation fault(core dumped),所以:
一般產(chǎn)生段錯(cuò)誤的原因有如下:
- 1. 訪問了不存在的內(nèi)存地址。
- 2. 訪問了系統(tǒng)保護(hù)的內(nèi)存地址。
- 3. 數(shù)組或堆空間訪問越界,等等一系列內(nèi)存非法訪問操作。
二. coredump 文件定位方法
根據(jù)上一篇配置 coredump 機(jī)制的方法,成功配置好之后。
就可以運(yùn)行代碼,當(dāng)程序運(yùn)行中崩潰時(shí)即可產(chǎn)生相應(yīng)的 coredump 文件。
操作如下:
- 1. 首先,開啟 coredump 功能。設(shè)置成功后,可以通過 ulimit -c 命令驗(yàn)證。
- 2. 其次,關(guān)閉 ubuntu 系統(tǒng)下 apport.service 服務(wù)程序。
- 3. 其次,編譯代碼。編譯時(shí)必須加 -g 編譯選項(xiàng)(帶調(diào)試信息的編譯選項(xiàng))。
- 4. 最后,運(yùn)行程序。當(dāng)程序運(yùn)行時(shí)出現(xiàn) 核心轉(zhuǎn)儲(chǔ)等提示錯(cuò)誤時(shí),即可生成 coredump 文件。
這里為了方便起見,不更改 coredump 文件的生成路徑,即生成的 coredump 文件會(huì)與可執(zhí)行程序在同一目錄下,文件默認(rèn)叫 core 的文件。
下面以一段測(cè)試代碼說明一下,Linux 環(huán)境下 使用 gdb 調(diào)試器對(duì) coredump 文件進(jìn)行分析調(diào)試。
代碼如下:
#include <stdio.h> #include <string.h> #include <unistd.h> int fun() { printf("---fun()\n"); char buffer[3] = {0}; memcpy(buffer, "Hello", 6); printf("---buffer: %s\n", buffer); return 0; } int main() { printf("---main()---\n"); int pid = 0; pid = getpid(); printf("pid %d\n", pid); fun(); printf("---End main\n"); return 0; }
1. 編譯運(yùn)行
操作如下:
編譯代碼。輸入 gcc -g main.c -o main.out 命令
運(yùn)行程序。輸入 ./main.out 命令。
如下所示:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ ./main.out ---main()--- pid 3814 ---fun() ---buffer: Hello *** stack smashing detected ***: terminated 已放棄 (核心已轉(zhuǎn)儲(chǔ))
當(dāng)程序出現(xiàn)段錯(cuò)誤時(shí)崩潰時(shí),可查看程序當(dāng)前目錄下是否生成 coredump 相關(guān)文件。
如下所示:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ ls -l 總用量 180 -rw------- 1 wangtian wangtian 393216 12月 13 16:55 core drwxrwxr-x 2 wangtian wangtian 4096 11月 25 22:24 debug -rw-rw-r-- 1 wangtian wangtian 90 10月 28 13:32 debug.c -rw-rw-r-- 1 wangtian wangtian 81 10月 20 18:12 debug.h -rw-rw-r-- 1 wangtian wangtian 5688 12月 13 16:55 debug.o -rw-rw-r-- 1 wangtian wangtian 356 12月 13 16:55 main.c -rw-rw-r-- 1 wangtian wangtian 7144 12月 13 16:55 main.o -rwxrwxr-x 1 wangtian wangtian 21144 12月 13 16:55 main.out
2. gdb 調(diào)試 coredump 文件
查看 coredump 文件。
輸入 objdump -t core 命令:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ objdump -t core core: 文件格式 elf64-x86-64 SYMBOL TABLE: 無符號(hào)
可以看到,coredump 文件是不帶符號(hào)表的。
所以調(diào)試時(shí),需要同時(shí)加上 可執(zhí)行程序文件。
輸入命令 gdb main.out core 進(jìn)行調(diào)試,如下所示:
wangtian@wangtian-virtual-machine:~/Code_Learns/C_Learns/debug/debug2$ gdb main.out core For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from main.out... [New LWP 4272] Core was generated by `./main.out'. Program terminated with signal SIGABRT, Aborted. #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 50 ../sysdeps/unix/sysv/linux/raise.c: 沒有那個(gè)文件或目錄.
可以看到,程序收到 signal SIGABRT 后崩潰了。
輸入 bt (backtrace 縮寫) 命令,可以查看下程序崩潰時(shí)的堆棧信息,
如下所示:
(gdb) bt #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 #1 0x00007ff8d6f5c859 in __GI_abort () at abort.c:79 #2 0x00007ff8d6fc729e in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ff8d70f108f "*** %s ***: terminated\n") at ../sysdeps/posix/libc_fatal.c:155 #3 0x00007ff8d7069aea in __GI___fortify_fail ( msg=msg@entry=0x7ff8d70f1077 "stack smashing detected") at fortify_fail.c:26 #4 0x00007ff8d7069ab6 in __stack_chk_fail () at stack_chk_fail.c:24 #5 0x000056183642c243 in fun () at main.c:12 #6 0x000056183642c28c in main () at main.c:20
以上的堆棧信息,顯示 __stack_chk_fail () 錯(cuò)誤。可以看到調(diào)用 fun() 函數(shù)過程中發(fā)生了棧問題。
__stack_chk_fail () 打印信息說明發(fā)生了緩沖區(qū)溢出,即數(shù)組越界問題。
從這段棧信息中并不能直觀的定位到出現(xiàn)問題的地方,因?yàn)槌霈F(xiàn)問題的地方是之前的數(shù)組越界,但是這里的棧信息已經(jīng)執(zhí)行到主函數(shù) main中, 特別是在大項(xiàng)目中更是無法準(zhǔn)確的定位出錯(cuò)的地方。
越界寫壞內(nèi)存,什么時(shí)候會(huì) crash 取決于我們寫壞的內(nèi)存什么時(shí)候被用到,有時(shí)候會(huì)離犯罪現(xiàn)場(chǎng)特別遠(yuǎn)。所以,導(dǎo)致定位這樣情況的崩潰問題比較麻煩。
在發(fā)生__stack_chk_fail () 錯(cuò)誤的附近函數(shù) fun() 中第11 行打斷點(diǎn),如下所示:
(gdb) b main.c:11 Breakpoint 1 at 0x56183642c22a: file main.c, line 11. (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x000056183642c22a in fun at main.c:11 (gdb) r Starting program: /home/wangtian/Code_Learns/C_Learns/debug/debug2/main.out ---main()--- pid 3868 ---fun() ---buffer: Hello Breakpoint 1, fun () at main.c:11 11 return 0; (gdb) n 12 } (gdb) *** stack smashing detected ***: terminated Program received signal SIGABRT, Aborted. __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 50 ../sysdeps/unix/sysv/linux/raise.c: 沒有那個(gè)文件或目錄. (gdb)
初步可以定位到,是在函數(shù) fun() 內(nèi)部發(fā)生了數(shù)組越界問題。目前還不能定位到具體某一行。
canary 棧溢出保護(hù)機(jī)制的具體原理:
- gcc 編譯器默認(rèn)開啟了 canary 棧保護(hù)機(jī)制。
- canray 棧保護(hù)機(jī)制是專門針對(duì)棧溢出攻擊涉及的一中保護(hù)機(jī)制。
- 由于棧溢出攻擊的主要目標(biāo)是通過溢出覆蓋函數(shù)棧高位的返回地址,因此,其思路是在函數(shù)開始執(zhí)行前,即返回地址前寫入一個(gè)字長(zhǎng)的隨機(jī)數(shù)據(jù)(canary),在函數(shù)返回前校驗(yàn)該值是否被改變,如果改變則認(rèn)為是棧溢出,程序直接終止,以此來防止信息泄露。
總結(jié)
以上一系列的操作,說明通過 gdb 對(duì) 數(shù)據(jù)越界問題的調(diào)試定位,這種方法不是太好用。
原因:越界寫壞內(nèi)存,什么時(shí)候會(huì) crash 取決于我們寫壞的內(nèi)存什么時(shí)候被用到,有時(shí)候會(huì)離犯罪現(xiàn)場(chǎng)特別遠(yuǎn)。所以,導(dǎo)致定位這樣情況的崩潰問題比較麻煩。后續(xù)文章再針對(duì)數(shù)組越界問題定位方法進(jìn)行新的總結(jié)。
好了,以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
linux下apache開啟url重寫的方法(詳細(xì)說明)
Apache 2.x 中URL重寫,是通過mod_rewrite.so 來實(shí)現(xiàn)的,所以您要查看您的Apache 是否已經(jīng)被編譯進(jìn)去這個(gè)模塊了,并且在Apache的配置文件httpd.conf 中已經(jīng)調(diào)用了這個(gè)模塊2012-04-04Apache?POI操作批量導(dǎo)入MySQL數(shù)據(jù)庫(kù)
本文主要介紹了Apache?POI操作批量導(dǎo)入MySQL數(shù)據(jù)庫(kù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06WAMPserver配置方法(允許外部訪問、phpmyadmin設(shè)置為輸入用戶名密碼才可登錄等)
這篇文章主要介紹了WAMPserver配置(允許外部訪問、phpmyadmin設(shè)置為輸入用戶名密碼才可登錄等),需要的朋友可以參考下2015-02-02