Linux下core文件的演示分析

一)core文件

一般是当程序崩溃时,内核把该程序当前的内存映射到core文件里,例如当程序出现段错误,内核会发送SIGSEGV信号给程序,使程序中断,并把该程序的内存写入到core文件.
所以core文件中只是程序的内存映像,如果在编译时加入调试信息的话,那么还会有调试信息.

下面我们讨论一下core相关的话题:

1)ulimit

ulimit -c 可以设置core的文件,并能控制是否产生core文件,如果设置为0的话,将不产生core文件,如果设置的大小小于core文件,则对core文件截取.
所以如果要调试core文件,一定要打开这个限制,如下:

查看core文件的限制,此时为0,即不成生core文件
ulimit -c 
0

打开core文件的限制,不限制core文件的大小,使程序可以产生core文件
ulimit -c unlimited
ulimit -c 
unlimited

2)core文件的名称和生成路径 

/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展.文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.PID,为0则表示生成的core文件统一命名为core.
如下:
查看core_uses_pid 默认为1,即在core文件名后加入PID
cat /proc/sys/kernel/core_uses_pid 
1

设定只产生core的文件名
echo "0" > /proc/sys/kernel/core_uses_pid

查看当前目录,此时没有core文件
ls
test  test.c

执行./test程序,产生core文件.
./test 
*** glibc detected *** ./test: double free or corruption (fasttop): 0x09f29008 ***
======= Backtrace: =========
/lib/libc.so.6[0xbd3f7d]
/lib/libc.so.6(cfree+0x90)[0xbd75d0]
./test[0x80483dc]
/lib/libc.so.6(__libc_start_main+0xdc)[0xb83dec]
./test[0x8048301]
======= Memory map: ========
00430000-00431000 r-xp 00430000 00:00 0          [vdso]
00b51000-00b6a000 r-xp 00000000 08:01 3704501    /lib/ld-2.5.so
00b6a000-00b6b000 r-xp 00018000 08:01 3704501    /lib/ld-2.5.so
00b6b000-00b6c000 rwxp 00019000 08:01 3704501    /lib/ld-2.5.so
00b6e000-00ca5000 r-xp 00000000 08:01 3704502    /lib/libc-2.5.so
00ca5000-00ca7000 r-xp 00137000 08:01 3704502    /lib/libc-2.5.so
00ca7000-00ca8000 rwxp 00139000 08:01 3704502    /lib/libc-2.5.so
00ca8000-00cab000 rwxp 00ca8000 00:00 0 
00dab000-00db6000 r-xp 00000000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
00db6000-00db7000 rwxp 0000a000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
08048000-08049000 r-xp 00000000 08:01 327681     /root/test
08049000-0804a000 rw-p 00000000 08:01 327681     /root/test
09f29000-09f4a000 rw-p 09f29000 00:00 0 
b7e00000-b7e21000 rw-p b7e00000 00:00 0 
b7e21000-b7f00000 ---p b7e21000 00:00 0 
b7f0c000-b7f0d000 rw-p b7f0c000 00:00 0 
b7f21000-b7f22000 rw-p b7f21000 00:00 0 
bfd58000-bfd6d000 rw-p bfd58000 00:00 0          [stack]
Aborted (core dumped)

再次查看,系统中已经产生core文件,且文件名为core
ls
core  test  test.c

/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式,如下:
echo "/tmp/core-%e-%u-%s" > /proc/sys/kernel/core_pattern

%e表示添加命令名
%u表示添加当前uid
%s表示添加导致产生core的信号 

