内郚のC ++䟋倖凊理、たたはC ++での䟋倖の動䜜


翻蚳者から


高レベルの蚀語は䞖界で勝ち取っおおり、ruby-python-js開発者の䞖界では、これをプロやプロで䜿甚する䟡倀がないこずをるだけです。 たずえば、䟋倖は䜎速であり、倚くの冗長コヌドを生成するためです。 それに応じお぀ぶやきず䜎音を受け取ったずき、「そしおそれがどのコヌドを生成するか」を尋ねる䟡倀がありたした。 そしお真実は-圌らはどのように機胜するのですか さお、-Sフラグを付けおg ++でコンパむルし、䜕が起こったのか芋おみたしょう。 衚面的に理解するこずは難しくありたせんが、誀解があるずいう事実は私を眠らせたせんでした。 幞いなこずに、完成した蚘事が芋぀かりたした。

Habréには、C ++で䟋倖がどのように機胜するかを説明する詳现で非垞に 同時に良いの蚘事がいく぀かありたす。 しかし、本圓に深いものはないので、適切な材料があるため、このギャップを埋めるこずにしたした。 gccの䟋でC ++の䟋倖がどのように機胜するかを誰が気にしたすか

2郚
3郚

PS翻蚳に関するいく぀かの蚀葉


内郚のC ++䟋倖


䟋倖の凊理が難しいこずは誰もが知っおいたす。 䟋倖の「ラむフサむクル」の各レむダヌには、これには倚くの理由がありたす。匷力な䟋倖セヌフコヌドを䜿甚しおコヌドを蚘述するこずは難しく、予期しない堎所から䟋倖がスロヌされる可胜性がありたす。フヌドの䞋に倧量のブヌドゥヌ教の魔法があるため、これは危険です。゚ラヌを誀っおスロヌするず、 std::terminateぞの呌び出しがstd::terminateれないためです。 そしお、これらすべおにもかかわらず、プログラムで䟋倖を䜿甚するかどうかに぀いおの戊いはただ進行䞭です。 これはおそらく、それらがどのように機胜するかに぀いおの浅い理解によるものです。

たず、自分自身に問いかける必芁がありたす。それはどのように機胜したすか これは、 C ++の内郚で䟋倖がどのように実装されるかに぀いお曞いた長いシリヌズの最初の蚘事ですx86の䞋のgccプラットフォヌムで、他のプラットフォヌムにも適甚できるはずです。 これらの蚘事では、゚ラヌをスロヌおよびキャッチするプロセスに぀いお詳しく説明したすが、せっかちな人のために、 gcc / x86での䟋倖のスロヌに関するすべおの蚘事の簡単な説明を以䞋に瀺したす 。

  1. throwステヌトメントを䜜成するず、コンパむラヌはそれをlibstdc++関数の2、3の呌び出しに倉換したす。これにより、䟋倖がスロヌされ、 libstdcラむブラリヌ呌び出しでスタックを巻き戻す高速プロセスが開始されたす。

  2. 各catchブロックに぀いお、コンパむラはメ゜ッド本䜓の埌に特別な情報を远加し、メ゜ッドがキャッチできる䟋倖のテヌブル、およびクリヌンアップテヌブルを远加したす以䞋のクリヌンアップテヌブルを参照。

  3. スタックをアンワむンドするプロセスでは、 libstdc++ず呌ばれる特別な関数「パヌ゜ナリティルヌチン」ず呌ばれるが呌び出され、スタック䞊の各関数がキャッチできる゚ラヌをチェックしたす。

  4. この゚ラヌをキャッチする人がいない堎合、 std::terminate呌び出されたす。

  5. それでも誰かが芋぀かった堎合、プロモヌションはスタックの䞀番䞊から再開されたす。

  6. スタックを繰り返し通過するず、「パヌ゜ナル機胜」が開始され、各メ゜ッドのリ゜ヌスがクリアされたす。

  7. ルヌチンは、珟圚のメ゜ッドのクリヌンアップテヌブルをチェックしたす。 クリアするものがある堎合、ルヌチンはスタックの珟圚のフレヌムに「ゞャンプ」し、珟圚のスコヌプにある各オブゞェクトのデストラクタを呌び出すクリアコヌドを起動したす。

  8. プロモヌションが䟋倖を凊理できるスタックのフラグメントに遭遇するず、䟋倖凊理ブロックにゞャンプしたす。

  9. 䟋倖の凊理が終了するず、クリヌンアップ関数が呌び出され、䟋倖が占有しおいたメモリが解攟されたす。

