段错误(segmentation fault)总结及调试

本文参考以下文章整理而成,向作者致谢。
http://www.diybl.com/course/6_system/linux/Linuxjs/200875/130421.html
http://blog.chinaunix.net/space.php?uid=317451&do=blog&id=92412&page=3#comment

1.错误总结:

1.1 向受到系统保护的内存地址写数据
有些内存是内核占用的或者是其他程序正在使用,为了保证系统正常工作,所以会受到系统的保护,而不能任意访问.(例1,例2)
1.2 内存越界(数组越界,变量类型不一致等)
如例3中的数组访问越界或例4中的将一个整数按照字符串的方式输出。

习惯性检查以下情形,有利于避免段错误:
<1>定义了指针后记得初始化,在使用的时候记得判断是否为NULL
<2>在使用数组的时候是否被初始化,数组下标是否越界,数组元素是否存在等
<3>在变量处理的时候变量的格式控制是否合理等

2.错误调试:
2.1 向受到系统保护的内存地址写数据
例1.
#include <stdio.h>  
int main()  
{  
int i = 0;  
scanf ("%d", i); /* should have used &i */ 
printf ("%d\n", i);  
return 0;  
}
编译运行并调试
segmentation-fault$ gcc -g -o segerr segerr.c
segmentation-fault$ gdb ./segerr
GNU gdb 6.4-debian
Copyright 2005 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 “i486-linux-gnu”…Using host libthread_db library “/ lib/tls/i686/cmov/libthread_db.so.1″.

(gdb) l –用l(list)显示我们的源代码
01 #include <stdio.h>  
02 int main()  
03 {  
04 int i = 0;  
05 scanf (”%d”, i); /* should have used &i */ 
06 printf (”%d\n”, i);  
07 return 0;
08 }

(gdb) b 5 –用b(break)设置断点
Breakpoint 1 at 0×80483b7: file segerr.c, line 8.

(gdb) p i –用p(print)打印变量i的值

$1 = 0

(gdb) r –用r(run)运行,直到断点处
Starting program: /home/falcon/temp/segerr
Breakpoint 1, main () at segerr.c:8
8 scanf (”%d”, i); /* should have used &i */ –[试图往地址0处写进一个值]

(gdb) n –用n(next)执行下一步
10
Program received signal SIGSEGV, Segmentation fault.
0xb7e9a1ca in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6

(gdb) c –在上面我们接收到了SIGSEGV,然后用c(continue)继续执行
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

(gdb) quit –退出gdb

错把&i写成了i
开始时初始化i为0,程序试图向内存地址0存放一个值!

[补充:
可以通过man 7 signal查看SIGSEGV的信息。
segmentation-fault$ man 7 signal | grep SEGV
Reformatting signal(7), please wait…
SIGSEGV 11 Core Invalid memory reference

例2:
#include <stdio.h>  
int main()  
{  
char *p;  
p = NULL;  
*p = 'x';  
printf("%c", *p);  
return 0;  
}
用gdb可知p的值为0x0,而*p = 'x'即位向内存的0地址处赋值。

2.1 内存越界,如数组越界,变量不一致等
例3
#include <stdio.h>  
int main()  
{  
char test[1];  
printf("%c", test[1000000000]);  
return 0;  
}
例4 以下几种访问格式的变化,均可导致segmentation fault.
#include <stdio.h>  
#include <string.h>
int main()  
{  
int b = 10;  
char c = 'c';
char buf[100];
printf("%s", c);
printf("%s\n", b);  
memset(buf, 0, 100);
sprintf(buf, "%s", c);//试图把char型按照字符串格式转换 
memset(buf, 0, 100);
sprintf(buf, "%s", b);//试图把int型按照字符串转换
return 0;  
}

3. segmentation fault调试方法:

3.1 条件编译输出程序关键输出信息
# ifdef DEBUG
printf ("程序关键信息");
# endif
使用-DDEBUG进行编译。

3.2 gdb调试。
关于gdb调试的更多举例:
例5
#include <stdio.h>
dummy_function (void)
{
  unsigned char *ptr = 0x00;
  // 产生错误的原因在于,定义指针变量ptr之后,将其初始化为0.
  *ptr = 0x00;
}
int main (void)
{
  dummy_function ();
  return 0;
}
gdb调试后,发现错误行。进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man 7 signal),知道SIGSEGV默认handler的动作是打印”段错误"的出错信息,并产生Core文件。

3.3 通过分析core file定位段错误
core file 作用及设置请参考:http://blog.csdn.net/ispcfs/article/details/6845753
segmentation-fault$ gdb ./a.out core
GNU gdb 6.8-debian
Copyright (C) 2008 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 "x86_64-linux-gnu"...

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-x86-64.so.2...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 16740]
#0  0x0000000000400458 in dummy_function () at segmentation-fault-sample5.c:5
5         *ptr = 0x00;
(gdb)
可见,通过corefile可以定位段错误行。

此外,还可以通过修改SIGSEGV的handler函数和backtrace/objdump进行调试。

Logo

更多推荐