我们运行上面的小程序,如下:
./test 
*** glibc detected *** ./test: double free or corruption (fasttop): 0x09dbe008 ***
======= Backtrace: =========
/lib/libc.so.6[0xbd3f7d]
/lib/libc.so.6(cfree+0x90)[0xbd75d0]
./test[0x80483dc]
/lib/libc.so.6(__libc_start_main+0xdc)[0xb83dec]
./test[0x8048301]
======= Memory map: ========
004e3000-004e4000 r-xp 004e3000 00:00 0          [vdso]
00b51000-00b6a000 r-xp 00000000 08:01 3704501    /lib/ld-2.5.so
00b6a000-00b6b000 r-xp 00018000 08:01 3704501    /lib/ld-2.5.so
00b6b000-00b6c000 rwxp 00019000 08:01 3704501    /lib/ld-2.5.so
00b6e000-00ca5000 r-xp 00000000 08:01 3704502    /lib/libc-2.5.so
00ca5000-00ca7000 r-xp 00137000 08:01 3704502    /lib/libc-2.5.so
00ca7000-00ca8000 rwxp 00139000 08:01 3704502    /lib/libc-2.5.so
00ca8000-00cab000 rwxp 00ca8000 00:00 0 
00dab000-00db6000 r-xp 00000000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
00db6000-00db7000 rwxp 0000a000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
08048000-08049000 r-xp 00000000 08:01 327681     /root/test
08049000-0804a000 rw-p 00000000 08:01 327681     /root/test
09dbe000-09ddf000 rw-p 09dbe000 00:00 0 
b7e00000-b7e21000 rw-p b7e00000 00:00 0 
b7e21000-b7f00000 ---p b7e21000 00:00 0 
b7f77000-b7f78000 rw-p b7f77000 00:00 0 
b7f8c000-b7f8d000 rw-p b7f8c000 00:00 0 
bf8cc000-bf8e2000 rw-p bf8cc000 00:00 0          [stack]
Aborted (core dumped)

ls -l /tmp
total 212
-rw------- 1 root root 413696 Apr 10 10:51 core-test-0-6.2663
注:test为程序名,0代表我们是用root执行的程序,6代表我们收到的是SIGABRT(6)信号

下面我们将core文件名格式调整为加入时间,PID,主机名,如下:
echo "/tmp/core-%t-%p-%h" > /proc/sys/kernel/core_pattern

%t添加core文件生成时的unix时间
%p添加pid
%h添加主机名

再次运行test程序,输出略.

再次查看core文件
ls -l /tmp
total 372
-rw------- 1 root root 413696 Apr 10 10:52 core-1302447166-2677-test1

注:其中的1302447166为UNIX时间.2677为PID,test1为主机名,我们注意到此时没有core_uses_pid的PID,说明我们加入了PID的设定后,系统屏蔽了core_user_pid的设定.


3)setuid与core文件

如果一个程序设定了setuid,那么普通用户在默认情况下是无法生成core文件的,只有更改/proc/sys/fs/suid_dumpable才可以,如下:

查看suid_dumpable,默认为0,即不产生core文件.
cat /proc/sys/fs/suid_dumpable
0

为下面的试验顺利,我们将core_pattern的格式下做调整,加入用户名,如下:
echo "/tmp/core-%e-%u-%s" > /proc/sys/kernel/core_pattern

查看/tmp/test(溢出程序)的权限,并加入setuid权限,如下:
ls -l /tmp/test
-rwxr-xr-x 1 root root 4811 Apr 10 10:41 /tmp/test
chmod +s /tmp/test 
ls -l /tmp/test    
-rwsr-sr-x 1 root root 4811 Apr 10 10:41 /tmp/test

