📄 linux下的段错误产生的原因及调试方法.htm
字号:
<TBODY>
<TR>
<TD> 1 dummy_function
(void)<BR> 2 {<BR>
3 unsigned char *ptr
= 0x00;<BR>
4 *ptr =
0x00;<BR> 5 }<BR>
6<BR> 7 int main
(void)<BR> 8 {<BR>
9 dummy_function
();<BR> 10<BR>
11 return
0;<BR> 12
}<BR></TD></TR></TBODY></TABLE>作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。我们尝试编译运行它:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>xiaosuo@gentux test $
./a.out<BR>段错误<BR></TD></TR></TBODY></TABLE>果然不出所料,它出错并退出了。<BR><SPAN
style="FONT-WEIGHT: bold">1.利用gdb逐步查找段错误:</SPAN><BR>这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上“-g
-rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>xiaosuo@gentux test $ gcc -g -rdynamic d.c<BR>xiaosuo@gentux test $
gdb ./a.out<BR>GNU gdb 6.5<BR>Copyright (C) 2006 Free Software Foundation,
Inc.<BR>GDB is free software, covered by the GNU General Public License,
and you are<BR>welcome to change it and/or distribute copies of it under
certain conditions.<BR>Type "show copying" to see the conditions.<BR>There
is absolutely no warranty for GDB. Type "show warranty" for
details.<BR>This GDB was configured as "i686-pc-linux-gnu"...Using host
libthread_db library "/lib/libthread_db.so.1".<BR><BR>(gdb) r<BR>Starting
program: /home/xiaosuo/test/a.out<BR><BR>Program received signal SIGSEGV,
Segmentation fault.<BR>0x08048524 in dummy_function () at
d.c:4<BR>4
*ptr =
0x00;<BR>(gdb)
<BR></TD></TR></TBODY></TABLE>哦?!好像不用一步步调试我们就找到了出错位置d.c文件的第4行,其实就是如此的简单。<BR>从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man
7 signal),我们知道SIGSEGV默认handler的动作是打印”段错误"的出错信息,并产生Core文件,由此我们又产生了方法二。<BR><SPAN
style="FONT-WEIGHT: bold">2.分析Core文件:</SPAN><BR>Core文件是什么呢?<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>The default action of certain signals is to cause a process to
terminate and produce a core dump file, a disk file containing an image of
the process's memory at the time of termination. A list of the
signals which cause a process to dump core can be found in
signal(7).</TD></TR></TBODY></TABLE>以 上资料摘自man page(man 5
core)。不过奇怪了,我的系统上并没有找到core文件。后来,忆起为了渐少系统上的拉圾文件的数量(本人有些洁癖,这也是我喜欢Gentoo的原因
之一),禁止了core文件的生成,查看了以下果真如此,将系统的core文件的大小限制在512K大小,再试:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>xiaosuo@gentux test $ ulimit -c<BR>0<BR>xiaosuo@gentux test $ ulimit
-c 1000<BR>xiaosuo@gentux test $ ulimit -c<BR>1000<BR>xiaosuo@gentux test
$ ./a.out<BR>段错误 (core dumped)<BR>xiaosuo@gentux test $ ls<BR>a.out
core d.c f.c g.c pango.c test_iconv.c
test_regex.c<BR></TD></TR></TBODY></TABLE>core文件终于产生了,用gdb调试一下看看吧:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>xiaosuo@gentux test $ gdb ./a.out core<BR>GNU gdb 6.5<BR>Copyright (C)
2006 Free Software Foundation, Inc.<BR>GDB is free software, covered by
the GNU General Public License, and you are<BR>welcome to change it and/or
distribute copies of it under certain conditions.<BR>Type "show copying"
to see the conditions.<BR>There is absolutely no warranty for GDB.
Type "show warranty" for details.<BR>This GDB was configured as
"i686-pc-linux-gnu"...Using host libthread_db library
"/lib/libthread_db.so.1".<BR><BR><BR>warning: Can't read pathname for load
map: 输入/输出错误.<BR>Reading symbols from /lib/libc.so.6...done.<BR>Loaded
symbols for /lib/libc.so.6<BR>Reading symbols from
/lib/ld-linux.so.2...done.<BR>Loaded symbols for
/lib/ld-linux.so.2<BR>Core was generated by `./a.out'.<BR>Program
terminated with signal 11, Segmentation fault.<BR>#0 0x08048524 in
dummy_function () at
d.c:4<BR>4
*ptr =
0x00;<BR></TD></TR></TBODY></TABLE>哇,好历害,还是一步就定位到了错误所在地,佩服一下Linux/Unix系统的此类设计。<BR>接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现“运行时错误”,这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。<BR>Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了:<BR><SPAN
style="FONT-WEIGHT: bold">3.段错误时启动调试:</SPAN><BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>#include <stdio.h><BR>#include <stdlib.h><BR>#include
<signal.h><BR>#include <string.h><BR><BR>void dump(int
signo)<BR>{<BR> char
buf[1024];<BR> char
cmd[1024];<BR> FILE
*fh;<BR><BR> snprintf(buf,
sizeof(buf), "/proc/%d/cmdline",
getpid());<BR> if(!(fh =
fopen(buf,
"r")))<BR>
exit(0);<BR> if(!fgets(buf,
sizeof(buf),
fh))<BR>
exit(0);<BR>
fclose(fh);<BR>
if(buf[strlen(buf) - 1] ==
'\n')<BR>
buf[strlen(buf) - 1] = '\0';<BR>
snprintf(cmd, sizeof(cmd), "gdb %s %d", buf,
getpid());<BR>
system(cmd);<BR><BR>
exit(0);<BR>}<BR><BR>
void<BR>dummy_function
(void)<BR>{<BR> unsigned char
*ptr = 0x00;<BR> *ptr =
0x00;<BR>}<BR><BR> int<BR>main
(void)<BR>{<BR> signal(SIGSEGV,
&dump);<BR> dummy_function
();<BR><BR> return
0;<BR>}<BR></TD></TR></TBODY></TABLE>编译运行效果如下:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>xiaosuo@gentux test $ gcc -g -rdynamic f.c<BR>xiaosuo@gentux test $
./a.out<BR>GNU gdb 6.5<BR>Copyright (C) 2006 Free Software Foundation,
Inc.<BR>GDB is free software, covered by the GNU General Public License,
and you are<BR>welcome to change it and/or distribute copies of it under
certain conditions.<BR>Type "show copying" to see the conditions.<BR>There
is absolutely no warranty for GDB. Type "show warranty" for
details.<BR>This GDB was configured as "i686-pc-linux-gnu"...Using host
libthread_db library "/lib/libthread_db.so.1".<BR><BR>Attaching to
program: /home/xiaosuo/test/a.out, process 9563<BR>Reading symbols from
/lib/libc.so.6...done.<BR>Loaded symbols for /lib/libc.so.6<BR>Reading
symbols from /lib/ld-linux.so.2...done.<BR>Loaded symbols for
/lib/ld-linux.so.2<BR>0xffffe410 in __kernel_vsyscall ()<BR>(gdb)
bt<BR>#0 0xffffe410 in __kernel_vsyscall ()<BR>#1 0xb7ee4b53
in waitpid () from /lib/libc.so.6<BR>#2 0xb7e925c9 in strtold_l ()
from /lib/libc.so.6<BR>#3 0x08048830 in dump (signo=11) at
f.c:22<BR>#4 <signal handler called><BR>#5 0x0804884c in
dummy_function () at f.c:31<BR>#6 0x08048886 in main () at
f.c:38<BR></TD></TR></TBODY></TABLE>怎么样?是不是依旧很酷?<BR>以上方法都是在系统上有gdb的前提下进行的,如果没有呢?其实glibc为我们提供了此类能够dump栈内容的函数簇,详见/usr/include/execinfo.h(这些函数都没有提供man
page,难怪我们找不到),另外你也可以通过<A
href="http://www.gnu.org/software/libc/manual/html_node/Backtraces.html"
target=_blank>gnu的手册</A>进行学习。<BR><SPAN
style="FONT-WEIGHT: bold">4.利用backtrace和objdump进行分析:</SPAN><BR>重写的代码如下:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>#include <execinfo.h><BR>#include <stdio.h><BR>#include
<stdlib.h><BR>#include <signal.h><BR><BR>/* A dummy function
to make the backtrace more interesting.
*/<BR> void<BR>dummy_function
(void)<BR>{<BR> unsigned char
*ptr = 0x00;<BR> *ptr =
0x00;<BR>}<BR><BR>void dump(int
signo)<BR>{<BR> void
*array[10];<BR> size_t
size;<BR> char
**strings;<BR> size_t
i;<BR><BR> size = backtrace
(array, 10);<BR> strings =
backtrace_symbols (array,
size);<BR><BR> printf ("Obtained
%zd stack frames.\n",
size);<BR><BR> for (i = 0; i
< size;
i++)<BR>
printf ("%s\n",
strings[i]);<BR><BR> free
(strings);<BR><BR>
exit(0);<BR>}<BR><BR>
int<BR>main (void)<BR>{<BR>
signal(SIGSEGV, &dump);<BR>
dummy_function ();<BR><BR>
return 0;<BR>}<BR></TD></TR></TBODY></TABLE>编译运行结果如下:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>xiaosuo@gentux test $ gcc -g -rdynamic g.c<BR>xiaosuo@gentux test $
./a.out<BR>Obtained 5 stack frames.<BR>./a.out(dump+0x19)
[0x80486c2]<BR>[0xffffe420]<BR>./a.out(main+0x35)
[0x804876f]<BR>/lib/libc.so.6(__libc_start_main+0xe6)
[0xb7e02866]<BR>./a.out
[0x8048601]<BR></TD></TR></TBODY></TABLE>这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:<BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD>xiaosuo@gentux test $ objdump -d a.out<BR></TD></TR></TBODY></TABLE><BR>
<TABLE
style="BORDER-RIGHT: rgb(153,153,153) 1px solid; BORDER-TOP: rgb(153,153,153) 1px solid; FONT-SIZE: 12px; BORDER-LEFT: rgb(153,153,153) 1px solid; WIDTH: 80%; BORDER-BOTTOM: rgb(153,153,153) 1px solid"
align=center>
<TBODY>
<TR>
<TD> 8048765: e8 02 fe ff
ff call
804856c
<signal@plt><BR> 804876a:
e8 25 ff ff ff
call 8048694 <dummy_function><BR> <SPAN
style="COLOR: rgb(255,1,2)">804876f</SPAN>:
b8 00 00 00 00
mov
$0x0,%eax<BR> 8048774:
c9
leave<BR></TD></TR></TBODY></TABLE>我们还是找到了在哪个函数(dummy_function)中出错的,信息已然不是很完整,不过有总比没有好的啊!<BR><SPAN
style="FONT-WEIGHT: bold">后记:</SPAN><BR>本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱</P><BR><BR>
<P id=TBPingURL>Trackback:
http://tb.blog.csdn.net/TrackBack.aspx?PostId=1478650</P><BR></DIV>
<DIV class=postFoot>
<SCRIPT src=""></SCRIPT>
[<A title=功能强大的网络收藏夹,一秒钟操作就可以轻松实现保存带来的价值、分享带来的快乐
href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(saveit=window.open('http://wz.csdn.net/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes'));saveit.focus();">收藏到我的网摘</A>]
baizhiwen_2005发表于 2007年01月10日 08:51:00 </DIV></DIV><LINK
href="http://blog.csdn.net/baizhiwen_2005/Services/Pingback.aspx" rel=pingback><!--<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"xmlns:dc="http://purl.org/dc/elements/1.1/"xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"><rdf:Descriptionrdf:about="http://blog.csdn.net/baizhiwen_2005/archive/2007/01/10/1478650.aspx"dc:identifier="http://blog.csdn.net/baizhiwen_2005/archive/2007/01/10/1478650.aspx"dc:title="Linux下的段错误产生的原因及调试方法"trackback:ping="http://tb.blog.csdn.net/TrackBack.aspx?PostId=1478650" /></rdf:RDF>-->
<SCRIPT>function hide(){showComment();}</SCRIPT>
<BR>
<DIV class=post>
<DIV class=postTitle>相关文章:</DIV>
<UL class=postText>
<LI><A href="http://blog.csdn.net/absurd/archive/2006/02/28/612612.aspx"
target=_blank>Linux下的调试工具</A> 2006-02-28 <A
href="http://blog.csdn.net/absurd/" target=_blank>absurd</A>
<LI><A href="http://blog.csdn.net/zhenxizhou/archive/2003/03/15/17883.aspx"
target=_blank>关于VC代码的编写和调试(三)</A> 2003-03-15 <A
href="http://blog.csdn.net/zhenxizhou/" target=_blank>zhenxizhou</A>
<LI><A href="http://blog.csdn.net/zcatlinux/archive/2004/09/21/111652.aspx"
target=_blank>掌握 Linux 调试技术(转自:CU)</A> 2004-09-21 <A
href="http://blog.csdn.net/zcatlinux/" target=_blank>zcatlinux</A>
<LI><A href="http://blog.csdn.net/haoel/archive/2003/07/02/2879.aspx"
target=_blank>用GDB调试程序(一)</A> 2003-07-02 <A href="http://blog.csdn.net/haoel/"
target=_blank>haoel</A>
<LI><A href="http://blog.csdn.net/momodev/archive/2006/06/05/774497.aspx"
target=_blank>《PHP实战》第一章 程序调试 [连载中...]</A> 2006-06-05 <A
href="http://blog.csdn.net/momodev/"
target=_blank>momodev</A></LI></UL></DIV><BR><BR>
<DIV class=post id=csdn_zhaig_ad_yahoo></DIV>
<SCRIPT type=text/javascript>document.write("<img src=http://counter.csdn.net/pv.aspx?id=24 border=0 width=0 height=0>");</SCRIPT>
<SPAN class=PreAndNext id=viewpost.ascx_PreviousAndNextEntriesDown>
<DIV align=center> | <A
href="http://blog.csdn.net/baizhiwen_2005/archive/2007/01/09/1478120.aspx">掌握
Linux 调试技术 >></A></DIV></SPAN><SPAN
id=Anthem_Comments.ascx_ltlComments__><SPAN id=Comments.ascx_ltlComments><BR>
<DIV id=comments>
<H3></H3>没有评论。 </DIV></SPAN></SPAN>
<SCRIPT language=javascript>
ad_width=468;
ad_height=60;
adcss=2;
unionuser=19;
ad_type='j';
count=5;
</SCRIPT>
<SCRIPT language=javascript src="Linux下的段错误产生的原因及调试方法.files/showads.js"
type=text/javascript></SCRIPT>
<SCRIPT language=javascript src="Linux下的段错误产生的原因及调试方法.files/showgm.js"
type=text/javascript></SCRIPT>
<DIV class=moreinfo>
<DIV id=Anthem_PostComment.ascx_CommentUpdatePanel__>
<DIV id=PostComment.ascx_CommentUpdatePanel>
<TABLE class=comments id=CommentForm cellSpacing=1 cellPadding=1 border=0>
<TBODY>
<TR>
<TD width=75>大名</TD>
<TD><INPUT id=PostComment.ascx_tbName style="WIDTH: 300px" disabled
maxLength=32 size=40 name=PostComment.ascx:tbName></TD>
<TD><SPAN id=PostComment.ascx_RequiredFieldValidator2
style="VISIBILITY: hidden; COLOR: red" initialvalue=""
evaluationfunction="RequiredFieldValidatorEvaluateIsValid"
errormessage="请输入名字"
controltovalidate="PostComment.ascx_tbName">请输入名字</SPAN></TD></TR>
<TR>
<TD>网址</TD>
<TD><INPUT id=PostComment.ascx_tbUrl style="WIDTH: 300px" disabled
maxLength=256 size=40 name=PostComment.ascx:tbUrl></TD>
<TD></TD></TR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -