Linux下程序出现段错误的调试解决方法
今天程序出现了如下的一条错误:
testrouter[17281]: segfault at 13a4 ip 0000003c0ac0920b sp 00007f1ebdd64bc0 error 4 in libc-2.15.so[3c0ac00000+20000]
查看错误类别是段错误,并且给出了堆栈指针指向的位置。产生段错误的原因一般有以下几点:
1、内存访问越界
2、多线程程序使用了线程不安全的函数。
应该使用下面这些可重入的函数:
asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n) ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c) getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c) fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c) getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3) getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n) nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3) getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c) getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c) getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)
3、多线程读写的数据未加锁保护。
对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump
4、非法指针
5、堆栈溢出
不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。
Linux系统默认的情况下,是不产生段错误文件,可以通过下面的命令来查看系统默认段错误文件的大小:
ulimit -c
一般显示的结果是0。可以通过以下两条命令设置段错误文件大小为2048字节和大小不受限制:
ulimit -c 2048
ulimit -c unlimited
注意以上在终端中输入只是暂时的,若要永久生效需要把以上命令之一加入到/etc/profile或者/文件中。
core文件的作用如下:
当我们的程序崩溃时,内核有可能把该程序当前内存映射到core文件里,方便程序员找到程序出现问题的地方。最常出现的,几乎所有C程序员都出现过的错误就是“段错误”。当一个程序崩溃时,在进程当前工作目录的core文件中复制了该进程的存储镜像。core文件仅仅是一个内存映象(同时加上调试信息),主要是用来调试的。Linux/Unix收到下面的信号时候会产生core文件:
SIGABRT 异常终止(abort) 终止w/core
SIGBUS 硬件故障 终止w/core
SIGEMT 硬件故障 终止w/core
SIGFPE 算术异常 终止w/core
SIGILL 非法硬件指令 终止w/core
SIGIOT 硬件故障 终止w/core
SIGQUIT 终端退出符 终止w/core
SIGSEGV 无效存储访问 终止w/core
SIGSYS 无效系统调用 终止w/core
SIGTRAP 硬件故障 终止w/core
SIGXCPU 超过CPU限制(setrlimit) 终止w/core
SIGXFSZ 超过文件长度限制(setrlimit) 终止w/core
#include<stdio.h>
int main()
{
char *ptr="test";
strcpy(ptr,"TEST");
return 0;
}
gcc –g test.c -o core_test
编译完成后,运行程序./core_test,程序会中断,产生“段错误”这个提示。会看到当前目录下会出现一个core.15649的文件,下面我们利用这个文件进行错误的查找,利用调试工具gdb实现:
gdb ./core_test
显示出如下的信息:
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/cyl/openwrt/switch/switch/test_core...done.
(gdb)
调试的时候,我们在gdb下执行run
(gdb) run
Starting program: /home/cyl/openwrt/switch/switch/test_core
Program received signal SIGSEGV, Segmentation fault.
0x00762f16 in __memcpy_ssse3 () from /lib/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6.i686
从此信息中可以看出,
收到SIGSEGV信号,触发段错误,并提示地址0x00762f16、调用 _memcpy_ssse3()报的错,位于/lib/libc.so.6库中。
objdump -d ./test_core > segfault3Dump
然后我们查看segfault3Dump,查看产生段错误信息的位置。
test_core[15649]: segfault at 80484c4 ip 00762f16 sp bfc9d7b8 error 7 in libc-2.12.so[62e000+191000]
然后正则匹配发生段错误的地方:
grep -n -A 10 -B 10 "80484c4" ./segfault3Dump
结果如下:
[direwolf@direwolf switch]$ grep -n -A 10 -B 10 "80484c4" ./segfault3Dump
121- 80483b9: c7 04 24 80 95 04 08 movl $0x8049580,(%esp)
122- 80483c0: ff d0 call *%eax
123- 80483c2: c9 leave
124- 80483c3: c3 ret
125-
126-080483c4 <main>:
127- 80483c4: 55 push %ebp
128- 80483c5: 89 e5 mov %esp,%ebp
129- 80483c7: 83 e4 f0 and $0xfffffff0,%esp
130- 80483ca: 83 ec 20 sub $0x20,%esp
131: 80483cd: c7 44 24 1c c4 84 04 movl $0x80484c4,0x1c(%esp)
132- 80483d4: 08
133- 80483d5: c7 44 24 08 05 00 00 movl $0x5,0x8(%esp)
134- 80483dc: 00
135- 80483dd: c7 44 24 04 c9 84 04 movl $0x80484c9,0x4(%esp)
136- 80483e4: 08
137- 80483e5: 8b 44 24 1c mov 0x1c(%esp),%eax
138- 80483e9: 89 04 24 mov %eax,(%esp)
139- 80483ec: e8 03 ff ff ff call 80482f4 <memcpy@plt>
140- 80483f1: b8 00 00 00 00 mov $0x0,%eax
141- 80483f6: c9
可以看到段错误发生在main函数内,查看汇编代码可以知道,
对应的汇编指令是movl $0x80484c4,0x1c
(%esp)。接下来对照c语言的源代码,我们使用下面的命令进行反汇编,先生成可调试文件,再反汇编。
gcc -g test_core.c
objdump -S a.out
可以得到下面的信息,我们这里仅仅找到main函数的入口:
080483c4 <main>:
************************************************************************/
#include<stdio.h>
int main()
{
80483c4: 55 push %ebp
80483c5: 89 e5 mov %esp,%ebp
80483c7: 83 e4 f0 and $0xfffffff0,%esp
80483ca: 83 ec 20 sub $0x20,%esp
char *ptr="test";
80483cd: c7 44 24 1c c4 84 04 movl $0x80484c4,0x1c(%esp)
80483d4: 08
strcpy(ptr,"TEST");
80483d5: c7 44 24 08 05 00 00 movl $0x5,0x8(%esp)
80483dc: 00
80483dd: c7 44 24 04 c9 84 04 movl $0x80484c9,0x4(%esp)
80483e4: 08
80483e5: 8b 44 24 1c mov 0x1c(%esp),%eax
80483e9: 89 04 24 mov %eax,(%esp)
80483ec: e8 03 ff ff ff call 80482f4 <memcpy@plt>
return 0;
80483f1: b8 00 00 00 00 mov $0x0,%eax
}
找到
movl $0x80484c4,0x1c
(%esp)这条汇编指令,可以知道,程序是在调用strcpy的时候发生错误!
1、出现段错误时,首先应该想到段错误的定义,从它出发考虑引发错误的原因。
2、在使用指针时,定义了指针后记得初始化指针,在使用的时候记得判断是否为NULL。
3、在使用数组时,注意数组是否被初始化,数组下标是否越界,数组元素是否存在等。
4、在访问变量时,注意变量所占地址空间是否已经被程序释放掉。
5、在处理变量时,注意变量的格式控制是否合理等。
更多推荐
所有评论(0)