zoukankan      html  css  js  c++  java
  • linux下g++从异常中还原异常类型

    一、异常终止
    在C++中,如果有一个异常没有被任何人捕捉,此时默认的处理是将进程终止掉,终止的时候使用的信号是sigabrt。好在内核对于这种信号的默认处理是会生成一个coredump文件,对于一些服务器来说,通过core文件可以知道当时的进程信息,如果附带了调试信息,那么调用的堆栈信息清晰可见。
    理想总是丰满的,但是现实还是骨干的。有些时候这些文件的coredump文件对应的源文件已经不存在,或者即使存在,此时运行的版本和当前我们看到的版本已经不同,如果此时再继续使用源文件来分析这个问题,那就是刻舟求剑的最好注脚了。
    之前遇到的一个情况是出现异常的进程可执行文件的源代码大体结构还在,但是中间毕竟进行了多次调整,在异常调用链中throw异常的函数中,使用非常工整的排比式throw,也就是if xxx throw Exception(args) else if yyy throw Exception,这些异常的类型相同,但是结构的成员中包含了我们在最为关心的字符提示以及一些错误码提示问题。
    二、gcc对异常结构的基本接口及流程
    gcc对异常的处理分为两个步骤,一个是为抛出的异常对象之前建立一个内部使用的__cxa_exception结构,然后将这个结构和抛出的对象对象拼接成一个g++内部使用的结构,传递给__cxa_throw函数,从g++的throw函数实现来看,g++对于通过内部throw接口
    extern "C" void
    __cxxabiv1::__cxa_throw (void *obj, std::type_info *tinfo, 
                 void (*dest) (void *))
    抛出的异常,第一个参数必须是一个结构指针,这个指针指向的是抛出的对象实例,而在指针的连续的低地址位置,其中包含着一个异常处理头结构,也就是对我们透明的,但是底层实现中约定的统一接口__cxa_exception结构。这里可以推导出一个问题,throw的所有变量都是在堆栈上分配独立的地址空间,然后执行构造函数,之后再抛出异常;不论抛出的是全局变量,还是临时变量,新throw类型的空间都在堆栈上单独分配,分配了之后再执行构造函数,然后抛送给__cxa_throw函数执行。
    而这个通用的__cxa_exception结构则由调用_cxa_throw函数之前由编译器通过__cxa_allocate_exception申请,这个函数接受的是throw结构大小,然后它在throw的基础上加上exception的大小,偏移指针之后将地址返回给用户。这种方法是不是似曾相识呢?因为几乎所有的动态内存分配都是通过这种方法实现的,所有的malloc分配的空间都会在结构的开始加上结构的长度信息。

    再看一下throw函数的原型
    extern "C" void
    __cxxabiv1::__cxa_throw (void *obj, std::type_info *tinfo, 
                 void (*dest) (void *))
    第一个参数即throw的类型,第二个表示throw类型的动态类型识别信息,第三个表示该对象的析构函数地址,如果没有析构函数,最后一个参数可以为空。
    三、如何还原
    有了这些信息,可以从throw函数的参数中找到所有的信息,还原异常结构。也就是首先通过第二个参数找到对应的类型信息,然后将第一个参数转换为该类型的指针显示内存结构即可。
    1、演示代码
    [root@Harry throwtype]# cat throwtype.cpp 
    #include <stdio.h>
    #include <typeinfo>

    struct base 
    {
        int m_holder;
        int m_holder2;
        base():m_holder(0x12345678){}
    };
    struct derive : public base
    {
        virtual int vfun(){}
    };
    struct nvdctor: public base
    {
        ~nvdctor(){}
    };

    nvdctor gnvdctor;
    int baz()
    {
        throw gnvdctor;
    }

    int bar()
    {
        throw nvdctor();
    }
    int foo()
    {
        throw(derive());
    }
    int main()
    {
        baz();
        throw(base());
    }
    2、触发异常
    [root@Harry throwtype]# g++ throwtype.cpp -g
    [root@Harry throwtype]# ./a.out 
    terminate called after throwing an instance of 'nvdctor'
    Aborted (core dumped)
    [root@Harry throwtype]# gdb -c core_20199_6_0 a.out 
    GNU gdb (GDB) Fedora (7.0-3.fc12)
    Copyright (C) 2009 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "i686-redhat-linux-gnu".
    For bug reporting instructions, please see:
    <http://www.gnu.org/software/gdb/bugs/>...
    Reading symbols from /home/tsecer/CodeTest/throwtype/a.out...done.
    Reading symbols from /usr/lib/libstdc++.so.6...(no debugging symbols found)...done.
    Loaded symbols for /usr/lib/libstdc++.so.6
    Reading symbols from /lib/libm.so.6...(no debugging symbols found)...done.
    Loaded symbols for /lib/libm.so.6
    Reading symbols from /lib/libgcc_s.so.1...(no debugging symbols found)...done.
    Loaded symbols for /lib/libgcc_s.so.1
    Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
    Loaded symbols for /lib/libc.so.6
    Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
    Loaded symbols for /lib/ld-linux.so.2
    Core was generated by `./a.out'.
    Program terminated with signal 6, Aborted.
    #0  0x00a30424 in __kernel_vsyscall ()
    Missing separate debuginfos, use: debuginfo-install glibc-2.11.2-3.i686 libgcc-4.4.2-7.fc12.i686 libstdc++-4.4.2-7.fc12.i686
    (gdb) bt
    #0  0x00a30424 in __kernel_vsyscall ()
    #1  0x00234b91 in raise () from /lib/libc.so.6
    #2  0x0023646a in abort () from /lib/libc.so.6
    #3  0x054269ff in __gnu_cxx::__verbose_terminate_handler() ()
       from /usr/lib/libstdc++.so.6
    #4  0x054246f6 in ?? () from /usr/lib/libstdc++.so.6
    #5  0x05424733 in std::terminate() () from /usr/lib/libstdc++.so.6
    #6  0x05424872 in __cxa_throw () from /usr/lib/libstdc++.so.6
    #7  0x08048623 in baz () at throwtype.cpp:22
    #8  0x080486c1 in main () at throwtype.cpp:35
    (gdb) frame 6
    #6  0x05424872 in __cxa_throw () from /usr/lib/libstdc++.so.6
    (gdb) x/10x $ebp
    0xbfb7af28:    0xbfb7af48    0x08048623    0x0830b068    0x080488a0
    0xbfb7af38:    0x08048770    0x080484bc    0xbfb7af88    0x08049c60
    0xbfb7af48:    0xbfb7af78    0x080486c1
    (gdb) x/10x 0x080488a0
    0x80488a0 <_ZTI7nvdctor>:    0x08049ce8    0x08048894    0x0804888c    0x72656436
    0x80488b0 <_ZTS6derive+4>:    0x00657669    0x08049d28    0x080488ac    0x00000000
    0x80488c0 <_ZTI6derive+12>:    0x00000001    0x0804888c
    (gdb) shell c++filt _ZTI7nvdctor
    typeinfo for nvdctor
    (gdb) p *(nvdctor*) 0x0830b068
    $1 = {<base> = {m_holder = 305419896, m_holder2 = 0}, <No data fields>}
    (gdb) 
    3、对应反汇编代码
    有兴趣的同学可以看一下,所有需要的东西应该都有了,也省得大家自己写测试代码验证
    [root@Harry throwtype]# objdump -dr throwtype.o | c++filt

    throwtype.o:     file format elf32-i386


    Disassembly of section .text:

    00000000 <baz()>:
       0:    55                       push   %ebp
       1:    89 e5                    mov    %esp,%ebp
       3:    83 ec 18                 sub    $0x18,%esp
       6:    c7 04 24 08 00 00 00     movl   $0x8,(%esp)
       d:    e8 fc ff ff ff           call   e <baz()+0xe>
                e: R_386_PC32    __cxa_allocate_exception
      12:    89 c2                    mov    %eax,%edx
      14:    89 d0                    mov    %edx,%eax
      16:    8b 0d 00 00 00 00        mov    0x0,%ecx
                18: R_386_32    gnvdctor
      1c:    89 08                    mov    %ecx,(%eax)
      1e:    8b 0d 04 00 00 00        mov    0x4,%ecx
                20: R_386_32    gnvdctor
      24:    89 48 04                 mov    %ecx,0x4(%eax)
      27:    c7 44 24 08 00 00 00     movl   $0x0,0x8(%esp)
      2e:    00 
                2b: R_386_32    nvdctor::~nvdctor()
      2f:    c7 44 24 04 00 00 00     movl   $0x0,0x4(%esp)
      36:    00 
                33: R_386_32    typeinfo for nvdctor
      37:    89 14 24                 mov    %edx,(%esp)
      3a:    e8 fc ff ff ff           call   3b <baz()+0x3b>
                3b: R_386_PC32    __cxa_throw

    0000003f <bar()>:
      3f:    55                       push   %ebp
      40:    89 e5                    mov    %esp,%ebp
      42:    53                       push   %ebx
      43:    83 ec 14                 sub    $0x14,%esp
      46:    c7 04 24 08 00 00 00     movl   $0x8,(%esp)
      4d:    e8 fc ff ff ff           call   4e <bar()+0xf>
                4e: R_386_PC32    __cxa_allocate_exception
      52:    89 c3                    mov    %eax,%ebx
      54:    89 d8                    mov    %ebx,%eax
      56:    c7 00 00 00 00 00        movl   $0x0,(%eax)
      5c:    c7 40 04 00 00 00 00     movl   $0x0,0x4(%eax)
      63:    89 04 24                 mov    %eax,(%esp)
      66:    e8 fc ff ff ff           call   67 <bar()+0x28>
                67: R_386_PC32    nvdctor::nvdctor()
      6b:    c7 44 24 08 00 00 00     movl   $0x0,0x8(%esp)
      72:    00 
                6f: R_386_32    nvdctor::~nvdctor()
      73:    c7 44 24 04 00 00 00     movl   $0x0,0x4(%esp)
      7a:    00 
                77: R_386_32    typeinfo for nvdctor
      7b:    89 1c 24                 mov    %ebx,(%esp)
      7e:    e8 fc ff ff ff           call   7f <bar()+0x40>
                7f: R_386_PC32    __cxa_throw

    00000083 <foo()>:
      83:    55                       push   %ebp
      84:    89 e5                    mov    %esp,%ebp
      86:    53                       push   %ebx
      87:    83 ec 14                 sub    $0x14,%esp
      8a:    c7 04 24 0c 00 00 00     movl   $0xc,(%esp)
      91:    e8 fc ff ff ff           call   92 <foo()+0xf>
                92: R_386_PC32    __cxa_allocate_exception
      96:    89 c3                    mov    %eax,%ebx
      98:    89 d8                    mov    %ebx,%eax
      9a:    c7 00 00 00 00 00        movl   $0x0,(%eax)
      a0:    c7 40 04 00 00 00 00     movl   $0x0,0x4(%eax)
      a7:    c7 40 08 00 00 00 00     movl   $0x0,0x8(%eax)
      ae:    89 04 24                 mov    %eax,(%esp)
      b1:    e8 fc ff ff ff           call   b2 <foo()+0x2f>
                b2: R_386_PC32    derive::derive()
      b6:    c7 44 24 08 00 00 00     movl   $0x0,0x8(%esp)
      bd:    00 
      be:    c7 44 24 04 00 00 00     movl   $0x0,0x4(%esp)
      c5:    00 
                c2: R_386_32    typeinfo for derive
      c6:    89 1c 24                 mov    %ebx,(%esp)
      c9:    e8 fc ff ff ff           call   ca <foo()+0x47>
                ca: R_386_PC32    __cxa_throw

    000000ce <main>:
      ce:    55                       push   %ebp
      cf:    89 e5                    mov    %esp,%ebp
      d1:    83 e4 f0                 and    $0xfffffff0,%esp
      d4:    53                       push   %ebx
      d5:    83 ec 1c                 sub    $0x1c,%esp
      d8:    e8 fc ff ff ff           call   d9 <main+0xb>
                d9: R_386_PC32    baz()
      dd:    c7 04 24 08 00 00 00     movl   $0x8,(%esp)
      e4:    e8 fc ff ff ff           call   e5 <main+0x17>
                e5: R_386_PC32    __cxa_allocate_exception
      e9:    89 c3                    mov    %eax,%ebx
      eb:    89 d8                    mov    %ebx,%eax
      ed:    89 04 24                 mov    %eax,(%esp)
      f0:    e8 fc ff ff ff           call   f1 <main+0x23>
                f1: R_386_PC32    base::base()
      f5:    c7 44 24 08 00 00 00     movl   $0x0,0x8(%esp)
      fc:    00 
      fd:    c7 44 24 04 00 00 00     movl   $0x0,0x4(%esp)
     104:    00 
                101: R_386_32    typeinfo for base
     105:    89 1c 24                 mov    %ebx,(%esp)
     108:    e8 fc ff ff ff           call   109 <main+0x3b>
                109: R_386_PC32    __cxa_throw

    0000010d <__static_initialization_and_destruction_0(int, int)>:
     10d:    55                       push   %ebp
     10e:    89 e5                    mov    %esp,%ebp
     110:    83 ec 18                 sub    $0x18,%esp
     113:    83 7d 08 01              cmpl   $0x1,0x8(%ebp)
     117:    75 32                    jne    14b <__static_initialization_and_destruction_0(int, int)+0x3e>
     119:    81 7d 0c ff ff 00 00     cmpl   $0xffff,0xc(%ebp)
     120:    75 29                    jne    14b <__static_initialization_and_destruction_0(int, int)+0x3e>
     122:    c7 04 24 00 00 00 00     movl   $0x0,(%esp)
                125: R_386_32    gnvdctor
     129:    e8 fc ff ff ff           call   12a <__static_initialization_and_destruction_0(int, int)+0x1d>
                12a: R_386_PC32    nvdctor::nvdctor()
     12e:    b8 00 00 00 00           mov    $0x0,%eax
                12f: R_386_32    nvdctor::~nvdctor()
     133:    c7 44 24 08 00 00 00     movl   $0x0,0x8(%esp)
     13a:    00 
                137: R_386_32    __dso_handle
     13b:    c7 44 24 04 00 00 00     movl   $0x0,0x4(%esp)
     142:    00 
                13f: R_386_32    gnvdctor
     143:    89 04 24                 mov    %eax,(%esp)
     146:    e8 fc ff ff ff           call   147 <__static_initialization_and_destruction_0(int, int)+0x3a>
                147: R_386_PC32    __cxa_atexit
     14b:    c9                       leave  
     14c:    c3                       ret    

    0000014d <global constructors keyed to gnvdctor>:
     14d:    55                       push   %ebp
     14e:    89 e5                    mov    %esp,%ebp
     150:    83 ec 18                 sub    $0x18,%esp
     153:    c7 44 24 04 ff ff 00     movl   $0xffff,0x4(%esp)
     15a:    00 
     15b:    c7 04 24 01 00 00 00     movl   $0x1,(%esp)
     162:    e8 a6 ff ff ff           call   10d <__static_initialization_and_destruction_0(int, int)>
     167:    c9                       leave  
     168:    c3                       ret    

    Disassembly of section .text._ZN4baseC2Ev:

    00000000 <base::base()>:
       0:    55                       push   %ebp
       1:    89 e5                    mov    %esp,%ebp
       3:    8b 45 08                 mov    0x8(%ebp),%eax
       6:    c7 00 78 56 34 12        movl   $0x12345678,(%eax)
       c:    5d                       pop    %ebp
       d:    c3                       ret    

    Disassembly of section .text._ZN4baseC1Ev:

    00000000 <base::base()>:
       0:    55                       push   %ebp
       1:    89 e5                    mov    %esp,%ebp
       3:    8b 45 08                 mov    0x8(%ebp),%eax
       6:    c7 00 78 56 34 12        movl   $0x12345678,(%eax)
       c:    5d                       pop    %ebp
       d:    c3                       ret    

    Disassembly of section .text._ZN6derive4vfunEv:

    00000000 <derive::vfun()>:
       0:    55                       push   %ebp
       1:    89 e5                    mov    %esp,%ebp
       3:    5d                       pop    %ebp
       4:    c3                       ret    

    Disassembly of section .text._ZN7nvdctorD1Ev:

    00000000 <nvdctor::~nvdctor()>:
       0:    55                       push   %ebp
       1:    89 e5                    mov    %esp,%ebp
       3:    5d                       pop    %ebp
       4:    c3                       ret    

    Disassembly of section .text._ZN6deriveC1Ev:

    00000000 <derive::derive()>:
       0:    55                       push   %ebp
       1:    89 e5                    mov    %esp,%ebp
       3:    83 ec 18                 sub    $0x18,%esp
       6:    8b 45 08                 mov    0x8(%ebp),%eax
       9:    83 c0 04                 add    $0x4,%eax
       c:    89 04 24                 mov    %eax,(%esp)
       f:    e8 fc ff ff ff           call   10 <derive::derive()+0x10>
                10: R_386_PC32    base::base()
      14:    8b 45 08                 mov    0x8(%ebp),%eax
      17:    c7 00 08 00 00 00        movl   $0x8,(%eax)
                19: R_386_32    vtable for derive
      1d:    c9                       leave  
      1e:    c3                       ret    

    Disassembly of section .text._ZN7nvdctorC1Ev:

    00000000 <nvdctor::nvdctor()>:
       0:    55                       push   %ebp
       1:    89 e5                    mov    %esp,%ebp
       3:    83 ec 18                 sub    $0x18,%esp
       6:    8b 45 08                 mov    0x8(%ebp),%eax
       9:    89 04 24                 mov    %eax,(%esp)
       c:    e8 fc ff ff ff           call   d <nvdctor::nvdctor()+0xd>
                d: R_386_PC32    base::base()
      11:    c9                       leave  
      12:    c3                       ret    
    [root@Harry throwtype]#

  • 相关阅读:
    zookeeper分布式锁和服务优化配置
    【转】从Mac/OS和iOS开放源码浅谈UNIX家谱
    【转】深入了解CPU两大架构ARM与X86
    【转】volatile关键字。编译器不优化,多线程会改。防止随时变动的
    栈,寄存器,局部变量,内存,语言级别优化程序的方法
    在coursera上有哪些值得推荐的课程
    【转】楼天城楼教主的acm心路历程(作为励志用)
    硬中断软中断
    CPU GPU FPU TPU 及厂商
    C#中使用DLL相关问题
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487507.html
Copyright © 2011-2022 走看看