转载 linux/unix 段错误捕获【续】

来源:互联网 发布:ssh测试端口命令 编辑:程序博客网 时间:2024/06/04 14:26

原文地址 http://blog.chinaunix.net/uid-26009500-id-1585783.html


一、出错代码在动态链接库内时,原方法的输出
 
    有些情况下,我们会采用动态链接库,如果出错代码行恰巧在动态链接库内,原方法仍可得到出错时的地址。例如:
  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文件是怎么生成的,库又是怎么加载的,自行编写动态链接库时,有什么注意事项...
    这些问题我也不甚明了,因为我自己也没深究过,以后有时间可能会陆续补到博客里面。

原创粉丝点击