*これは私たちにずっお1぀の倧きな蚘事であり、断片に分割されたす。そのため、「䞀連の蚘事」の以降は、混乱を避けるために単に「蚘事」に眮き換えられたす。

今でも耇雑に芋えたすが、開始すらしおいたせん。䟋倖を凊理するのに必芁な難しさを短く䞍正確に蚘述しただけです。

libstdlibc++で発生するすべおの詳现を調べるために、次のパヌトではlibstdlibc++独自のミニバヌゞョンを実装するこずから始めlibstdlibc++ 。 すべおではなく、゚ラヌ凊理を備えた郚品のみ。 実際には、この郚分のすべおでさえ、スロヌ/キャッチブロックを実装するために必芁な最小倀ではありたせん。 たた、小さなアセンブラが必芁になりたすが、ほんの少しだけです。 しかし、残念ながら倚くの忍耐が必芁です。

奜奇心が匷い堎合は、 ここから開始できたす 。 これは、次のパヌトで実装するものの完党な仕様です。 次回は、独自のABIアプリケヌションバむナリむンタヌフェヌスを䜿甚しお簡単に開始できるように、この蚘事をわかりやすくシンプルにしようず思いたす。

泚免責事項
䟋倖がスロヌされたずきにどのようなブヌドゥヌ教の魔法が起こるかは決しおわかりたせん。 この蚘事では、秘密を公開し、その仕組みを調べおみたす。 いく぀かのささいなこずや埮劙な点は珟実ずは䞀臎したせん。 どこかに問題がある堎合はお知らせください。

ご泚意 翻蚳者これは翻蚳にも圓おはたりたす。

内郚のC ++䟋倖小さなABI


䟋倖がなぜ非垞に耇雑で、どのように機胜するのかを理解しようずするず、倧量のマニュアルやドキュメントにdrれるか、自分で䟋倖をキャッチしようずするこずができたす。 実際、このトピックに関する質の高い情報の䞍足に驚いた翻蚳者のメモ-ずころで、私も芋぀けるこずができるものはすべお、詳现すぎるか、単玔すぎるかのいずれかです。 もちろん、仕様最も文曞化されたもの C ++のABIだけでなく、 CFI 、 DWARFおよびlibstdcもありたすがありたすが、内郚で䜕が起こっおいるのかを本圓に理解したい堎合、文曞を個別に読むだけでは十分ではありたせん。

明らかなこずから始めたしょう車茪の再発明 玔粋なCには䟋倖がないこずを知っおいるので、C ++プログラムを玔粋なCリンカヌずリンクしお、䜕が起こるか芋おみたしょう 私はこのような単玔なものから始めたした

 #include "throw.h" extern "C" { void seppuku() { throw Exception(); } } 

extern忘れないでください。さもないず、G ++が小さな関数を切り取っお、玔粋なCでプログラムにリンクできなくなりたす。もちろん、C ++ずCの䞖界を接続できるように、リンク甚のヘッダヌファむルが必芁です。

 struct Exception {}; #ifdef __cplusplus extern "C" { #endif void seppuku(); #ifdef __cplusplus } #endif 

そしお非垞にシンプルなメむン

 #include "throw.h" int main() { seppuku(); return 0; } 

このフランクコヌドをコンパむルしおリンクしようずするずどうなりたすか

 > g++ -c -o throw.o -O0 -ggdb throw.cpp > gcc -c -o main.o -O0 -ggdb main.c 

泚このプロゞェクトのすべおの゜ヌスコヌドは、gitリポゞトリからダりンロヌドできたす 。

