讓程序崩潰時產生coredump
在 Windows 下我們已經習慣了用 Windbg 之類的工具調試 dump 文件,從而分析並排除程序運行時錯誤。在 Linux 下我們同樣可以完成類似的工作 —— Core Dump。
我們先看看相關的設置。
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 20
file size (blocks, -f) unlimited
pending signals (-i) 16382
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) unlimited
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
"core file size (blocks, -c) 0" 意味著在程序崩潰時不會生成 core dump 文件,我們需要修改一下設置。如果你和我一樣懶得修改配置文件,那麼就輸入下面這樣命令吧。
$ sudo sh -c "ulimit -c unlimited; ./test" # test 是可執行文件名。
等等…… 我們還是先準備個測試目標。
#include <stdio.h>
#include <stdlib.h>
void test()
{
char* s = "abc";
*s = 'x';
}
int main(int argc, char** argv)
{
test();
return (EXIT_SUCCESS);
}
很顯然,我們在 test() 裡面寫了一個不該寫的東東,這無疑會很嚴重。生成可執行文件後,執行上面的命令。
$ sudo sh -c "ulimit -c unlimited; ./test"
Segmentation fault (core dumped)
$ ls -l
total 96
-rw------- 1 root root 167936 2010-01-06 13:30 core
-rwxr-xr-x 1 yuhen yuhen 9166 2010-01-06 13:16 test
這個 core 文件就是被系統 dump 出來的,我們分析目標就是它了。
$ sudo gdb test core
GNU gdb (GDB) 7.0-ubuntu
Copyright (C) 2009 Free Software Foundation, Inc.
Reading symbols from .../dist/Debug/test...done.
warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/tls/i686/cmov/libpthread.so.0... ...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/tls/i686/cmov/libpthread.so.0
Reading symbols from /lib/tls/i686/cmov/libc.so.6... ...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2... ...done.
(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
#0 0x080483f4 in test () at main.c:16
warning: Source file is more recent than executable.
16 *s = 'x';
最後這幾行提示已經告訴我們錯誤的原因和代碼位置,接下來如何調試就是 gdb 的技巧了,可以先輸入 "where" 看看調用堆棧。
(gdb) where
#0 0x080483f4 in test () at main.c:16
#1 0x08048401 in main (argc=1, argv=0xbfd53e44) at main.c:22
(gdb) p s
$1 = 0x80484d0 "abc"
(gdb) info files
Symbols from ".../dist/Debug/test".
Local core dump file:
Local exec file:
`.../dist/Debug/test', file type elf32-i386.
Entry point: 0x8048330
0x08048134 - 0x08048147 is .interp
... ...
0x08048330 - 0x080484ac is .text
0x080484ac - 0x080484c8 is .fini
0x080484c8 - 0x080484d4 is .rodata
很顯然 "abc" 屬於 .rodata,嚴禁調戲。
附:如果你調試的是 Release (-O2) 版本,而且刪除(strip)了符號表,那還是老老實實數彙編代碼吧。可見用 Debug 版本試運行是很重要滴!!!