6

W nagłówkach środowiska wykonawczego niskiego poziomu Objective-C (/usr/include/objc) znajduje się plik objc-exceptions.h. Wydaje się, że jest to sposób implementacji kompilatora ObjC przez @try/.Przykład wykonania implementacji @ [email protected] catch Objective-C w środowisku wykonawczym?

Próbuję wywoływać te funkcje ręcznie (w przypadku eksperymentów z uruchomieniem i implementacją ObjC) w celu wychwycenia wyjątku "nierozpoznany selektor wysłany do klasy".

Zasadniczo wszystko, czego szukam, jest przykładem wykonania @try/@catch przy użyciu funkcji środowiska wykonawczego niskiego poziomu. Z góry dziękuję!

+0

Biorąc pod uwagę obj-C++, czy chcesz też samodzielnie rozwijać stos? – hamstergene

+0

Eugene, nie jestem pewien, czy podążam ... Szukam przykładu takiego jak UncaughtException, którego udało mi się wymyślić tutaj: https://gist.github.com/1073294#file_uncaught_exception. m, ale domyślam się czegoś bardziej przypominającego rodzaj "begin_try" i "end_try". – TooTallNate

+0

Zestawienie bloku try/catch rzeczywiście pokazuje połączenia z objc_begin_catch i objc_end_catch. Czy próbowałeś przez to przejrzeć, aby zobaczyć, jak są wywoływane? –

Odpowiedz

7

Chcesz się dowiedzieć, w jaki sposób środowisko wykonawcze obsługuje wyjątki?

Przygotuj się na rozczarowanie.

Ponieważ tak się nie dzieje. ObjC nie ma ABI obsługującej wyjątki, tylko SPI, które już znalazłeś. Bez wątpienia odkryłeś również, że wyjątek ABI w Objective-C jest dokładnie taki sam jak C++ exception handling ABI. W tym celu zacznijmy od jakiegoś kodu.

#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"); 
    } 
} 

Run poprzez brzękiem z -ObjC -O3 (i pozbawiony obrzydliwe ilości informacji debugowania) otrzymujemy w ten sposób:

_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 

Jeśli skompilować go z ObjC++ nic się nie zmieni. (Cóż, to nie do końca prawda. Ostatni _objc_terminate zamienia się w skok w osobistą rutynę Clang'a ___clang_call_terminate). W każdym razie ten kod można podzielić na 3 ważne sekcje. Pierwszy z nich to _main na początek LBB0_2 lub tam, gdzie ma miejsce nasz blok try. Ponieważ rażąco rzucając wyjątek i łapie go w naszym bloku try, kompilator posunął się naprzód i usunął gałąź wokół LBB0_2 i przeniósł się bezpośrednio do uchwytów catch. W tym momencie Objective-C lub dokładniej CoreFoundation ustawił dla nas obiekt wyjątku, a libC++ rozpoczęło wyszukiwanie procedury obsługi wyjątku podczas wymaganej fazy rozwijania.

Drugi ważny blok kodu to LBB0_2 na koniec LBB0_11, w którym znajdują się nasze bloki catch i finally. Ponieważ wszystko jest dobrze, cały kod pod nim jest martwy (i miejmy nadzieję, że zostanie usunięty), ale wyobraźmy sobie, że tak nie było.

Trzecia część pochodzi z LBB0_8 na dole, gdzie kompilator wysłałby skok z NSLog w LBB0_2, gdybyśmy zrobili coś głupiego, powiedzmy, próbowali nie złapać naszego wyjątku. Ten handler odwraca się nieco po wywołaniu w objc_begin_catch, co powoduje, że rozgałęziamy się wokół ret i przechodzimy na objc_exception_rethrow(), który mówi instruktorowi odwijającemu, że upuszczamy piłkę i kontynuujemy wyszukiwanie handlerów w innym miejscu. Oczywiście, jesteśmy główni, więc nie ma innych uchwytów, a gdy wychodzimy, wywoływane jest std::terminate.

To wszystko, jeśli chcesz spróbować pisać te rzeczy ręcznie. Wszystkie funkcje __cxa_* i ObjC SPI rzucają wokół obiektów wyjątków w sposób, na który nie można polegać, i (raczej pesymistycznie wiele) procedur obsługi są emitowane w very tight order, aby upewnić się, że kontrakt ABI ABI jest spełniony, ponieważ jeśli to nie jest specyfikacja specyfikacji std::terminate Zostać wezwanym.Jeśli chcesz wziąć czynny udział w słuchaniu, możesz mieć redefine the exception handling stuff z własnymi funkcjami, a Objective-C ma objc_setUncaughtExceptionHandler, objc_setExceptionMatcherobjc_setExceptionPreprocessor.

Powiązane problemy