作者descent (「雄辩是银,沉默是金」)
看板C_and_CPP
标题[心得] c++ exception handle 的实作
时间Fri Jan 13 21:56:17 2017
c++ exception handling 还真不是普通的复杂, 我目前仅仅知道其实作原理, 但实作细
节太复杂, 没能搞懂。
vc 和 gcc 有不同的作法, 我研究的是 gcc 的作法。
看了不少参考资料, 这篇文章以 binary hacks 繁体中文版 item 38, 39, 40, 41 为
主, 因为有个小程式可以用来实验以及说明 exception handle。
下面这 3 个函式是最主要的关键:
1 __cxa_throw
2 _Unwind_RaiseException
3 __gxx_personality_v0 (int version, _Unwind_Action actions,
_Unwind_Exception_Class exception_class, struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
这些函式的 source code 在 gcc libgcc 目录下, libgcc 是一个很神秘的 library, 里
头几乎是 gcc 特异功能的实做。unwind, 软体浮点数 ... 都是在这里。
gcc-3.4.4/gcc
gcc-5.4.0/libgcc
_Unwind_SjLj_RaiseException
gcc-5.4.0/libgcc/unwind-sjlj.c
_Unwind_RaiseException
gcc-5.4.0/libgcc/unwind.inc
#define PERSONALITY_FUNCTION __gxx_personality_v0
PERSONALITY_FUNCTION (int version,
_Unwind_Action actions,
_Unwind_Exception_Class exception_class,
struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
/gcc-5.4.0/libstdc++-v3/libsupc++/eh_personality.cc
__cxa_throw
extern "C" void __cxxabiv1::__cxa_throw (void *obj, std::type_info *tinfo,
void (_GLIBCXX_CDTOR_CALLABI *dest) (void *))
gcc-5.4.0/libstdc++-v3/libsupc++/eh_throw.cc
a.cpp L116 throw 100;
会转成呼叫 (ref a.cpp L118 ~ 120)
__cxa_allocate_exception()
__cxa_throw()
__cxa_throw() 发动时的流程:
__cxxabiv1::__cxa_throw
->
执行的是 _Unwind_SjLj_RaiseException
#ifdef _GLIBCXX_SJLJ_EXCEPTIONS
_Unwind_SjLj_RaiseException (&header->exc.unwindHeader);
#else
_Unwind_RaiseException (&header->exc.unwindHeader);
#endif
|
|
|-> __gxx_personality_sj0
|
|
|-> uw_install_context
uw_install_context 会呼叫 longjmp 回到上一个函式, 以 a.cpp 来说,
就是 func1()。
__gxx_personality_sj0 是干麻用的? 搜寻是不是有对应的 catch statement, 或是有那
个物件需要解构, 得去执行解构函式, 像 func1() 有个物件需要解构,
__gxx_personality_sj0 知道这件事情, 所以才要让 _Unwind_RaiseException 往
func1 跳, 很神奇是吧! 一但 func1() 拿掉 a.cpp L128 那个 Obj obj, 就不会跳回
func1()。
bt.cpp 只有模拟一半的功能, 使用 setjmp/longjmp, back_to_func 可以回到前一个
function, sjlj 就是用类似的方法串起这些 jmp_buf; 不过我不知道怎麽使用
.eh_frame, .gcc_except_table section 里头的资料来得知是不是有那个解构函式需要
执行, 是不是有符合的 catch statement。
bt.cpp
1 #include <setjmp.h>
2 #include <string>
3 #include <map>
4
5 using namespace std;
6
7 map<string, jmp_buf> stack_frame;
8
9 void back_to_func(const string &fn)
10 {
11 //jmp_buf frame = stack_frame[fn];
12 //stack_frame[fn];
13 longjmp(stack_frame[fn], 5);
14 }
15
16 void f3()
17 {
18 printf("in f3\n");
19 back_to_func("f2");
20 }
21
22 void f2()
23 {
24 jmp_buf frame;
25 int ret = setjmp(frame);
26 if (ret == 0)
27 {
28 stack_frame.insert({"f2", frame});
29 f3();
30 }
31 else
32 {
33 printf("back to f2\n");
34 back_to_func("f1");
35 }
36 }
37
38 void f1()
39 {
40 jmp_buf frame;
41 int ret = setjmp(frame);
42 if (ret == 0)
43 {
44 stack_frame.insert({"f1", frame});
45 f2();
46 }
47 else
48 {
49 printf("back to f1\n");
50 back_to_func("main");
51 }
52 }
53
54 int main(int argc, char *argv[])
55 {
56 jmp_buf frame;
57 int ret = setjmp(frame);
58 if (ret == 0)
59 {
60 stack_frame.insert({"main", frame});
61 f1();
62 }
63 else
64 {
65 printf("back to main\n");
66 }
67 printf("end main\n");
68 return 0;
69 }
binary hacks 繁体中文版 item 38, 39, 40, 41 是用 gcc 3.4.4 讲解, 虽然过时了,
但基本原理是一样的, 就先从 gcc 3.4.4 的建构开始吧。
g++ 使用 setjmp/longjmp, dwarf 这两种来支援 c++ exception handle, 目前的 gcc
5 似乎不使用 --enable-sjlj-exceptions, 我比较熟悉 setjmp/longjmp 的作法,
dwarf2 太苦了, 我不想走这条路, 先以 --enable-sjlj-exceptions 来建构 gcc 3.4.4
。
我以熟悉的 setjmp/long 来学习, 编译 gcc 3.4.4 加上 --enable-sjlj-exceptions,
即使用以 setjmp/longjmp 实做的 exception handle。
setjmp/longjmp, dwarf 是用来处理 unwind, 就是从目前的函式回到上一个函式, 类似
bt.cpp 做的事情, dwarf 的作法需要去理解 dwarf 格式, 听说是不得了的复杂, 我不想
花时间在上头, 而 setjmp/longjmp 我已经知道其实作原理, 不需要在花额外的功夫。
另外一个需要的能力就是知道要回到那一个 function, 这就是靠神秘的 LSDA 的内容来
得知, g++ 会在 .gcc_except_table section 插入某些资讯, 让
__gxx_personality_sj0 可以用来判断要回到那个函式。
env:
32 bit debian
编译 gcc-3.4.4
tar xvf gcc-3.4.4.tar.bz2
mkdir gcc-build
cd gcc-build
../gcc-3.4.4/configure --enable-languages=c,c++ --enable-sjlj-exceptions
make
make install
编译时可能会遇到一些 header 的问题, 我把 /usr/include/i386-linux-gnu/* link
到 /usr/include
root@debian32:/usr/include# ls -l sys
lrwxrwxrwx 1 root root 32 Dec 26 15:42 sys -> /usr/include/i386-linux-gnu/sys
没支援 --enable-sjlj-exceptions g++ 的编译错误
descent@debian64:eh_impl$ g++ -g -o a a.cpp
/tmp/ccRq4BBp.o: In function `main':
/home/descent/git/eh_impl/a.cpp:126: undefined reference to
`__gxx_personality_sj0'
/home/descent/git/eh_impl/a.cpp:138: undefined reference to
`_Unwind_SjLj_Register'
/home/descent/git/eh_impl/a.cpp:142: undefined reference to
`_Unwind_SjLj_Unregister'
collect2: error: ld returned 1 exit status
支援 --enable-sjlj-exceptions 的 g++
descent@debian32:eh_impl$ g++ -v
Reading specs from /usr/local/lib/gcc/i686-pc-linux-gnu/3.4.4/specs
Configured with: ../gcc-3.4.4/configure --enable-languages=c,c++
--enable-sjlj-exceptions
Thread model: posix
gcc version 3.4.4
a.cpp 是 binary hack 书上提供的范例, 提供了对照, try/catch/throw 是怎麽转成一
般的 c++ 程式码, 看上去就清楚了, 最麻烦的就是那个 lsda 到底是怎麽样的资料结
构, 可惜书上也没写得很清楚, 看来只能看第 0 手资料了。
list 1. a.cpp 执行结果
/usr/local/bin/g++ -g -o a a.cpp
使用 ldd 查看 so
descent@debian32:eh_impl$ ldd a
linux-gate.so.1 (0xb77bf000)
libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xb763a000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb75e5000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xb75c8000) # 没有
dynamic lik 我们编译的那个 libgcc_s.so.1
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7411000)
/lib/ld-linux.so.2 (0x8007d000)
debian32:eh_impl$ export LD_LIBRARY_PATH=/usr/local/lib # x86 32 bit
environment
再一次 ldd
descent@debian32:eh_impl$ ldd a
linux-gate.so.1 (0xb779e000)
libstdc++.so.6 => /usr/local/lib/libstdc++.so.6 (0xb76b6000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xb764f000)
libgcc_s.so.1 => /usr/local/lib/libgcc_s.so.1 (0xb7647000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7490000)
/lib/ld-linux.so.2 (0x800e2000)
这样就对了。
descent@debian32:eh_impl$ ./a
func1 begin
obj ctor
func2 begin
obj dtor
thrown_obj: 100
list 1 的结果可以成功呼叫解构函式, 以及跑到正确的 catch 程式码。可以用 gdb 跑
跑看, exception handle 的神秘感解除了一半, 另外一半还在 libunwind, libgcc 里头
的函式。
a.cpp L166 就是 L118 ~ 120 那 3 行; a.cpp L137 ~ 145 就是 L149 ~ 167 那麽多行
。
a.cpp
1 // test c++ exception handle by g++ 3.4.4
2 // example code from binary hacks chinese version, page 145
3
4 #include <cstdio>
5 #include <iostream>
6 #include <typeinfo>
7 using namespace std;
8
9 #include <unwind.h>
10
11 extern "C"
12 {
13 // libsupc++/eh_alloc.cc
14 void * __cxa_allocate_exception(std::size_t thrown_size);
15
16 // libsupc++/eh_throw.cc
17 //void __cxa_throw (void *obj, std::type_info *tinfo, void (*dest)
(void *));
18 void __cxa_throw (void *obj, void *tinfo, void (*dest) (void *));
19
20 // libsupc++/eh_catch.cc
21 void * __cxa_begin_catch (void *exc_obj_in);
22 void __cxa_end_catch ();
23
24
25 #define PERSONALITY_FUNCTION __gxx_personality_sj0
26
27 // libsupc++/eh_personality.cc
28 _Unwind_Reason_Code PERSONALITY_FUNCTION (int version,
29 _Unwind_Action actions,
30 _Unwind_Exception_Class exception_class,
31 struct _Unwind_Exception *ue_header,
32 struct _Unwind_Context *context);
33
34
35 }
36
37 struct Lsda
38 {
39 unsigned char start_format;
40 unsigned char type_format;
41 unsigned char type_length;
42 unsigned char call_site_format;
43 unsigned char call_site_length;
44 unsigned char call_site_table[2];
45 signed char action_table[2];
46 const std::type_info *catch_type[1];
47 }__attribute__((packed));
48
49 Lsda my_lsda=
50 {
51 0xff,
52 0x00,
53 10,
54 0x01,
55 2,
56 {0,1},
57 {1,0},
58 &typeid(int),
59 };
60
61
62 // unwind-sjlj.c
63 /* This structure is allocated on the stack of the target function.
64 This must match the definition created in except.c:init_eh. */
65 struct SjLj_Function_Context
66 {
67 /* This is the chain through all registered contexts. It is
68 filled in by _Unwind_SjLj_Register. */
69 struct SjLj_Function_Context *prev;
70
71 /* This is assigned in by the target function before every call
72 to the index of the call site in the lsda. It is assigned by
73 the personality routine to the landing pad index. */
74 int call_site;
75
76 /* This is how data is returned from the personality routine to
77 the target function's handler. */
78 _Unwind_Word data[4];
79
80 /* These are filled in once by the target function before any
81 exceptions are expected to be handled. */
82 _Unwind_Personality_Fn personality;
83 void *lsda;
84
85 #ifdef DONT_USE_BUILTIN_SETJMP
86 /* We don't know what sort of alignment requirements the system
87 jmp_buf has. We over estimated in except.c, and now we have
88 to match that here just in case the system *didn't* have more
89 restrictive requirements. */
90 jmp_buf jbuf __attribute__((aligned));
91 #else
92 void *jbuf[];
93 #endif
94 };
95
96 //#define CXX_EH
97
98 class Obj
99 {
100 public:
101 Obj()
102 {
103 cout << "obj ctor" << endl;
104 }
105 ~Obj()
106 {
107 cout << "obj dtor" << endl;
108 }
109
110 };
111
112 void func2()
113 {
114 cout << "func2 begin" << endl;
115 #ifdef CXX_EH
116 throw 100;
117 #else
118 void *throw_obj = __cxa_allocate_exception(sizeof(int));
119 *(int*)throw_obj = 100; // 这就是那个 throw 100, 的那个 100
120 __cxa_throw(throw_obj, (std::type_info*)&typeid(int), NULL);
121 #endif
122 cout << "func2 end" << endl;
123 }
124
125 void func1()
126 {
127 cout << "func1 begin" << endl;
128 Obj obj;
129
130 func2();
131 cout << "func1 end" << endl;
132 }
133
134 int main(int argc, char *argv[])
135 {
136 #ifdef CXX_EH
137 try
138 {
139 cout << "hello" << endl;
140 func1();
141 }
142 catch (int eh)
143 {
144 cout << "catch int: " << eh << endl;
145 }
146
147 #else
148
149 SjLj_Function_Context sjlj;
150
151 sjlj.personality = __gxx_personality_sj0;
152 sjlj.lsda = (void*)&my_lsda;
153 sjlj.call_site = 1;
154
155 if (__builtin_setjmp(sjlj.jbuf) == 1)
156 {
157 void *thrown_obj = __cxa_begin_catch((void*)sjlj.data[0]);
158 printf("thrown_obj: %d\n", *(int*)thrown_obj);
159 __cxa_end_catch();
160 }
161 else
162 {
163 _Unwind_SjLj_Register(&sjlj);
164 //throw 100;
165 func1();
166 }
167 _Unwind_SjLj_Unregister(&sjlj);
168 #endif
169 return 0;
170 }
objdump -d a 看不到详细的反组译程式码, 我使用 gdb 来反组译, 这是意外的收获。
list 2. dis.gdb
1 >0x8048b16 <func1()+96> lea -0x28(%ebp),%eax
2 0x8048b19 <func1()+99> mov %eax,(%esp)
3 0x8048b1c <func1()+102> call 0x8048d2e <Obj::Obj()>
4 0x8048b21 <func1()+107> movl $0x1,-0x58(%ebp)
5 0x8048b28 <func1()+114> call 0x8048a32 <func2()>
6 0x8048b2d <func1()+119> movl $0x8048e5a,0x4(%esp)
7 0x8048b35 <func1()+127> movl $0x804b080,(%esp)
8 0x8048b3c <func1()+134> call 0x80487d0
<_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
9 0x8048b41 <func1()+139> movl $0x8048780,0x4(%esp)
10 0x8048b49 <func1()+147> mov %eax,(%esp)
11 0x8048b4c <func1()+150> call 0x8048770 <_ZNSolsEPFRSoS_E@plt>
12 0x8048b51 <func1()+155> jmp 0x8048b8c <func1()+214>
13 0x8048b53 <func1()+157> lea 0x18(%ebp),%ebp
14 0x8048b56 <func1()+160> mov -0x54(%ebp),%eax
15 0x8048b59 <func1()+163> mov %eax,-0x64(%ebp)
16 0x8048b5c <func1()+166> mov -0x64(%ebp),%edx
17 0x8048b5f <func1()+169> mov %edx,-0x60(%ebp)
18 0x8048b62 <func1()+172> lea -0x28(%ebp),%eax
19 0x8048b65 <func1()+175> mov %eax,(%esp)
20 0x8048b68 <func1()+178> movl $0x0,-0x58(%ebp)
21 0x8048b6f <func1()+185> call 0x8048d02 <Obj::~Obj()> the 1st dtor
22 0x8048b74 <func1()+190> mov -0x60(%ebp),%eax
23 0x8048b77 <func1()+193> mov %eax,-0x64(%ebp)
24 0x8048b7a <func1()+196> mov -0x64(%ebp),%edx
25 0x8048b7d <func1()+199> mov %edx,(%esp)
26 0x8048b80 <func1()+202> movl $0xffffffff,-0x58(%ebp)
27 0x8048b87 <func1()+209> call 0x80487e0 <_Unwind_SjLj_Resume@plt>
28 0x8048b8c <func1()+214> lea -0x28(%ebp),%eax
29 0x8048b8f <func1()+217> mov %eax,(%esp)
30 0x8048b92 <func1()+220> movl $0xffffffff,-0x58(%ebp)
31 0x8048b99 <func1()+227> call 0x8048d02 <Obj::~Obj()> the 2nd
dtor
32 0x8048b9e <func1()+232> lea -0x5c(%ebp),%eax
33 0x8048ba1 <func1()+235> mov %eax,(%esp)
34 0x8048ba4 <func1()+238> call 0x8048810 <_Unwind_SjLj_Unregister@plt>
35 0x8048ba9 <func1()+243> add $0x6c,%esp
36 0x8048bac <func1()+246> pop %ebx
37 0x8048bad <func1()+247> pop %esi
38 0x8048bae <func1()+248> pop %edi
39 0x8048baf <func1()+249> pop %ebp
40 0x8048bb0 <func1()+250> ret
list 2 L21, L31 有 2 个 dtor, 很奇怪吧, L21 是给 exception handle 用的, 当从
throw 回到 func1 时, 会莫名的抵达这里, 事实上是回到 L13 0x8048b53 这里, 然後在
执行 L27 回到上一个 stack frame (本例来说就是 main); L31 则是给正常执行流程呼
叫的 dtor, L12 有个狡猾的 jmp, 真是机关算尽。
list 3 是 g++ 3.4.4 的反组译版本, 更清楚了, 我应该早点想到的, 它不只为我解除
了 2 个 dtor 的疑惑, 还把莫名会抵达 func1() 的原因也找了出来, 甚至连那个 Lsda
也帮我厘清了, 也因为知道 Lsda 的内容, 我连带改出 g++ 5.4.0 的版本了。
list 3 是使用 try/catch/throw 的版本, list 3 L302, 303, 是不是和自己填入
a.cpp L151 ~ 153 一样呢?
list 3 L303, L387 就是那个该死的 lsda, 从 list 3 L387 ~ L402, 在
.gcc_except_table section (就是 LSDA - Language Specific Data Area), 又是另外
一个狡猾的地方。
至於 g++ 5.4.0 我怎麽改出来的呢? 就是用 g++ 5.4.0 去反组译 try/catch/throw 的
版本, 把 .gcc_except_table section, 填到那个 lsda 就好了, 果然还真的不同。
再来是那个莫名回到 func1 的动作是怎麽作到的呢? 这个困扰我好久, 用 gdb 追也找不
出所以然, 照理说应该要有一个 setjmp 在这里, 才能透过 longjmp 回到这, 但我就一
直找不到哪里有 call setjmp, 直到我用 g++ -S 之後才看到, 原来 g++ 在 func1 安插
了类似 setjmp 的程式码, 这才让 _Unwind_RaiseException 有能力回到 func1。
list 3 L182 _Unwind_SjLj_Register 的动作类似 bt.cpp 那个 map<string,
jmp_buf>, 把每一个 fuction 要回来的位置记起来, 它的参数 SjLj_Function_Context
里头有 jmp_buf, 得先把 jmp_buf 填好才行, 让 uw_install_context 的 longjmp 回到
这里。
由於是 g++ 插入的 code, 得从组合语言去看出来才行, 还真是难。L177 的 .L18 就是
setjmp 纪录起来的值, 这里就是在填上面说的 jmp_buf 的部份, 但并不是产生呼叫
setjmp 的程式码, 而是填入那个 jmp_buf 所需要的值就可以了, 所以
_Unwind_RaiseException 发动 uw_install_context, 就会回到 L202, 和 gdb 的显示是
一样的。
把 func1() Obj obj; 拿掉, 再看 g++ 产生的 a.s, 就会发现那个 func1 和 c 的长相
一样, 不会被偷偷插入那麽多程式码了。
#define uw_install_context(CURRENT, TARGET) \
do \
{ \
_Unwind_SjLj_SetContext ((TARGET)->fc); \
longjmp ((TARGET)->fc->jbuf, 1); \
} \
while (0)
list 3 L172 ~ 173 是不是有类似的行为, 塞入 __gxx_personality_sj0, lsda 这些资
料, lsda 是我目前还无法突破的部份。
list 3. g++-3.4.4 -S a.cpp a-3.3.4.s
1 .file "a.cpp"
2 .text
3 .align 2
4 .type _ZSt17__verify_groupingPKcjRKSs, @function
116 .LC0:
117 .string "func2 begin"
118 .LC1:
119 .string "func2 end"
120 .text
121 .align 2
122 .globl _Z5func2v
123 .type _Z5func2v, @function
124 _Z5func2v:
125 pushl %ebp
126 movl %esp, %ebp
127 subl $24, %esp
128 movl $.LC0, 4(%esp)
129 movl $_ZSt4cout, (%esp)
130 call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
131 movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
4(%esp)
132 movl %eax, (%esp)
133 call _ZNSolsEPFRSoS_E
134 movl $4, (%esp)
135 call __cxa_allocate_exception
136 movl $100, (%eax)
137 .L11:
138 movl $0, 8(%esp)
139 movl $_ZTIi, 4(%esp)
140 movl %eax, (%esp)
141 call __cxa_throw
142 movl $.LC1, 4(%esp)
143 movl $_ZSt4cout, (%esp)
144 call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
145 movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
4(%esp)
146 movl %eax, (%esp)
147 call _ZNSolsEPFRSoS_E
148 leave
149 ret
150 .L10:
151 .size _Z5func2v, .-_Z5func2v
152 .globl _Unwind_SjLj_Resume
153 .globl __gxx_personality_sj0
154 .globl _Unwind_SjLj_Register
155 .globl _Unwind_SjLj_Unregister
156 .section .rodata
157 .LC2:
158 .string "func1 begin"
159 .LC3:
160 .string "func1 end"
161 .text
162 .align 2
163 .globl _Z5func1v
164 .type _Z5func1v, @function
165 _Z5func1v:
166 pushl %ebp
167 movl %esp, %ebp
168 pushl %edi
169 pushl %esi
170 pushl %ebx
171 subl $108, %esp
172 movl $__gxx_personality_sj0, -68(%ebp)
173 movl $.LLSDA1420, -64(%ebp)
174 leal -60(%ebp), %eax
175 leal -24(%ebp), %edx
176 movl %edx, (%eax)
177 movl $.L18, %edx
178 movl %edx, 4(%eax)
179 movl %esp, 8(%eax)
180 leal -92(%ebp), %eax
181 movl %eax, (%esp)
182 call _Unwind_SjLj_Register
183 movl $.LC2, 4(%esp)
184 movl $_ZSt4cout, (%esp)
185 movl $-1, -88(%ebp)
186 call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
187 movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
4(%esp)
188 movl %eax, (%esp)
189 call _ZNSolsEPFRSoS_E
190 leal -40(%ebp), %eax
191 movl %eax, (%esp)
192 call _ZN3ObjC1Ev
193 movl $1, -88(%ebp)
194 call _Z5func2v
195 movl $.LC3, 4(%esp)
196 movl $_ZSt4cout, (%esp)
197 call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
198 movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
4(%esp)
199 movl %eax, (%esp)
200 call _ZNSolsEPFRSoS_E
201 jmp .L15
202 .L18:
203 leal 24(%ebp), %ebp
204 movl -84(%ebp), %eax
205 movl %eax, -100(%ebp)
206 .L14:
207 movl -100(%ebp), %edx
208 movl %edx, -96(%ebp)
209 leal -40(%ebp), %eax
210 movl %eax, (%esp)
211 movl $0, -88(%ebp)
212 call _ZN3ObjD1Ev
213 movl -96(%ebp), %eax
214 movl %eax, -100(%ebp)
215 .L16:
216 movl -100(%ebp), %edx
217 movl %edx, (%esp)
218 movl $-1, -88(%ebp)
219 call _Unwind_SjLj_Resume
220 .L15:
221 leal -40(%ebp), %eax
222 movl %eax, (%esp)
223 movl $-1, -88(%ebp)
224 call _ZN3ObjD1Ev
225 .L13:
226 leal -92(%ebp), %eax
227 movl %eax, (%esp)
228 call _Unwind_SjLj_Unregister
229 addl $108, %esp
230 popl %ebx
231 popl %esi
232 popl %edi
233 popl %ebp
234 ret
235 .size _Z5func1v, .-_Z5func1v
236 .section .gcc_except_table,"a",@progbits
237 .LLSDA1420:
238 .byte 0xff
239 .byte 0xff
240 .byte 0x1
241 .uleb128 .LLSDACSE1420-.LLSDACSB1420
242 .LLSDACSB1420:
243 .uleb128 0x0
244 .uleb128 0x0
245 .LLSDACSE1420:
246 .text
247 .section .rodata
248 .LC4:
249 .string "obj dtor\n"
250 .section .gnu.linkonce.t._ZN3ObjD1Ev,"ax",@progbits
251 .align 2
252 .weak _ZN3ObjD1Ev
253 .type _ZN3ObjD1Ev, @function
254 _ZN3ObjD1Ev:
255 pushl %ebp
256 movl %esp, %ebp
257 subl $8, %esp
258 movl $.LC4, (%esp)
259 call printf
260 leave
261 ret
262 .size _ZN3ObjD1Ev, .-_ZN3ObjD1Ev
263 .section .rodata
264 .LC5:
265 .string "obj ctor\n"
266 .section .gnu.linkonce.t._ZN3ObjC1Ev,"ax",@progbits
267 .align 2
268 .weak _ZN3ObjC1Ev
269 .type _ZN3ObjC1Ev, @function
270 _ZN3ObjC1Ev:
271 pushl %ebp
272 movl %esp, %ebp
273 subl $8, %esp
274 movl $.LC5, (%esp)
275 call printf
276 leave
277 ret
278 .size _ZN3ObjC1Ev, .-_ZN3ObjC1Ev
279 .section .rodata
280 .LC6:
281 .string "hello"
282 .LC7:
283 .string "catch int: "
284 .text
285 .align 2
286 .globl main
287 .type main, @function
288 main:
289 pushl %ebp
290 movl %esp, %ebp
291 pushl %edi
292 pushl %esi
293 pushl %ebx
294 subl $92, %esp
295 andl $-16, %esp
296 movl $0, %eax
297 addl $15, %eax
298 addl $15, %eax
299 shrl $4, %eax
300 sall $4, %eax
301 subl %eax, %esp
302 movl $__gxx_personality_sj0, -44(%ebp)
303 movl $.LLSDA1421, -40(%ebp)
304 leal -36(%ebp), %eax
305 leal -12(%ebp), %edx
306 movl %edx, (%eax)
307 movl $.L31, %edx
308 movl %edx, 4(%eax)
309 movl %esp, 8(%eax)
310 leal -68(%ebp), %eax
311 movl %eax, (%esp)
312 call _Unwind_SjLj_Register
313 movl $.LC6, 4(%esp)
314 movl $_ZSt4cout, (%esp)
315 movl $2, -64(%ebp)
316 call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
317 movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
4(%esp)
318 movl %eax, (%esp)
319 call _ZNSolsEPFRSoS_E
320 call _Z5func1v
321 jmp .L24
322 .L30:
323 cmpl $1, -84(%ebp)
324 je .L25
325 movl -76(%ebp), %eax
326 movl %eax, (%esp)
327 movl $-1, -64(%ebp)
328 call _Unwind_SjLj_Resume
329 .L25:
330 movl -76(%ebp), %edx
331 movl %edx, (%esp)
332 movl $-1, -64(%ebp)
333 call __cxa_begin_catch
334 movl (%eax), %eax
335 movl %eax, -16(%ebp)
336 movl $.LC7, 4(%esp)
337 movl $_ZSt4cout, (%esp)
338 movl $1, -64(%ebp)
339 call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
340 movl %eax, %edx
341 movl -16(%ebp), %eax
342 movl %eax, 4(%esp)
343 movl %edx, (%esp)
344 call _ZNSolsEi
345 movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_,
4(%esp)
346 movl %eax, (%esp)
347 call _ZNSolsEPFRSoS_E
348 jmp .L27
349 .L31:
350 leal 12(%ebp), %ebp
351 movl -64(%ebp), %eax
352 movl -60(%ebp), %edx
353 movl %edx, -76(%ebp)
354 movl -56(%ebp), %edx
355 movl %edx, -84(%ebp)
356 cmpl $1, %eax
357 je .L30
358 .L26:
359 movl -76(%ebp), %eax
360 movl %eax, -80(%ebp)
361 call __cxa_end_catch
362 movl -80(%ebp), %edx
363 movl %edx, -76(%ebp)
364 .L28:
365 movl -76(%ebp), %eax
366 movl %eax, (%esp)
367 movl $-1, -64(%ebp)
368 call _Unwind_SjLj_Resume
369 .L27:
370 call __cxa_end_catch
371 .L24:
372 movl $0, -72(%ebp)
373 .L23:
374 leal -68(%ebp), %eax
375 movl %eax, (%esp)
376 call _Unwind_SjLj_Unregister
377 movl -72(%ebp), %eax
378 leal -12(%ebp), %esp
379 popl %ebx
380 popl %esi
381 popl %edi
382 popl %ebp
383 ret
384 .size main, .-main
385 .section .gcc_except_table
386 .align 4
387 .LLSDA1421:
388 .byte 0xff
389 .byte 0x0
390 .uleb128 .LLSDATT1421-.LLSDATTD1421
391 .LLSDATTD1421:
392 .byte 0x1
393 .uleb128 .LLSDACSE1421-.LLSDACSB1421
394 .LLSDACSB1421:
395 .uleb128 0x0
396 .uleb128 0x0
397 .uleb128 0x1
398 .uleb128 0x1
399 .LLSDACSE1421:
400 .byte 0x1
401 .byte 0x0
402 .align 4
403 .long _ZTIi
404 .LLSDATT1421:
405 .text
406 .section .gnu.linkonce.t._ZSt3minIjERKT_S2_S2_,"ax",@progbits
407 .align 2
408 .weak _ZSt3minIjERKT_S2_S2_
409 .type _ZSt3minIjERKT_S2_S2_, @function
430 .text
431 .align 2
454 .align 2
455 .type _GLOBAL__I_my_lsda, @function
456 _GLOBAL__I_my_lsda:
457 pushl %ebp
458 movl %esp, %ebp
459 subl $8, %esp
460 movl $65535, 4(%esp)
461 movl $1, (%esp)
462 call _Z41__static_initialization_and_destruction_0ii
463 leave
464 ret
465 .size _GLOBAL__I_my_lsda, .-_GLOBAL__I_my_lsda
466 .section .ctors,"aw",@progbits
467 .align 4
468 .long _GLOBAL__I_my_lsda
469 .text
470 .align 2
471 .type _GLOBAL__D_my_lsda, @function
472 _GLOBAL__D_my_lsda:
473 pushl %ebp
474 movl %esp, %ebp
475 subl $8, %esp
476 movl $65535, 4(%esp)
477 movl $0, (%esp)
478 call _Z41__static_initialization_and_destruction_0ii
479 leave
480 ret
481 .size _GLOBAL__D_my_lsda, .-_GLOBAL__D_my_lsda
482 .section .dtors,"aw",@progbits
483 .align 4
484 .long _GLOBAL__D_my_lsda
494 .section .note.GNU-stack,"",@progbits
495 .ident "GCC: (GNU) 3.4.4"
由於用到 typeinfo 来判断型别, 这是为什麽 exception handle 需要有 rtti 支援的原
因。
从 global object, static object, virtaul function, rtti 到 exception handle,
现在你知道 c++ 有那麽多的黑魔法, c++ 真是不简单, 这也是为人所诟病的一个特性,
太黑箱了。
在 c++ 这麽多的特性, 我最有兴趣的是 virtual function 和 exception handle 的实
作, 我已经找了多年的资料, 有点收获真是开心。
typeid ref:
typeid详解 (
https://goo.gl/i8t0TU )
执行时期型态资讯(RTTI) (
https://goo.gl/SdoIX6 )
ref:
C++ exception handling internals (
https://goo.gl/4zQhql ) (感谢帅叔叔提供)
http://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf
(
https://goo.gl/N70Kzk )
c++ 异常处理(1) (
https://goo.gl/orGGA5 )
c++ 异常处理(2) (
https://goo.gl/MSwKzu )
Exception Handling in LLVM (
https://goo.gl/gauszo )
Itanium C++ ABI: Exception Handling ($Revision: 1.1 $) (
https://goo.gl/7x2i3H )
非本地跳转:应用 (
https://goo.gl/OGa4vu )
浅谈C++例外处理 (中篇) (
https://goo.gl/2Qg8Ta )
Chapter 8. Exception Frames (
https://goo.gl/jivOjq )
GCC C++ Exception Handling Implementation (
https://goo.gl/D1xGfL )
.gcc_except_table (
https://goo.gl/0BYPhM )
Elf-Section .gcc_except_table (
https://goo.gl/mpyUFD )
SJLJ EH: C++ exception handling in PNaCl using setjmp()+longjmp() (
https://goo.gl/G2RYZd )
binary hacks 繁体中文版 item 38, 39, 40, 41
// 本文使用 Blog2BBS 自动将Blog文章转成缩址的BBS纯文字
http://goo.gl/TZ4E17 //
blog 版本
http://descent-incoming.blogspot.tw/2016/12/c-exception-handling1.html
--
纸上得来终觉浅,绝知此事要躬行。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 49.218.4.162
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1484315788.A.52E.html
1F:推 bachelorwhc: 太强惹 01/13 22:07
2F:推 Caesar08: 推 01/13 22:45
3F:推 ilikekotomi: 虽然看不懂还是要推 01/13 23:16
4F:推 lc85301: 这个必须推 01/13 23:17
5F:推 b98901056: 先推再来努力研究@@ 01/13 23:31
6F:推 genius945: 推 原来是这样啊(咦? 01/14 00:08
7F:推 ko27tye: 天哪 改天再看好惹 01/14 02:00
8F:推 Neisseria: d 大的东西常常看不懂,还是推一个 01/14 08:18
板上很多文章我也看不懂的。
9F:推 Ebergies: 很好奇怎麽会想要去解析实际做法呢? 纯粹好奇而已吗~ 01/14 17:28
只是单纯「想知道」而已, 我不是作 compiler 相关的工作,
不过搞这个还真吃力不讨好, 我应该多学点民间专长才是。
※ 编辑: descent (101.15.66.217), 01/14/2017 21:09:32
10F:推 sunneo: uefi上要是这个也弄起来该有多好 02/13 00:23