linux/unix 段错误捕获【续】

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

相关推荐
JZC_xiaozhong3 小时前
数据不互通、审批慢?企业多系统智能协同与流程自动化解决方案
运维·自动化·流程管理·流程自动化·数据集成与应用集成·流程监控·流程可视化设计
爱学习的小囧3 小时前
ESXi 8.0 原生支持 NVMe 固态硬盘吗?VMD 配置详解教程
linux·运维·服务器·esxi·esxi8.0
大鹏说大话3 小时前
SSL证书自动化的未来:ACME协议与Let’s Encrypt实践
网络·安全
坚持就完事了3 小时前
Linux中的变量
linux·运维·服务器
hERS EOUS3 小时前
nginx 代理 redis
运维·redis·nginx
handler013 小时前
从源码到二进制:深度拆解 Linux 下 C 程序的编译与链接全流程
linux·c语言·开发语言·c++·笔记·学习
Cat_Rocky4 小时前
利用Packet Tracer网络实验
linux·运维·服务器
被摘下的星星4 小时前
网际协议(IP协议)
网络·tcp/ip
嵌入式×边缘AI:打怪升级日志4 小时前
Linux 驱动实战:SR501 人体红外传感器驱动开发与调试全记录
linux·运维·驱动开发
正点原子4 小时前
【正点原子Linux连载】第三章 U-Boot使用 摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南
linux·运维·驱动开发