用普通用户运行test程序,如下:
su - test
./test 
*** glibc detected *** ./test: double free or corruption (fasttop): 0x08279008 ***
======= Backtrace: =========
/lib/libc.so.6[0x175f7d]
/lib/libc.so.6(cfree+0x90)[0x1795d0]
./test[0x80483dc]
/lib/libc.so.6(__libc_start_main+0xdc)[0x125dec]
./test[0x8048301]
======= Memory map: ========
00110000-00247000 r-xp 00000000 08:01 3704502    /lib/libc-2.5.so
00247000-00249000 r-xp 00137000 08:01 3704502    /lib/libc-2.5.so
00249000-0024a000 rwxp 00139000 08:01 3704502    /lib/libc-2.5.so
0024a000-0024d000 rwxp 0024a000 00:00 0 
00b51000-00b6a000 r-xp 00000000 08:01 3704501    /lib/ld-2.5.so
00b6a000-00b6b000 r-xp 00018000 08:01 3704501    /lib/ld-2.5.so
00b6b000-00b6c000 rwxp 00019000 08:01 3704501    /lib/ld-2.5.so
00bc3000-00bc4000 r-xp 00bc3000 00:00 0          [vdso]
00dab000-00db6000 r-xp 00000000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
00db6000-00db7000 rwxp 0000a000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
08048000-08049000 r-xp 00000000 08:01 327681     /tmp/test
08049000-0804a000 rw-p 00000000 08:01 327681     /tmp/test
08279000-0829a000 rw-p 08279000 00:00 0 
b7e00000-b7e21000 rw-p b7e00000 00:00 0 
b7e21000-b7f00000 ---p b7e21000 00:00 0 
b7fd0000-b7fd1000 rw-p b7fd0000 00:00 0 
b7fe5000-b7fe6000 rw-p b7fe5000 00:00 0 
bfcb0000-bfcc5000 rw-p bfcb0000 00:00 0          [stack]
Aborted

确认没有产生core文件
ls
ssh-jioUnx2695  ssh-psRqMA2559  ssh-TbaOaU2593  test

我们切换到root用户将suid_dumpable设定为1,如下:
echo 1 > /proc/sys/fs/suid_dumpable 

再次运行程序,发现已经产生了core文件,如下:
./test 
*** glibc detected *** ./test: double free or corruption (fasttop): 0x0816a008 ***
======= Backtrace: =========
/lib/libc.so.6[0xbd3f7d]
/lib/libc.so.6(cfree+0x90)[0xbd75d0]
./test[0x80483dc]
/lib/libc.so.6(__libc_start_main+0xdc)[0xb83dec]
./test[0x8048301]
======= Memory map: ========
0022f000-00230000 r-xp 0022f000 00:00 0          [vdso]
00b51000-00b6a000 r-xp 00000000 08:01 3704501    /lib/ld-2.5.so
00b6a000-00b6b000 r-xp 00018000 08:01 3704501    /lib/ld-2.5.so
00b6b000-00b6c000 rwxp 00019000 08:01 3704501    /lib/ld-2.5.so
00b6e000-00ca5000 r-xp 00000000 08:01 3704502    /lib/libc-2.5.so
00ca5000-00ca7000 r-xp 00137000 08:01 3704502    /lib/libc-2.5.so
00ca7000-00ca8000 rwxp 00139000 08:01 3704502    /lib/libc-2.5.so
00ca8000-00cab000 rwxp 00ca8000 00:00 0 
00dab000-00db6000 r-xp 00000000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
00db6000-00db7000 rwxp 0000a000 08:01 3704511    /lib/libgcc_s-4.1.1-20070105.so.1
08048000-08049000 r-xp 00000000 08:01 327681     /tmp/test
08049000-0804a000 rw-p 00000000 08:01 327681     /tmp/test
0816a000-0818b000 rw-p 0816a000 00:00 0 
b7e00000-b7e21000 rw-p b7e00000 00:00 0 
b7e21000-b7f00000 ---p b7e21000 00:00 0 
b7fcb000-b7fcc000 rw-p b7fcb000 00:00 0 
b7fe0000-b7fe1000 rw-p b7fe0000 00:00 0 
bf983000-bf999000 rw-p bf983000 00:00 0          [stack]
Aborted (core dumped)

ls
core-test-500-6.2945  ssh-jioUnx2695  ssh-psRqMA2559  ssh-TbaOaU2593  test



4)强制生成core文件

下面有两种生成core文件的技巧:

第一种是发送信号给进程,如下:
kill -s SIGSEGV $$

注:此时会生成一个core文件,但这将会杀掉自己当前的进程,使自己要重启bash.


第二种方法是用gcore,如下:
gcore $$
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
0x003aa402 in __kernel_vsyscall ()
Saved corefile core.2561



二)创建自己的黑匣子

在软件开发的过程中,可以在应用程序中建立一个黑匣子,使用它代替调试器有如下的好处:
1)它将为用户提供一份详细的控制日志.这样的话,用户即可以保存一些有用的调试信息,又可以不损失性能.
2)当试图调试堆栈溢出的时候,尤其有用,回想典型的堆栈溢出,它会导致跟踪信息无效,使调试几乎无用.