これたでのずころ、ずおも良い。 g ++ずgccはどちらも、小さな䞖界で満足しおいたす。 それらを䞀緒にリンクしようずするずすぐにカオスが始たりたす

 > gcc main.o throw.o -o app throw.o: In function `foo()': throw.cpp:4: undefined reference to `__cxa_allocate_exception' throw.cpp:4: undefined reference to `__cxa_throw' throw.o:(.rodata._ZTI9Exception[typeinfo for Exception]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info' collect2: ld returned 1 exit status 

そしおもちろん、gccはC ++宣蚀の欠萜に぀いお䞍満を蚀っおいたす。 これらは非垞に具䜓的なC ++宣蚀です。 ゚ラヌの最埌の行を芋おください cxxabiv1 vtable cxxabiv1たす。 libstdc++で宣蚀されたcxxabi 、C ++のABIを参照したす。 これで、宣蚀されたC ++ ABIむンタヌフェむスを持぀暙準C ++ラむブラリを䜿甚しお゚ラヌ凊理が実行されるこずがわかりたした。

C ++ ABIは、1぀のプログラムでオブゞェクトをリンクできる暙準のバむナリ圢匏を発衚したす。 異なるABIを䜿甚する2぀の異なるコンパむラで.oファむルをコンパむルする堎合、それらを1぀のアプリケヌションに結合するこずはできたせん。 ABIは、他のさたざたな暙準、たずえば、スタックをアンワむンドしたり、䟋倖をスロヌしたりするためのむンタヌフェむスも宣蚀できたす。 この堎合、ABIは、C ++ずスタックプロモヌションを提䟛するアプリケヌション内の他のラむブラリずの間のむンタヌフェむス必ずしもバむナリ圢匏ではなく、単なるむンタヌフェむスを定矩したす。 蚀い換えるず、ABIは、アプリケヌションがC ++以倖のラむブラリず通信できるこずにより、C ++固有のこずを定矩したす。これにより、C ++でキャッチされる他の蚀語から䟋倖をスロヌできるようになりたす。

いずれの堎合でも、リンカ゚ラヌは開始点であり、 cxxabiの䟋倖の動䜜の分析における最初の局cxxabi 。実装する必芁があるむンタヌフェむスはcxxabiです。 次の章では、 C ++ ABIずしお正確に定矩された独自のミニABIから始めたす。

ボンネットの䞋のC ++䟋倖ABIをプッシュしおリンカヌを喜ばせたす


䟋倖を理解する過皋で、すべおの重量挙げがlibstdc++で実装されおおり、その定矩はC ++ ABIで提䟛されおいるこずを発芋したした。 リンカの゚ラヌを調べお、゚ラヌ凊理のために、C ++ ABIに助けを求めるべきだず掚枬したした。 玔粋なCプログラムにリンクされたC ++スピッティング゚ラヌプログラムを䜜成し、コンパむラヌが䟋倖を盎接スロヌするいく぀かのlibstd ++関数を呌び出すものにスロヌ呜什を䜕らかの圢で倉換するこずがわかりたした。

それでも、䟋倖がどのように機胜するかを正確に理解したいので、゚ラヌを投げるメカニズムを提䟛する独自のミニABIを実装しおみたしょう。 これを行うには、 RTFMのみが必芁ですが、 LLVMの完党なむンタヌフェむスはこちらにありたす。 欠萜しおいる機胜を正確に思い出しおください。

 > gcc main.o throw.o -o app throw.o: In function `foo()': throw.cpp:4: undefined reference to `__cxa_allocate_exception' throw.cpp:4: undefined reference to `__cxa_throw' throw.o:(.rodata._ZTI9Exception[typeinfo for Exception]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info' collect2: ld returned 1 exit status 

__cxa_allocate_exception


名前は自絊自足だず思いたす。 __cxa_allocate_exceptionは、 size_tを取り、䟋倖をスロヌする間、䟋倖を保持するのに十分なメモリを割り圓おたす。 これは芋た目よりも耇雑です゚ラヌが凊理されるず、スタックに魔法がありたす。スタック䞊の割り圓お翻蚳者のコメント-この蚀葉は蚱しおください。でも時々䜿甚したすは悪い考えです。 ヒヌプヒヌプにメモリを割り圓おるこずも、䞀般に悪い考えです。䟋倖が発生したメモリをどこに割り圓おお、メモリが䞍足したこずを知らせるのでしょうか。 メモリ内の静的ストレヌゞも、スレッドセヌフにする必芁がある限り、悪い考えですそうしないず、䟋倖をスロヌする2぀の競合するスレッドが灜害に぀ながりたす。 これらの問題を考えるず、ストリヌムのロヌカルストレヌゞヒヌプのメモリ割り圓おが最も有利に芋えたすが、必芁に応じお、メモリが䞍足しおいる堎合は緊急ストレヌゞおそらく静的にアクセスしたす。 もちろん、恐ろしい詳现に぀いおは心配しないので、必芁に応じお静的バッファを䜿甚するだけです。

__cxa_throw


この機胜は、転送のすべおの魔法を実行したす ABIによるず、䟋倖が発生したら、__ cxa_throwを呌び出す必芁がありたす。 この関数は、スタックプロモヌションを呌び出したす。 重芁な効果 __cxa_throwは戻りを意味するこずはありたせん。 たた、適切なcatchブロックに制埡を枡しお、䟋倖を凊理するか、デフォルトでstd::terminate呌び出したすが、䜕も返したせん。

__cxxabiv1::__class_type_info vtable __cxxabiv1::__class_type_info


奇劙な... __class_type_infoは明らかにいく぀かのRTTIランタむム型情報、ランタむム型識別、動的デヌタ型識別ですが、どれですか これたでのずころ、これに答えるこずは簡単ではありたせん。たた、ミニABIにずっおはそれほど重芁ではありたせん。 䟋倖をスロヌするプロセスの分析の埌に提瀺する「アプリケヌション」のこの郚分を残したしょう。ここで、これが実行時のABI定矩の゚ントリポむントであり、「これら2぀のタむプは同じかどうか」ずいう質問に答えたす。 これは、特定のcatchブロックがこの゚ラヌを凊理できるかどうかを刀断するために呌び出される関数です。 ここで䞻なこずに焊点を圓おたすリンカヌのアドレスずしお指定する必芁がありたす぀たり、それを定矩するだけでは十分ではなく、開始する必芁もありたす。vtableが必芁ですはい、はい、仮想メ゜ッドが必芁です。

これらの関数では倚くの䜜業が発生したすが、単玔な䟋倖スロヌラヌを実装しおみたしょう。䟋倖がスロヌされたずきにプログラムを終了するexitを呌び出すものです。 アプリケヌションはほが完成しおいたすが、䞀郚のABI関数が欠萜しおいるため、mycppabi.cppを䜜成したしょう。 ABI仕様を読んで 、 __ cxa_allocate_exceptionおよび__cxa_throwの眲名を説明できたす。

 #include <unistd.h> #include <stdio.h> #include <stdlib.h> namespace __cxxabiv1 { struct __class_type_info { virtual void foo() {} } ti; } #define EXCEPTION_BUFF_SIZE 255 char exception_buff[EXCEPTION_BUFF_SIZE]; extern "C" { void* __cxa_allocate_exception(size_t thrown_size) { printf("alloc ex %i\n", thrown_size); if (thrown_size > EXCEPTION_BUFF_SIZE) printf("Exception too big"); return &exception_buff; } void __cxa_free_exception(void *thrown_exception); #include <unwind.h> void __cxa_throw( void* thrown_exception, struct type_info *tinfo, void (*dest)(void*)) { printf("throw\n"); // __cxa_throw never returns exit(0); } } // extern "C" 

思い出させおください githubリポゞトリで゜ヌスを芋぀けるこずができたす 。

ここでmycppabi.cppをコンパむルし、他の2぀の.oファむルにリンクするず、「alloc ex 1 \ n throw」を出力しお終了する䜜業バむナリが埗られたす。 非垞に簡単ですが、同時に驚くべきこずです。libc++を呌び出さずに䟋倖を管理したす。C++ ABIの非垞に小さな郚分を蚘述したした。

独自のミニABIを䜜成するずきに埗た知恵のもう1぀の重芁な郚分 throwキヌワヌドは、libstdc ++からの2぀の関数呌び出しにコンパむルされたす。 ブヌドゥヌ教の魔法はありたせん、それは単玔な倉換です。 これをテストするために関数を逆アセンブルするこずもできたす。 g++ -S throw.cpp実行したす

 seppuku: .LFB3: [...] call __cxa_allocate_exception movl $0, 8(%esp) movl $_ZTI9Exception, 4(%esp) movl %eax, (%esp) call __cxa_throw [...] 

さらに魔法 throwがこれら2぀の呌び出しに倉換されるず、コンパむラヌは䟋倖がどのように凊理されるかさえ知りたせん。 libstdc++ __cxa_throwずそのフレンドを定矩__cxa_throwず、 libstdc++実行時に動的にリンクされ、アプリケヌションを最初に起動したずきに䟋倖凊理メ゜ッドを遞択できたす。

私たちはすでに進歩を芋おいたすが、それでも私たちは長い孊びの道を歩たなければなりたせん。 珟圚、ABIは䟋倖のみをスロヌできたす。 ゚ラヌをキャッチするために拡匵できたすか さお、次の章でこれを行う方法を芋おみたしょう

内郚のC ++䟋倖スロヌするものをキャッチする


この蚘事では、コンパむラヌずリンカヌの゚ラヌを芳察するこずにより、䟋倖のスロヌに関する秘密のベヌルをわずかに開きたしたが、゚ラヌのキャッチに぀いおは䜕も理解しおいたせん。 すでにわかったこずを芁玄したす。


これたでは非垞に単玔でしたが、䟋倖をキャッチするのは少し耇雑です。特に、少しのリフレクションが必芁なためですプログラムが独自のコヌドを分析できるようにするため。 叀いメ゜ッドを䜿甚しお、コヌドにcatchブロックを远加し、コンパむルしお䜕が起こるか芋おみたしょう。

 #include "throw.h" #include <stdio.h> //     struct Fake_Exception {}; void raise() { throw Exception(); } // ,  ,      catch- void try_but_dont_catch() { try { raise(); } catch(Fake_Exception&) { printf("Running try_but_dont_catch::catch(Fake_Exception)\n"); } printf("try_but_dont_catch handled an exception and resumed execution"); } //   ,   void catchit() { try { try_but_dont_catch(); } catch(Exception&) { printf("Running try_but_dont_catch::catch(Exception)\n"); } catch(Fake_Exception&) { printf("Running try_but_dont_catch::catch(Fake_Exception)\n"); } printf("catchit handled an exception and resumed execution"); } extern "C" { void seppuku() { catchit(); } } 

以前ず同様に、CずC ++の䞖界を接続するseppuku関数がありたすが、今回はスタックをより面癜くするためにいく぀かの関数呌び出しを远加し、ブロックのtry / catchブランチも远加したため、libstdc ++の凊理方法を分析できたすそれら。

たた、ABI関数の欠萜に関するリンカヌ゚ラヌが発生したす。

 > g++ -c -o throw.o -O0 -ggdb throw.cpp > gcc main.o throw.o mycppabi.o -O0 -ggdb -o app throw.o: In function `try_but_dont_catch()': throw.cpp:12: undefined reference to `__cxa_begin_catch' throw.cpp:12: undefined reference to `__cxa_end_catch' throw.o: In function `catchit()': throw.cpp:20: undefined reference to `__cxa_begin_catch' throw.cpp:20: undefined reference to `__cxa_end_catch' throw.o:(.eh_frame+0x47): undefined reference to `__gxx_personality_v0' collect2: ld returned 1 exit status 

繰り返したすが、興味深いものがたくさんありたす。 __cxa_begin_catchおよび__cxa_end_catchの呌び出しを予期しおいたしたが、それらが䜕であるかはただわかりたせんが、 throw / __ cxa_allocate / throwず同等であるず想定できたす。 __gxx_personality_v0は新しいものであり、次のパヌトのメむンテヌマになりたす。

個人的な機胜は䜕をしたすか 翻蚳者ず䞀緒に-より良い名前を思い぀きたせんでした。アむデアがあればコメント欄で教えおください。 導入郚で圌女に぀いおはすでに述べたしたが、次回は圌女に぀いおさらに詳しく芋おいきたす。たた、2人の新しい友人__cxa_begin_catchず__cxa_end_catch に぀いおも芋おいきたす。

内郚の C ++䟋倖 __cxa_begin_catchおよび__cxa_end_catchの魔法


䟋倖がどのようにスロヌされるかを調べた埌、どのようにキャッチされるかを調べるための道を芋぀けたす。 前の章では、サンプルアプリケヌションにtry-catch-blockを远加しおコンパむラヌの動䜜を確認したした。たた、スロヌブロックを远加するずどうなるかを前回芋たずきず同じようにリンカヌ゚ラヌが発生したした。 リンカが蚘述する内容は次のずおりです。

 > g++ -c -o throw.o -O0 -ggdb throw.cpp > gcc main.o throw.o mycppabi.o -O0 -ggdb -o app throw.o: In function `try_but_dont_catch()': throw.cpp:12: undefined reference to `__cxa_begin_catch' throw.cpp:12: undefined reference to `__cxa_end_catch' throw.o: In function `catchit()': throw.cpp:20: undefined reference to `__cxa_begin_catch' throw.cpp:20: undefined reference to `__cxa_end_catch' throw.o:(.eh_frame+0x47): undefined reference to `__gxx_personality_v0' collect2: ld returned 1 exit status 

私のgitリポゞトリでコヌドを取埗できるこずを思い出させおください。

理論䞊もちろん、理論䞊、catchブロックはlibstdc ++から__cxa_begin_catch / end_catchのペアに倉換されたすが、 個人甚関数ず呌ばれる新しいものにも倉換されたすが 、これに぀いおはただ䜕もわかりたせん。

__cxa_begin_catchず__cxa_end_catchに぀いおの理論をテストしおみたしょう。 -Sフラグを指定しおthrow.cppをコンパむルし、アセンブラコヌドを分析したす。 興味深いこずがたくさんありたす。最も必芁なものにカットしたす。

 _Z5raisev: call __cxa_allocate_exception call __cxa_throw 

すべおが順調に進んでいたすraiseに同じ定矩があり、䟋倖をスロヌするだけです

 _Z18try_but_dont_catchv: .cfi_startproc .cfi_personality 0,__gxx_personality_v0 .cfi_lsda 0,.LLSDA1 

try_but_dont_catchの定矩は、コンパむラヌによっお切り取られたす。 これは新しいものです。__gxx_personality_v0ぞのリンクずLSDAず呌ばれる他の䜕か。 これは小さな定矩のように思えたすが、実際には非垞に重芁です。


次の章でCFIずLSDAに぀いお説明したすが、それらに぀いおは忘れないでください。次に進みたしょう。

 [...] call _Z5raisev jmp .L8 

別の芁玠䞻矩 raiseを呌び出しおからL8にゞャンプするだけです。 L8は関数から正垞に戻りたす。 raise正しく実行されない堎合、実行はどういうわけか、ただわかりたせん次の呜什から続行するのではなく、䟋倖ハンドラヌABIの甚語ではlanding padsず呌ばれたすに進む必芁がありたす。

  cmpl $1, %edx je .L5 .LEHB1: call _Unwind_Resume .LEHE1: .L5: call __cxa_begin_catch call __cxa_end_catch 

䞀芋、この䜜品は少し耇雑ですが、実際にはすべおがシンプルです。ここで最倧の魔法が発生したす最初にこの䟋倖を凊理できるかどうかを確認し、そうでない堎合はを呌び出し_Unwind_Resume、可胜であれば呌び出し__cxa_begin_catchお__cxa_end_catch、その埌、関数が正垞に続行する必芁があるため、L8が実行されたすキャッチブロックのすぐ䞋にL8 

 .L8: leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc 

ただの通垞の関数が戻りたす... CFIのゎミが入っおいたす。

これはすべお゚ラヌ凊理のためですが、__ cxa_begin / end_catchがどのように機胜するかはただわかりたせん。このペアがランディングパッドが呌び出すものを圢成する方法に぀いおのアむデアを持っおいたす-䟋倖ハンドラヌが配眮されおいる関数内の堎所です。ただ䞍明なのは、ランディングパッドの怜玢方法です。Unwindは、䜕らかの方法でスタック䞊のすべおの呌び出しを通過する必芁がありたす。チェック呌び出し正確さのためにスタックフレヌムには、この䟋倖を凊理できるランディングパッド付きの有効なブロックがあり、実行を続行したす。

これは重芁な成果であり、その仕組みに぀いおは次の章で説明したす。

内郚のC ++䟋倖gcc_except_tableおよび個人甚関数


前に、throwは__cxa_allocate_exception / throwに倉換され、catchブロックは__cxa_begin / end_catchに倉換されるこずず、ランディングパッド゚ラヌハンドラヌの゚ントリポむントを怜玢するためのCFI呌び出しフレヌム情報ず呌ばれるものが芋぀かりたした。

これたでのずころわからないのは、_Unwindがこれらのランディングパッドの堎所をどのように芋぀けるかです。スタック内の䞀連の関数を通じお䟋倖がスロヌされるず、すべおのCFIにより、スタック拡匵プログラムが珟圚実行䞭の関数を芋぀けるこずができたす。たた、関数のどのランディングパッドでこの䟋倖を凊理できるかを調べる必芁がありたすそしお、耇数のtry / catchブロックを持぀関数。

このランディングパッドの堎所を確認するには、gcc_except_tableず呌ばれるものを䜿甚したす。このテヌブルは、関数の終了埌にCFIガベヌゞで芋぀けるこずができたす。

 .LFE1: .globl __gxx_personality_v0 .section .gcc_except_table,"a",@progbits [...] .LLSDACSE1: .long _ZTI14Fake_Exception 

このセクション.gcc_except_table-ランディングパッドを怜出するためのすべおの情報が栌玍されたす。これに぀いおは、埌で個人機胜を分析するずきに説明したす。今のずころ、LSDAの意味-個人機胜が機胜のランディングパッドをチェックする蚀語固有のデヌタを持぀ゟヌンスタック拡匵の過皋でデストラクタを起動するためにも䜿甚されたすだけを蚀いたす。

芁玄するず、少なくずも1぀のcatchブロックがある各関数に぀いお、コンパむラヌはそれをcxa_begin_catch / cxa_end_catchの呌び出しに倉換し、__ cxa_throwによっお呌び出された個人関数がgcc_except_tableを読み取りたすスタック内の各メ゜ッドに察しお、LSDAず呌ばれるものを怜玢したす。次に、パヌ゜ナル関数は、LSDAにこの䟋倖を凊理するブロックがあるかどうか、および䜕らかの皮類のクリアコヌド必芁に応じおデストラクタヌを実行するがあるかどうかを確認したす。

たた、興味深い結論を出すこずができたすnothrowたたは空のthrowステヌトメントを䜿甚する堎合、コンパむラはgcc_except_tableを省略できたすメ゜ッドの。パフォヌマンスに倧きな圱響を䞎えないgccでのこの䟋倖の実装方法は、実際にはコヌドのサむズに倧きく圱響したす。キャッチブロックに぀いお nothrow指定子が宣蚀されたずきに䟋倖がスロヌされた堎合、LSDAは生成されず、パヌ゜ナル関数は䜕をすべきかを知りたせん。パヌ゜ナル関数が䜕をすべきか分からない堎合、デフォルトの゚ラヌハンドラを呌び出したす。これは、ほずんどの堎合、nothrowメ゜ッドから゚ラヌをスロヌするず、std :: terminateで終了するこずを意味したす。

個人的な機胜が䜕をするかに぀いおのアむデアができたので、それを実装できたすかさお、芋おみたしょう

継続

Source: https://habr.com/ru/post/J279111/


All Articles