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

来源:互联网 发布:怎么给淘宝刷好评赚钱 编辑:程序博客网 时间:2024/05/29 11:31

今天调试代码的时候,遇到一个问题就是出现了“段错误”。出现“段错误”的原因就是:访问的内存超出了系统给这个程序所设定的内存空间。知道原因是一个很好的开始,但是并不代表就很容易解决,特别是在代码量较大的情况下,如何才能定位到出错的地方?接下来,我就大概讲一下自己的一点经验,如何在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 nwGtpv1uMsgAddIeTV10804f5a0 T nwGtpv1uMsgAddIeTV20804f5e0 T nwGtpv1uMsgAddIeTV40804f500 T nwGtpv1uMsgGetTpduHandle0804f530 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,%esp3329- 804f563:8b 44 24 0c          mov    0xc(%esp),%eax3330- 804f567:89 34 24             mov    %esi,(%esp)3331- 804f56a:8b 74 24 10          mov    0x10(%esp),%esi3332- 804f56e:89 7c 24 04          mov    %edi,0x4(%esp)3333- 804f572:8b 7c 24 14          mov    0x14(%esp),%edi3334- 804f576:0f b7 50 0c          movzwl 0xc(%eax),%edx     //装入的32位寄存器 edx 前16位3335- 804f57a:03 50 18             add    0x18(%eax),%edx3336- 804f57d:89 f1                mov    %esi,%ecx3337- 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);425426   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);//0419   fprintf(stderr,"pMsg->msgLen:%d \n",pMsg->msgLen);//4420   pIe = (NwGtpv1uIeTv1T *) (pMsg->msgBuf + pMsg->msgLen);421   pIe->t        = type;422   pIe->v        = value;423   424   pMsg->msgLen += sizeof(NwGtpv1uIeTv1T);425426   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:424424  pIe->t        = type;(gdb) Program terminated with signal SIGSEGV, Segmentation fault.The program no longer exists.

段错误产生了!!


总结

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

0 0
原创粉丝点击