linux/unix 段错误捕获_转

时间:2022-11-06 08:35:06
本文为“在C/C++中捕获段错误,打印出错的具体位置”的续篇,进一步解决涉及动态链接库的情况。
 
背景知识:
·linux/unix下动态链接库的基本原理
·/proc/pid/maps文件的基本格式
·动态链接库:在进程执行过程中动态加载,进程间可以共享代码,可用在发布升级包等场合
 
概述:
    用户自己编写的代码均编译进了可执行文件里的时候,“在C/C++中捕获段错误,打印出错的具体位置”里给出了在发生段错误(或其他错误,读者可以修改附件里面的头文件,增加捕获的错误类型)的情况下,输出代码执行路径的方法。本文在此基础上,分析了当用户编写的部分代码不在可执行文件中时,如何获取代码执行路径。
    为简洁起见,后文用“原方法”指代前一文章内的分析方法。
 
正文:
先给出本文示例代码(linux/unix 段错误捕获_转 segvCatch_ext.rar
命令行下执行的命令行次序如下:
[root@redhat tcpBreak]# g++ -fPIC -shared -g -o libtest.so lib.cpp 
[root@redhat tcpBreak]# g++ -g test.cpp ./libtest.so
[root@redhat tcpBreak]# ./a.out
[root@redhat tcpBreak]# addr2line...(省略)
 
一、出错代码在动态链接库内时,原方法的输出
 
    有些情况下,我们会采用动态链接库,如果出错代码行恰巧在动态链接库内,原方法仍可得到出错时的地址。例如:
  1. signal[8] catched when running code at 8048ab3
  2. signal[8] catched when running code at 4001771b
  3. signal[8] catched when running code at 400176fd
    此例中,调用addr2line小工具的输出为
  1. [root@redhat tcpBreak]# addr2line 8048ab3 4001771b 400176fd -s -C -f -e a.out
  2. main
  3. test.cpp:15
  4. ??
  5. ??:0
  6. ??
  7. ??:0
    显然,后面两个地址翻译不出来了,因为其实出错代码根本不在可执行文件 a.out 内,而是位于一个动态链接库内。
 
二、动态链接库的偏移地址
 
     动态链接库无非就是编译后的代码,里面有一些基本的段、符号信息。如果出错代码行在动态链接库内,那必然可以从动态链接库内找到出错时的代码行号。
     好吧,那就让我们试一下:
  1. [root@redhat tcpBreak]# addr2line 4001771b 400176fd -s -C -f -e libtest.so
  2. ??
  3. ??:0
  4. ??
  5. ??:0
    还是翻译不出来。当然出不来了,因为进程挂掉时输出的地址,和动态链接库文件内的静态偏移地址根本就不是一回事。所以我们需要知道出错时,所输出的代码地址与动态链接库偏移地址之间的关系。
    事实上,每一个进程都对应了一个 /proc/pid 目录,下面记载了诸多与该进程相关的信息,其中有一个maps文件,里面记录了各个动态链接库的加载地址。我们只需要根据所得到的出错地址,以及这个maps文件,就可以得出具体是哪一个库,相应的偏移地址是多少。本文用例产生的输出为:
  1. -------------------------- 进程挂掉时的MAPS文件 --------------------------
  2. 08048000-08049000 r-xp 00000000 00:09 17256 /mnt/hgfs/share/net/tcpBreak/a.out
  3. 08049000-0804a000 rw-p 00001000 00:09 17256 /mnt/hgfs/share/net/tcpBreak/a.out
  4. 0804a000-0804b000 rwxp 00000000 00:00 0
  5. 40000000-40015000 r-xp 00000000 08:02 271023 /lib/ld-2.3.2.so
  6. 40015000-40016000 rw-p 00014000 08:02 271023 /lib/ld-2.3.2.so
  7. 40016000-40017000 rw-p 00000000 00:00 0
  8. 40017000-40018000 r-xp 00000000 00:09 17255 /mnt/hgfs/share/net/tcpBreak/libtest.so
  9. 40018000-40019000 rw-p 00000000 00:09 17255 /mnt/hgfs/share/net/tcpBreak/libtest.so
  10. 40019000-4001b000 rw-p 00000000 00:00 0
  11. 40026000-400cf000 r-xp 00000000 08:02 350892 /usr/lib/libstdc++.so.5.0.3
  12. 400cf000-400d4000 rw-p 000a9000 08:02 350892 /usr/lib/libstdc++.so.5.0.3
  13. 400d4000-400d9000 rw-p 00000000 00:00 0
  14. 400d9000-400fa000 r-xp 00000000 08:02 286922 /lib/tls/libm-2.3.2.so
  15. 400fa000-400fb000 rw-p 00020000 08:02 286922 /lib/tls/libm-2.3.2.so
  16. 400fb000-40102000 r-xp 00000000 08:02 271272 /lib/libgcc_s-3.2.2-20030225.so.1
  17. 40102000-40103000 rw-p 00007000 08:02 271272 /lib/libgcc_s-3.2.2-20030225.so.1
  18. 40103000-40104000 rw-p 00000000 00:00 0
  19. 42000000-4212e000 r-xp 00000000 08:02 286920 /lib/tls/libc-2.3.2.so
  20. 4212e000-42131000 rw-p 0012e000 08:02 286920 /lib/tls/libc-2.3.2.so
  21. 42131000-42133000 rw-p 00000000 00:00 0
  22. bfffd000-c0000000 rwxp ffffe000 00:00 0
  23. -------------------------------------------------------------------------
  24. --------------------------- 进程挂掉时的栈帧 --------------------------
  25. signal[8] catched when running code at 8048ab3
  26. signal[8] catched when running code at 4001771b
  27. signal[8] catched when running code at 400176fd
  28. -------------------------------------------------------------------------
    显然 4001771b 400176fd 对应的库是 libtest.so,偏移地址分别为 71b 6fd。
 
三、临门一脚
 
    知道了对应的动态链接库和偏移地址后,我们进一步用 addr2line 将这个偏移地址翻译一下就可以了。
  1. [root@redhat tcpBreak]# addr2line 71b 6fd -s -C -f -e libtest.so
  2. a()
  3. lib.cpp:14
  4. b()
  5. lib.cpp:10
    至此,大功告成。
 
四、简而言之
 
    不管是否有用到动态链接库,我们将原方法得到的输出,结合进程挂掉时maps文件的内容,就可以得到代码出错时的执行路径。根据代码所在部分,指定相应的文件给 addr2line 的 -e 参数即可。对于上面那个例子:
  1. [root@redhat tcpBreak]# addr2line 8048ab3 -s -C -f -e a.out
  2. main
  3. test.cpp:15
  4. [root@redhat tcpBreak]# addr2line 71b 6fd -s -C -f -e libtest.so
  5. a()
  6. lib.cpp:14
  7. b()
  8. lib.cpp:10
    本文发布的捕获出错执行路径的方法:
        1 在含有main函数的那个源码文件里,包含segvCatch_ext.h这个头文件
        2 具体如何解析出错时代码的执行路径,阅读segvCatch_ext.h头部的说明
    适用场景已经在前一篇文章里面描述过了,有问题可以给我发邮件(fireworks2@foxmail.com)。
 
五、似有余味
 
    一个程序启动后,地址是如何进行映射的,MAPS文件是怎么生成的,库又是怎么加载的,自行编写动态链接库时,有什么注意事项...
    这些问题我也不甚明了,因为我自己也没深究过,以后有时间可能会陆续补到博客里面。
 
参考资料:
[1] Linux debug : addr2line追踪出错地址, http://www.linuxidc.com/Linux/2011-05/35780.htm
[2] addr2line,可以根据一个地址打印出对应的代码行, http://archive.cnblogs.com/a/1996110/
[3] Linux下 /proc/maps 文件分析,http://bbs.chinaunix.net/viewthread.php?tid=2000825
[4] 《程序员的自我修养—链接、装载与库》,俞甲子,石凡,潘爱民. (PS 此书甚好,推荐大家阅读)

linux/unix 段错误捕获_转的更多相关文章

  1. linux/unix 段错误捕获【续】

    本文为“在C/C++中捕获段错误,打印出错的具体位置”的续篇,进一步解决涉及动态链接库的情况.   背景知识: ·linux/unix下动态链接库的基本原理 ·/proc/pid/maps文件的基本格 ...

  2. Linux的段错误调试方法

    linux段错误的调试方法 相关博文: http://blog.csdn.net/htianlong/article/details/7439030 http://www.cnblogs.com/pa ...

  3. Linux 下段错误 core文件

    什么是core dump? core的意思是内存,dump的意思是扔出来,堆出来:当一个程序奔溃时,在进程当前工作目录的core文件中复制了该进程的存储图像.core文件仅仅是一个内存映像(同时加上调 ...

  4. golang 程序 在linux 出现 段错误

    刚做的 golang 程序 发布到linux 竟然出现 段错误 原因是 内核版本过低,请升级内核

  5. Linux下段错误(C语言)

    问题描述:在Linux下编程有时会出现段错误的提醒,出现这种错误有可能是因为以下几种原因 1.数组越界:如果在初始化或者接收输入时内容超过了定义好的数组元素个数时会出现段错误,Linux的数组越界检查 ...

  6. gdb调试段错误及使用

    在编程调试中,经常出现段错误,此时可用gdb调试.具体方法为注册段错误信号处理函数,在处理函数中启动gdb.具体代码如下: void segv_handler(int no) { ]; ]; FILE ...

  7. C/C++捕获段错误,打印出错的具体位置(精确到哪一行)_转

    转自:C/C++捕获段错误,打印出错的具体位置(精确到哪一行) 修订:2013-02-16 其实还可以使用 glibc 的 backtrace_symbols 函数,把栈帧各返回地址里面的数字地址翻译 ...

  8. C/C++捕获段错误,打印出错的具体位置(精确到哪一行)

    修订:2013-02-16 其实还可以使用 glibc 的 backtrace_symbols 函数,把栈帧各返回地址里面的数字地址翻译成符号描述的 修订:2011-06-11 背景知识: · 在li ...

  9. linux Segmentation faults 段错误详解

    什么是段错误 下面是来自 Answers.com 的定义: A segmentation fault (often shortened to segfault) is a particular err ...

随机推荐

  1. ACM: FZU 2112 Tickets - 欧拉回路 - 并查集

     FZU 2112 Tickets Time Limit:3000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u P ...

  2. Microsoft Office Project 相关教程 收集

    Project教程 如何建立任务间链接 Project教程:[10]如何将项目插入主项目 如何有效使用Project(1)——编制进度计划.保存基准 如何有效使用Project(2)——进度计划的执行 ...

  3. 【leetcode】Text Justification

    Text Justification Given an array of words and a length L, format the text such that each line has e ...

  4. Android ViewPager实现选项卡切换

    ViewPager实现选项卡切换,效果图如下: 步骤一:这里使用兼容低版本的v4包进行布局,这里eclipse没有输入提示,所以要手动输入,文件名称“activity_main.xml” <Re ...

  5. linux tar文件解压

    把常用的tar解压命令总结下,当作备忘: tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个, ...

  6. 百度,人人,新浪,腾讯等分享Js代码

    <!-- Baidu Button BEGIN -->                        <div id="bdshare" class=" ...

  7. iOS开发证书和配置文件的使用

    提示:之前只是根据网上的流程完成了iOS项目的发布过程,对于证书和配置文件仍是一头雾水,这里参照别人的文章,简单了解一下,毕竟以后用的着. 这里有几个问题: 什么是iOS证书,它是如何与app应用关联 ...

  8. Framework类库(FCL)简介

    Framework类库(Framework Class Library,FCL)是一组DLL程序集的统称,其中含有数千个类型定义,每个类型都公开了一些功能 部分常规的FCL命名空间 命名空间 内容说明 ...

  9. 转载:SQL Server编程基本语法

    一.定义变量 --简单赋值 declare @a int print @a --使用select语句赋值 ) select @user1='张三' print @user1 ) print @user ...

  10. Kotlin Reference &lpar;七&rpar; Returns and Jumps

    most from reference kotlin有三个结构跳跃表达式 return 默认情况下,从最近的封闭函数或匿名函数返回. break 跳出整个循环 continue 跳出本次循环,进行下一 ...