[心得] c++ exception handle 的實作

看板C_and_CPP作者 (「雄辯是銀,沉默是金」)時間7年前 (2017/01/13 21:56), 7年前編輯推噓10(1000)
留言10則, 10人參與, 最新討論串1/1
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://www.ptt.cc/bbs/C_and_CPP/M.1484315788.A.52E.html

01/13 22:07, , 1F
太強惹
01/13 22:07, 1F

01/13 22:45, , 2F
01/13 22:45, 2F

01/13 23:16, , 3F
雖然看不懂還是要推
01/13 23:16, 3F

01/13 23:17, , 4F
這個必須推
01/13 23:17, 4F

01/13 23:31, , 5F
先推再來努力研究@@
01/13 23:31, 5F

01/14 00:08, , 6F
推 原來是這樣啊(咦?
01/14 00:08, 6F

01/14 02:00, , 7F
天哪 改天再看好惹
01/14 02:00, 7F

01/14 08:18, , 8F
d 大的東西常常看不懂,還是推一個
01/14 08:18, 8F
板上很多文章我也看不懂的。

01/14 17:28, , 9F
很好奇怎麼會想要去解析實際做法呢? 純粹好奇而已嗎~
01/14 17:28, 9F
只是單純「想知道」而已, 我不是作 compiler 相關的工作, 不過搞這個還真吃力不討好, 我應該多學點民間專長才是。 ※ 編輯: descent (101.15.66.217), 01/14/2017 21:09:32

02/13 00:23, , 10F
uefi上要是這個也弄起來該有多好
02/13 00:23, 10F
文章代碼(AID): #1OUDoCKk (C_and_CPP)