“段错误”定位及调试的一点经验

38 篇文章 1 订阅
6 篇文章 0 订阅

今天调试代码的时候,遇到一个问题就是出现了“段错误”。出现“段错误”的原因就是:访问的内存超出了系统给这个程序所设定的内存空间。知道原因是一个很好的开始,但是并不代表就很容易解决,特别是在代码量较大的情况下,如何才能定位到出错的地方?接下来,我就大概讲一下自己的一点经验,如何在Linux C中用几个命令搞定“段错误”。

1、dmesg

通过dmesg命令可以查看发生段错误的程序名称、引起段错误发生的内存地址、指令指针地址、堆栈指针地址、错误代码、错误原因等。

如,运行dmesg命令后,

[91046.776582] Test[26966]: segfault at 4 ip 0804f57f sp bfa0a224 error 6 in Test[8048000+1e000]

可以看出,发生段错误的地址:4, 和指令指针地址:0804f57f


2、ldd

使用ldd命令查看二进制程序的共享链接库依赖,包括库的名称、起始地址,这样可以确定段错误到底是发生在了自己的程序中还是依赖的共享库中。

如,运行 ldd bin/Test

	linux-gate.so.1 =>  (0xb77d7000)
	libevent-2.0.so.5 => /usr/lib/libevent-2.0.so.5 (0xb777d000)
	libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7764000)
	librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0xb775a000)
	libxml2.so.2 => /usr/lib/libxml2.so.2 (0xb762f000)
	libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb74d5000)
	/lib/ld-linux.so.2 (0xb77d8000)
	libdl.so.2 => /lib/tls/i686/cmov/libdl.so.2 (0xb74d1000)
	libz.so.1 => /lib/libz.so.1 (0xb74bc000)
	libm.so.6 => /lib/tls/i686/cmov/libm.so.6 (0xb7495000)

通过以上信息可以排除“段错误”发生在共享链接库的可能。

3、nm

使用nm命令列出二进制文件中的符号表,包括符号地址、符号类型、符号名等,这样可以帮助定位在哪里发生了段错误。

如,执行 nm bin/Test | grep 0804f5

0804f560 T nwGtpv1uMsgAddIeTV1
0804f5a0 T nwGtpv1uMsgAddIeTV2
0804f5e0 T nwGtpv1uMsgAddIeTV4
0804f500 T nwGtpv1uMsgGetTpduHandle
0804f530 T nwGtpv1uMsgGetTpduLength

在步骤1的时候,发生段错误的指令指针地址:0804f57f。结合以上信息,可以定位出错误的发生应该是在执行 nwGtpv1uMsgAddIeTV1 函数的时候。

4、objdump

objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,使用objdump生成二进制的相关信息,并重定向到文件中

如,执行 objdump -d bin/Test > testDump

接下来,需要在 testDump 文件中查找发生段错误的地址

如,执行 grep -n -A 10 -B 10 "0804f57f" ./testDump

3327:0804f560 <nwGtpv1uMsgAddIeTV1>:
3328- 804f560:	83 ec 08             	sub    $0x8,%esp
3329- 804f563:	8b 44 24 0c          	mov    0xc(%esp),%eax
3330- 804f567:	89 34 24             	mov    %esi,(%esp)
3331- 804f56a:	8b 74 24 10          	mov    0x10(%esp),%esi
3332- 804f56e:	89 7c 24 04          	mov    %edi,0x4(%esp)
3333- 804f572:	8b 7c 24 14          	mov    0x14(%esp),%edi
3334- 804f576:	0f b7 50 0c          	movzwl 0xc(%eax),%edx     //装入的32位寄存器 edx 前16位
3335- 804f57a:	03 50 18             	add    0x18(%eax),%edx
3336- 804f57d:	89 f1                	mov    %esi,%ecx
3337- 804f57f:	88 0a                	mov    %cl,(%edx)

可以发现,段错误就发生的位置:在 nwGtpv1uMsgAddIeTV1 函数中,执行汇编命令

3337- 804f57f:	88 0a                	mov    %cl,(%edx)

找到对应的 nwGtpv1uMsgAddIeTV1 函数源码:

411 NwGtpv1uRcT            
412 nwGtpv1uMsgAddIeTV1(NW_IN NwGtpv1uMsgHandleT hMsg,
413                     NW_IN uint8_t       type,
414                     NW_IN uint8_t       value)
415 {
416   NwGtpv1uMsgT *pMsg = (NwGtpv1uMsgT *) hMsg;
417   NwGtpv1uIeTv1T *pIe;
418   
419   
420   pIe = (NwGtpv1uIeTv1T *) (pMsg->msgBuf + pMsg->msgLen);
421   pIe->t        = type;
422   pIe->v        = value;
423   
424   pMsg->msgLen += sizeof(NwGtpv1uIeTv1T);
425
426   return NW_GTPV1U_OK;
427 }

很明显,其实就是指针pIe出现的问题。

5、fprintf

加上打印语句,看看变量值究竟是个什么鬼。

411 NwGtpv1uRcT            
412 nwGtpv1uMsgAddIeTV1(NW_IN NwGtpv1uMsgHandleT hMsg,
413                     NW_IN uint8_t       type,
414                     NW_IN uint8_t       value)
415 {
416   NwGtpv1uMsgT *pMsg = (NwGtpv1uMsgT *) hMsg;
417   NwGtpv1uIeTv1T *pIe;
418   fprintf(stderr,"pMsg->msgBuf:%d \n",pMsg->msgBuf);//0
419   fprintf(stderr,"pMsg->msgLen:%d \n",pMsg->msgLen);//4
420   pIe = (NwGtpv1uIeTv1T *) (pMsg->msgBuf + pMsg->msgLen);
421   pIe->t        = type;
422   pIe->v        = value;
423   
424   pMsg->msgLen += sizeof(NwGtpv1uIeTv1T);
425
426   return NW_GTPV1U_OK;
427 }

6、gdb

使用gdb工具来对程序进行调试

如,执行 gdb  --args ./bin/Test 2 127.0.0.1 127.0.0.1

加断点

(gdb) b NwGtpv1uMsg.c:418
(gdb) r
(gdb) s

调试信息发现,pMsg->msgBuf的值为0,pMsg->msgLen的值为4,很明显:指针pIe访问的地址就出问题了!!

再往下调试就会发现

Program received signal SIGSEGV, Segmentation fault.
nwGtpv1uMsgAddIeTV1 (hMsg=539354136, type=14 '\016', value=0 '\000') at /home/zlj/openair4G/openair-cn/GTPV1-U/nw-gtpv1u/src/NwGtpv1uMsg.c:424
424	  pIe->t        = type;
(gdb) 

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

段错误产生了!!


总结

在Linux环境下使用C做项目,“段错误”的出现比较常见,首先就是要知道段错误发生的原因,然后快速准确地找到错误位置,结合代码分析问题,最后干掉bug。

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值