6

Objective-Cの低レベル実行時ヘッダー(/usr/include/objc)には、objc-exceptions.hというファイルがあります。これは、ObjCコンパイラが@try/@catchをどのように実装したかのようです。Objective-Cの@ try- @ catch実装が実行時にどのように実行されるかの例?

"クラスに送信されたセレクタが認識できない"例外を捕捉するために、これらの関数を手動で呼び出そうとしています(ObjCランタイムと実装の実験用)。

基本的に、私が探しているのは、低レベルのランタイム機能を使用して@try/@catchを実行する方法の例です。前もって感謝します!

+0

obj-C++を考慮すると、スタックを巻き戻したいと思いますか? – hamstergene

+0

ユージン、私は私が従うかどうかはわかりません...私は、uncaughtExceptionのようなものの例を探しています。ここで私が理解できたのは、https://gist.github.com/1073294#file_uncaught_exceptionです。しかし、私は 'begin_try'や' end_try'のようなものを推測しています。 – TooTallNate

+0

try/catchブロックのアセンブリは実際にobjc_begin_catchとobjc_end_catchの呼び出しを示します。あなたはそれを見て、どのように呼び出されているか見てみましたか? –

答えて

7

したがって、ランタイムが例外処理をどのようにしているか知りたいですか?

ご了承ください。

だからです。 ObjCには例外処理ABIがありません。あなたがすでに見つけたSPIだけです。間違いなくObjective-C例外ABIが実際にC++ exception handling ABIと全く同じものであることも発見しました。そのために、いくつかのコードを始めましょう。

#include <Foundation/Foundation.h> 

int main(int argc, char **argv) { 
    @try { 
     @throw [NSException exceptionWithName:@"ExceptionalCircumstances" reason:@"Drunk on power" userInfo:nil]; 
    } @catch(...) { 
     NSLog(@"Catch"); 
    } @finally { 
     NSLog(@"Finally"); 
    } 
} 

-ObjC -O3と打ち鳴らす介して実行(およびデバッグ情報の嫌な量を剥奪)私たちは、この取得:にObjC++ 何もを変更しないと、あなたがそれをコンパイルする場合

_main:         ## @main 
    push rbp 
    mov rbp, rsp 
    push r14 
    push rbx 

    mov rdi, qword ptr [rip + L_OBJC_CLASSLIST_REFERENCES_$_] 
    mov rsi, qword ptr [rip + L_OBJC_SELECTOR_REFERENCES_] 

    lea rdx, qword ptr [rip + L__unnamed_cfstring_] 
    lea rcx, qword ptr [rip + L__unnamed_cfstring_2] 
    xor r8d, r8d 
    call qword ptr [rip + [email protected]] 

    mov rdi, rax 
    call _objc_exception_throw 
LBB0_2: 
    mov rdi, rax 
    call _objc_begin_catch 

    lea rdi, qword ptr [rip + L__unnamed_cfstring_4] 
    xor eax, eax 
    call _NSLog 

    call _objc_end_catch 

    xor ebx, ebx 
LBB0_8: 
    lea rdi, qword ptr [rip + L__unnamed_cfstring_6] 
    xor eax, eax 
    call _NSLog 

    test bl, bl 
    jne LBB0_10 
LBB0_11: 
    xor eax, eax 
    pop rbx 
    pop r14 
    pop rbp 
    ret 
LBB0_5: 
    mov rbx, rax 
    call _objc_end_catch 
    jmp LBB0_7 
LBB0_6: 
    mov rbx, rax 
LBB0_7: 
    mov rdi, rbx 
    call _objc_begin_catch 
    mov bl, 1 
    jmp LBB0_8 
LBB0_12: 
    mov r14, rax 
    test bl, bl 
    je LBB0_14 
    jmp LBB0_13 
LBB0_10: 
    call _objc_exception_rethrow 
    jmp LBB0_11 
LBB0_16:        ## %.thread 
    mov r14, rax 
LBB0_13: 
    call _objc_end_catch 
LBB0_14: 
    mov rdi, r14 
    call __Unwind_Resume 
LBB0_15: 
    call _objc_terminate 

を。 (まあ、それは完全に真実ではありません。最後の_objc_terminateは、clangの個人的な___clang_call_terminateルーチンへのジャンプに変わります)。とにかく、このコードは3つの重要なセクションに分けられます。最初は_mainからLBB0_2の開始点までです。またはtryブロックが発生します。例外的に例外がスローされ、tryブロックにキャッチされているため、コンパイラはLBB0_2周辺のブランチを削除し、キャッチハンドラにまっすぐ移動しました。この時点で、Objective-C、またはより正確にCoreFoundationは、私たちのために例外オブジェクトを設定しており、libC++は、必要な巻き戻し段階で例外ハンドラを検索し始めました。

コードの2番目に重要なブロックはLBB0_2から私たちのcatchfinallyブロックが住んLBB0_11の終わりにあります。すべてがうまくなっているので、この下のコードはすべて死んでいます(そして、うまくいけばリリースで取り除かれます)が、それはそうではないと想像してみましょう。

3番目の部分は、コンパイラがNSLogからのジャンプをLBB0_2に送り出したところのLBB0_8です。私たちが例外を捕まえようとしなかったような何か愚かなことをしたら、このハンドラは、objc_begin_catchを呼び出した後にちょっと反転して、retの周りを分岐し、ボールを落としたハンドラを知らせるobjc_exception_rethrow()に移動し、ハンドラの検索を続けます。もちろん、私たちはメインなので、他のハンドラはありません。std::terminateが呼び出されます。

これは、あなたが手でこのものを書き込もうとすると悪い時を過すと言います。 __cxa_*とObjC SPI関数はすべて、頼りにいらない方法で例外オブジェクトを投げ込みます。very tight orderで放出されたハンドラは、C++ ABI契約が満たされているかどうかを確認するために使用されます。std::terminate呼び出される。能動的なリスニングの役割を果たす場合は、自分自身の機能でredefine the exception handling stuffを許可し、Objective-Cはobjc_setUncaughtExceptionHandlerobjc_setExceptionMatcherobjc_setExceptionPreprocessorを持ちます。

関連する問題