我们用下面的程序来演示创建黑匣子的过程,如下:

#include
#include
#include

char tracebuf[4096] = "";
char *mstart = tracebuf;

int dbgprintf(const char *fmt, ...)
        __attribute__((__format__(__printf__, 1, 2)));

int dbgprintf(const char *fmt, ...)
{
        int n = 0;

        va_list ap;
        va_start(ap, fmt);

        int nchars = sizeof(tracebuf) - (mstart - tracebuf);

        if (nchars <= 2){
                mstart = tracebuf;
                nchars = sizeof(tracebuf);
        }

        n = vsnprintf(mstart, nchars, fmt, ap);
        mstart += n +1;

        va_end(ap);
        return n;
}

int defective(int x)
{
        int y = 1;
        dbgprintf("defective(%u)", x);
        if (x == 10){
                dbgprintf("time to corrupt the stack!");

                memset(&y, 0xa5, sizeof(y) + 128);
                dbgprintf("I'm still here; returning now.");
                return 0;
        }
        return defective(x+1);
}

int main (int argc, char *argv[])
{
        defective(1);
        dbgprintf("exiting...");
        return 0;
}

编译:
gcc -g trace-buffer.c -o trace-buffer

注:本程序通过递归处理,当达到第10次时将导致内存溢出,而这些被dbgprintf写入到全局的内存块中,再此之前我们也通过dbgprintf函数将调试信息写入到全局的内存块中.

运行程序:
./trace-buffer
Segmentation fault (core dumped)

下面用gdb对本次溢出进行调试,如下:
gdb ./trace-buffer core.2627 
GNU gdb Red Hat Linux (6.5-16.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./trace-buffer'.
Program terminated with signal 11, Segmentation fault.                        /*我们打开core,发现当时是出现了段错误*/
#0  0x0804847d in defective (x=Cannot access memory at address 0xa5a5a5ad
) at trace-buffer.c:45
45      }
(gdb) bt                                                                      /*用bt命令打印输出堆栈信息*/
#0  0x0804847d in defective (x=Cannot access memory at address 0xa5a5a5ad
) at trace-buffer.c:45
Cannot access memory at address 0xa5a5a5a9
(gdb) x/15s &tracebuf                                                         /*用x命令输出内存地址tracebuf的信息*/
0x8049720 :    "defective(1)"
0x804972d :         "defective(2)"
0x804973a :         "defective(3)"
0x8049747 :         "defective(4)"
0x8049754 :         "defective(5)"
0x8049761 :         "defective(6)"
0x804976e :         "defective(7)"
0x804977b :         "defective(8)"
0x8049788 :        "defective(9)"
0x8049795 :        "defective(10)"
0x80497a3 :        "time to corrupt the stack!"
0x80497be :        "I'm still here; returning now."
0x80497dd :        ""
0x80497de :        ""
0x80497df :        ""
(gdb) quit

注:我们看到用bt命令只看到了段错误的信息,而通过黑匣子看到了我们整个程序的运行过程,包括程序出错溢出时所发生的一切.




三)获取运行时的堆栈轨迹(gstack)

获得程序堆栈轨迹(bactrace)最简单的方法是使用gdb.这里的缺点是它依附于正在执行的进程,需要调试器将进程停下来,输入一些命令,然后再让进程继续运行.如果常常需要这么做,反复中断执行的程序会造成大量的时间浪费.
gstack的优点是其所消耗的执行时间少,而且是非交互式的.
下面是gstack应用的一个例子:

终端1)
top 

终端2)
gstack `pgrep top`
#0  0x004eb402 in __kernel_vsyscall ()
#1  0x00c33f7d in ___newselect_nocancel () from /lib/libc.so.6
#2  0x08051187 in ?? ()
#3  0x00b83dec in __libc_start_main () from /lib/libc.so.6
#4  0x08049741 in ?? ()

你可能感兴趣的:(linux)