关注、星标公众号,直达精彩内容

1965792e62dd711617da4b9693fa586b.png

来源:https://blog.csdn.net/qq_42519524/article/details/119608102

作者:文曲尽墨琛乃缺

 
 

一.段错误产生的原因

  1. 段错误就是访问了不可访问的内存,出现了运行时出现了segmentation fault的报错

  2. 产生的原因:访问不存在的内存地址、访问系统保护的内存地址 、访问只读的内存地址、空指针废弃(eg:malloc与free释放后,继续使用)、堆栈溢出、内存越界(数组越界,变量类型不一致等)

二. 使用GDB逐步查找段错误

  1. 首先加上命令行得先加上-g -rdynamic的参数进行编译,eg:gcc -g rdynamic xxx.c 随着gdb ./a.out

  2. eg:6be85b29e487c1ad435bca3c227ffc9c.png

  3. 运行结果42ec01818a30c0bd6d5de5458be0cad7.png很明显,都不用一步步的调试,后面几行就显示了出错位置。
    并且进程还收到了SIGSEGV信号而结束,而SIGSEGV默认的handler的动作是打印“段错误”的出错信息,并产生core文件。

三. 分析core文件

  1. 什么是core文件
    Core文件其实就是内存的映像,当程序崩溃时,存储内存的相应信息,主用用于对程序进行调试。 当程序崩溃时便会产生core文件,其实准确的应该说是core dump 文件,默认生成位置与可执行程序位于同一目录下,文件名为core.
    Core的意思是内存, Dump的意思是扔出来;core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump.

  2. 如何使用

gdb -c core文件路径 [应用程序的路径]
1

进去后输入where回车, 就可以显示程序在哪一行当掉的, 在哪个函数中。
但是core文件的生成跟你当前系统的环境设置有关系, 可以用下面的语句设置一下, 然后再运行程序便成生成core文件ulimit -c ulimited; 输入命令

ulimit -c   //查是否为0,是0就不生成core文件
ulimit -c 1000  //ulimit -c ulimited,改变数值,限制系统的core文件大小
12
  1. eg 510c564c87dd48f9dc203d9271a151f5.png

四.段错误时启动调试

(gdb)btab5886f18b393859c0e3ed60ff2c13ea.png二,三,四都是在基于系统上的gdb的前提进行的。如果没有,glibc为我们提供了此类能够dump栈内容的函数簇,详见/usr/include/execinfo.h

五. 利用backtrace和objdump进行分析

eg5a2c6aa135d338e0becd95b8e7e370f8.png0b9a1d5372bb33c008711c8c66c6660f.png

  1. 运行结果5013b3ecdef1cffb7e1c8eacf7814302.png

  2. 这里得提一下,需要用到库<execinfo.h>和<signal.h>

  3. 运行结果似乎没上面几种方式的信息多,清晰;但是还没完,我们再用objdump反汇编程序,找到上面地址对应的代码位置。

  4. objdump -d a.out找到对应main 0x5601a75e0b0f对应的代码位置303839deb81e4638b053bffad0130ac3.png

  5. backtrace函数
    backtrace函数的作用:当程序出现异常,段错误,崩溃的情况下,会收到内核发送给进程的异常信号,会把程序的堆栈信息打印出来。

int backtrace(void **buffer, int size)
 //该函数获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针数组,参数size用来指定buffer中可以保存多少个void*元素。函数的返回值是实际返回的void*元素个数。buffer中的void*元素实际是从堆栈中获取的返回地址。
 char **backtrace_symbols(void *const *buffer, int size)
 //该函数将backtrace函数获取的信息转化为一个字符串数组,参数buffer是backtrace获取的堆栈指针,size是backtrace返回值。函数返回值是一个指向字符串数组的指针,它包含char*元素个数为size。每个字符串包含了一个相对于buffer中对应元素的可打印信息,包括函数名、函数偏移地址和实际返回地址。
 //backtrace_symbols生成的字符串占用的内存是malloc出来的,但是是一次性malloc出来的,释放是只需要一次性释放返回的二级指针即可。
 void backtrace_symbols_fd(void *const *buffer, int size, int fd)
 //该函数与backtrace_symbols函数功能相同,只是它不会malloc内存,而是将结果写入文件描述符为fd的文件中,每个函数对应一行;该函数可重入。
1234567

注意事项:backtrace_symbols的实现需要符号名称的支持,在gcc编译过程中需要加入-rdynamic参数;

  1. objdump反汇编码

objdump -f test   //显示test的文件头信息

objdump -d test   //反汇编test中的需要执行指令的那些section

objdump -D test   //与-d类似,但反汇编test中的所有section

objdump -h test   //显示test的Section Header信息

objdump -x test   //显示test的全部Header信息

objdump -s test   //除了显示test的全部Header信息,还显示他们对应的十六进制文件代码
1234567891011

六.段错误信息的获取

当我们运行的时候,发先有段错误segmentation fault时,我们可以通过一些命令进行段错误信息获取。

  1. -g gcc -g 主要适用于gdb调试

  2. dmesg 直接输入命令dmesgdmesg 可以在应用程序崩溃时,显示内存中保存的相关信息。 dmesg 可以查看发生段错误的程序名称、引起段错误发生的内存地址、指令指针地址、堆栈指针地址、错误代码、错误原因等等。

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

版权声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