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]#

posted on 2019-03-07 09:25  tsecer  阅读(818)  评论(0编辑  收藏  举报

导航