123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434 |
- /*
- * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
- #if !__OBJC2__
- /***********************************************************************
- * 32-bit implementation
- **********************************************************************/
- #include "objc-private.h"
- #include <stdlib.h>
- #include <setjmp.h>
- #include <execinfo.h>
- #include "objc-exception.h"
- static objc_exception_functions_t xtab;
- // forward declaration
- static void set_default_handlers();
- /*
- * Exported functions
- */
- // get table; version tells how many
- void objc_exception_get_functions(objc_exception_functions_t *table) {
- // only version 0 supported at this point
- if (table && table->version == 0)
- *table = xtab;
- }
- // set table
- void objc_exception_set_functions(objc_exception_functions_t *table) {
- // only version 0 supported at this point
- if (table && table->version == 0)
- xtab = *table;
- }
- /*
- * The following functions are
- * synthesized by the compiler upon encountering language constructs
- */
-
- void objc_exception_throw(id exception) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
-
- if (PrintExceptionThrow) {
- _objc_inform("EXCEPTIONS: throwing %p (%s)",
- (void*)exception, object_getClassName(exception));
- void* callstack[500];
- int frameCount = backtrace(callstack, 500);
- backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
- }
- OBJC_RUNTIME_OBJC_EXCEPTION_THROW(exception); // dtrace probe to log throw activity.
- xtab.throw_exc(exception);
- _objc_fatal("objc_exception_throw failed");
- }
- void objc_exception_try_enter(void *localExceptionData) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- xtab.try_enter(localExceptionData);
- }
- void objc_exception_try_exit(void *localExceptionData) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- xtab.try_exit(localExceptionData);
- }
- id objc_exception_extract(void *localExceptionData) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- return xtab.extract(localExceptionData);
- }
- int objc_exception_match(Class exceptionClass, id exception) {
- if (!xtab.throw_exc) {
- set_default_handlers();
- }
- return xtab.match(exceptionClass, exception);
- }
- // quick and dirty exception handling code
- // default implementation - mostly a toy for use outside/before Foundation
- // provides its implementation
- // Perhaps the default implementation should just complain loudly and quit
- extern void _objc_inform(const char *fmt, ...);
- typedef struct { jmp_buf buf; void *pointers[4]; } LocalData_t;
- typedef struct _threadChain {
- LocalData_t *topHandler;
- objc_thread_t perThreadID;
- struct _threadChain *next;
- }
- ThreadChainLink_t;
- static ThreadChainLink_t ThreadChainLink;
- static ThreadChainLink_t *getChainLink() {
- // follow links until thread_self() found (someday) XXX
- objc_thread_t self = thread_self();
- ThreadChainLink_t *walker = &ThreadChainLink;
- while (walker->perThreadID != self) {
- if (walker->next != nil) {
- walker = walker->next;
- continue;
- }
- // create a new one
- // XXX not thread safe (!)
- // XXX Also, we don't register to deallocate on thread death
- walker->next = (ThreadChainLink_t *)malloc(sizeof(ThreadChainLink_t));
- walker = walker->next;
- walker->next = nil;
- walker->topHandler = nil;
- walker->perThreadID = self;
- }
- return walker;
- }
- static void default_try_enter(void *localExceptionData) {
- LocalData_t *data = (LocalData_t *)localExceptionData;
- ThreadChainLink_t *chainLink = getChainLink();
- data->pointers[1] = chainLink->topHandler;
- chainLink->topHandler = data;
- if (PrintExceptions) _objc_inform("EXCEPTIONS: entered try block %p\n", chainLink->topHandler);
- }
- static void default_throw(id value) {
- ThreadChainLink_t *chainLink = getChainLink();
- LocalData_t *led;
- if (value == nil) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: objc_exception_throw with nil value\n");
- return;
- }
- if (chainLink == nil) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: No handler in place!\n");
- return;
- }
- if (PrintExceptions) _objc_inform("EXCEPTIONS: exception thrown, going to handler block %p\n", chainLink->topHandler);
- led = chainLink->topHandler;
- chainLink->topHandler = (LocalData_t *)
- led->pointers[1]; // pop top handler
- led->pointers[0] = value; // store exception that is thrown
- #if TARGET_OS_WIN32
- longjmp(led->buf, 1);
- #else
- _longjmp(led->buf, 1);
- #endif
- }
-
- static void default_try_exit(void *led) {
- ThreadChainLink_t *chainLink = getChainLink();
- if (!chainLink || led != chainLink->topHandler) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: *** mismatched try block exit handlers\n");
- return;
- }
- if (PrintExceptions) _objc_inform("EXCEPTIONS: removing try block handler %p\n", chainLink->topHandler);
- chainLink->topHandler = (LocalData_t *)
- chainLink->topHandler->pointers[1]; // pop top handler
- }
- static id default_extract(void *localExceptionData) {
- LocalData_t *led = (LocalData_t *)localExceptionData;
- return (id)led->pointers[0];
- }
- static int default_match(Class exceptionClass, id exception) {
- //return [exception isKindOfClass:exceptionClass];
- Class cls;
- for (cls = exception->getIsa(); nil != cls; cls = cls->superclass)
- if (cls == exceptionClass) return 1;
- return 0;
- }
- static void set_default_handlers() {
- objc_exception_functions_t default_functions = {
- 0, default_throw, default_try_enter, default_try_exit, default_extract, default_match };
- // should this always print?
- if (PrintExceptions) _objc_inform("EXCEPTIONS: *** Setting default (non-Foundation) exception mechanism\n");
- objc_exception_set_functions(&default_functions);
- }
- void exception_init(void)
- {
- // nothing to do
- }
- void _destroyAltHandlerList(struct alt_handler_list *list)
- {
- // nothing to do
- }
- // !__OBJC2__
- #else
- // __OBJC2__
- /***********************************************************************
- * 64-bit implementation.
- **********************************************************************/
- #include "objc-private.h"
- #include <objc/objc-abi.h>
- #include <objc/objc-exception.h>
- #include <objc/NSObject.h>
- #include <execinfo.h>
- // unwind library types and functions
- // Mostly adapted from Itanium C++ ABI: Exception Handling
- // http://www.codesourcery.com/cxx-abi/abi-eh.html
- struct _Unwind_Exception;
- struct _Unwind_Context;
- typedef int _Unwind_Action;
- enum : _Unwind_Action {
- _UA_SEARCH_PHASE = 1,
- _UA_CLEANUP_PHASE = 2,
- _UA_HANDLER_FRAME = 4,
- _UA_FORCE_UNWIND = 8
- };
- typedef int _Unwind_Reason_Code;
- enum : _Unwind_Reason_Code {
- _URC_NO_REASON = 0,
- _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
- _URC_FATAL_PHASE2_ERROR = 2,
- _URC_FATAL_PHASE1_ERROR = 3,
- _URC_NORMAL_STOP = 4,
- _URC_END_OF_STACK = 5,
- _URC_HANDLER_FOUND = 6,
- _URC_INSTALL_CONTEXT = 7,
- _URC_CONTINUE_UNWIND = 8
- };
- struct dwarf_eh_bases
- {
- uintptr_t tbase;
- uintptr_t dbase;
- uintptr_t func;
- };
- OBJC_EXTERN uintptr_t _Unwind_GetIP (struct _Unwind_Context *);
- OBJC_EXTERN uintptr_t _Unwind_GetCFA (struct _Unwind_Context *);
- OBJC_EXTERN uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *);
- // C++ runtime types and functions
- // copied from cxxabi.h
- OBJC_EXTERN void *__cxa_allocate_exception(size_t thrown_size);
- OBJC_EXTERN void __cxa_throw(void *exc, void *typeinfo, void (*destructor)(void *)) __attribute__((noreturn));
- OBJC_EXTERN void *__cxa_begin_catch(void *exc);
- OBJC_EXTERN void __cxa_end_catch(void);
- OBJC_EXTERN void __cxa_rethrow(void);
- OBJC_EXTERN void *__cxa_current_exception_type(void);
- #if SUPPORT_ZEROCOST_EXCEPTIONS
- # define CXX_PERSONALITY __gxx_personality_v0
- #else
- # define CXX_PERSONALITY __gxx_personality_sj0
- #endif
- OBJC_EXTERN _Unwind_Reason_Code
- CXX_PERSONALITY(int version,
- _Unwind_Action actions,
- uint64_t exceptionClass,
- struct _Unwind_Exception *exceptionObject,
- struct _Unwind_Context *context);
- // objc's internal exception types and data
- struct objc_typeinfo {
- // Position of vtable and name fields must match C++ typeinfo object
- const void ** __ptrauth_cxx_vtable_pointer vtable; // objc_ehtype_vtable+2
- const char *name; // c++ typeinfo string
- Class cls_unremapped;
- };
- struct objc_exception {
- id obj;
- struct objc_typeinfo tinfo;
- };
- extern "C" void _objc_exception_noop(void) { }
- extern "C" bool _objc_exception_false(void) { return 0; }
- // extern "C" bool _objc_exception_true(void) { return 1; }
- extern "C" void _objc_exception_abort1(void) {
- _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 1);
- }
- extern "C" void _objc_exception_abort2(void) {
- _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 2);
- }
- extern "C" void _objc_exception_abort3(void) {
- _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 3);
- }
- extern "C" void _objc_exception_abort4(void) {
- _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 4);
- }
- extern "C" bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
- struct objc_typeinfo *throw_tinfo,
- void **throw_obj_p,
- unsigned outer);
- // C++ pointers to vtables are signed with no extra data.
- // C++ vtable entries are signed with a number derived from the function name.
- // For this fake vtable, we hardcode number as deciphered from the
- // assembly output during libc++abi's build.
- #if __has_feature(ptrauth_calls)
- # define VTABLE_PTR_AUTH "@AUTH(da, 0)"
- # define VTABLE_ENTRY_AUTH(x) "@AUTH(ia," #x ",addr)"
- #else
- # define VTABLE_PTR_AUTH ""
- # define VTABLE_ENTRY_AUTH(x) ""
- #endif
- #if __LP64__
- # define PTR ".quad "
- # define TWOPTRSIZE "16"
- #else
- # define PTR ".long "
- # define TWOPTRSIZE "8"
- #endif
- // Hand-built vtable for objc exception typeinfo.
- // "OLD" is GNU libcpp, "NEW" is libc++abi.
- asm(
- "\n .cstring"
- "\n l_.id_str: .asciz \"id\""
- "\n .section __DATA,__const"
- "\n .globl _OBJC_EHTYPE_id"
- "\n .globl _objc_ehtype_vtable"
- "\n .p2align 4"
- "\n _OBJC_EHTYPE_id:"
- "\n " PTR "(_objc_ehtype_vtable+" TWOPTRSIZE ") " VTABLE_PTR_AUTH
- "\n " PTR "l_.id_str"
- "\n " PTR "0"
- "\n _objc_ehtype_vtable:"
- "\n " PTR "0"
- // typeinfo's typeinfo - fixme hack
- "\n " PTR "_OBJC_EHTYPE_id"
- // destructor and in-place destructor
- "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(52634)
- "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(10344)
- // OLD __is_pointer_p
- "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(6889)
- // OLD __is_function_p
- "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(23080)
- // OLD __do_catch, NEW can_catch
- "\n " PTR "__objc_exception_do_catch" VTABLE_ENTRY_AUTH(27434)
- // OLD __do_upcast, NEW search_above_dst
- "\n " PTR "__objc_exception_false" VTABLE_ENTRY_AUTH(48481)
- // NEW search_below_dst
- "\n " PTR "__objc_exception_false" VTABLE_ENTRY_AUTH(41165)
- // NEW has_unambiguous_public_base (fixme need this?)
- "\n " PTR "__objc_exception_abort1" VTABLE_ENTRY_AUTH(14357)
- // paranoia: die if libcxxabi adds anything else
- "\n " PTR "__objc_exception_abort2"
- "\n " PTR "__objc_exception_abort3"
- "\n " PTR "__objc_exception_abort4"
- );
- /***********************************************************************
- * Foundation customization
- **********************************************************************/
- /***********************************************************************
- * _objc_default_exception_preprocessor
- * Default exception preprocessor. Expected to be overridden by Foundation.
- **********************************************************************/
- static id _objc_default_exception_preprocessor(id exception)
- {
- return exception;
- }
- static objc_exception_preprocessor exception_preprocessor = _objc_default_exception_preprocessor;
- /***********************************************************************
- * _objc_default_exception_matcher
- * Default exception matcher. Expected to be overridden by Foundation.
- **********************************************************************/
- static int _objc_default_exception_matcher(Class catch_cls, id exception)
- {
- Class cls;
- for (cls = exception->getIsa();
- cls != nil;
- cls = cls->superclass)
- {
- if (cls == catch_cls) return 1;
- }
- return 0;
- }
- static objc_exception_matcher exception_matcher = _objc_default_exception_matcher;
- /***********************************************************************
- * _objc_default_uncaught_exception_handler
- * Default uncaught exception handler. Expected to be overridden by Foundation.
- **********************************************************************/
- static void _objc_default_uncaught_exception_handler(id exception)
- {
- }
- static objc_uncaught_exception_handler uncaught_handler = _objc_default_uncaught_exception_handler;
- /***********************************************************************
- * objc_setExceptionPreprocessor
- * Set a handler for preprocessing Objective-C exceptions.
- * Returns the previous handler.
- **********************************************************************/
- objc_exception_preprocessor
- objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
- {
- objc_exception_preprocessor result = exception_preprocessor;
- exception_preprocessor = fn;
- return result;
- }
- /***********************************************************************
- * objc_setExceptionMatcher
- * Set a handler for matching Objective-C exceptions.
- * Returns the previous handler.
- **********************************************************************/
- objc_exception_matcher
- objc_setExceptionMatcher(objc_exception_matcher fn)
- {
- objc_exception_matcher result = exception_matcher;
- exception_matcher = fn;
- return result;
- }
- /***********************************************************************
- * objc_setUncaughtExceptionHandler
- * Set a handler for uncaught Objective-C exceptions.
- * Returns the previous handler.
- **********************************************************************/
- objc_uncaught_exception_handler
- objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
- {
- objc_uncaught_exception_handler result = uncaught_handler;
- uncaught_handler = fn;
- return result;
- }
- /***********************************************************************
- * Exception personality
- **********************************************************************/
- static void call_alt_handlers(struct _Unwind_Context *ctx);
- _Unwind_Reason_Code
- __objc_personality_v0(int version,
- _Unwind_Action actions,
- uint64_t exceptionClass,
- struct _Unwind_Exception *exceptionObject,
- struct _Unwind_Context *context)
- {
- bool unwinding = ((actions & _UA_CLEANUP_PHASE) ||
- (actions & _UA_FORCE_UNWIND));
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: %s through frame [ip=%p sp=%p] "
- "for exception %p",
- unwinding ? "unwinding" : "searching",
- (void*)(_Unwind_GetIP(context)-1),
- (void*)_Unwind_GetCFA(context), exceptionObject);
- }
- // If we're executing the unwind, call this frame's alt handlers, if any.
- if (unwinding) {
- call_alt_handlers(context);
- }
- // Let C++ handle the unwind itself.
- return CXX_PERSONALITY(version, actions, exceptionClass,
- exceptionObject, context);
- }
- /***********************************************************************
- * Compiler ABI
- **********************************************************************/
- static void _objc_exception_destructor(void *exc_gen)
- {
- // Release the retain from objc_exception_throw().
- struct objc_exception *exc = (struct objc_exception *)exc_gen;
- id obj = exc->obj;
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: releasing completed exception %p (object %p, a %s)",
- exc, obj, object_getClassName(obj));
- }
- [obj release];
- }
- void objc_exception_throw(id obj)
- {
- struct objc_exception *exc = (struct objc_exception *)
- __cxa_allocate_exception(sizeof(struct objc_exception));
- obj = (*exception_preprocessor)(obj);
- // Retain the exception object during unwinding
- // because otherwise an autorelease pool pop can cause a crash
- [obj retain];
- exc->obj = obj;
- exc->tinfo.vtable = objc_ehtype_vtable+2;
- exc->tinfo.name = object_getClassName(obj);
- exc->tinfo.cls_unremapped = obj ? obj->getIsa() : Nil;
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
- exc, (void*)obj, object_getClassName(obj));
- }
- if (PrintExceptionThrow) {
- if (!PrintExceptions)
- _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
- exc, (void*)obj, object_getClassName(obj));
- void* callstack[500];
- int frameCount = backtrace(callstack, 500);
- backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
- }
-
- OBJC_RUNTIME_OBJC_EXCEPTION_THROW(obj); // dtrace probe to log throw activity
- __cxa_throw(exc, &exc->tinfo, &_objc_exception_destructor);
- __builtin_trap();
- }
- void objc_exception_rethrow(void)
- {
- // exception_preprocessor doesn't get another bite of the apple
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: rethrowing current exception");
- }
-
- OBJC_RUNTIME_OBJC_EXCEPTION_RETHROW(); // dtrace probe to log throw activity.
- __cxa_rethrow();
- __builtin_trap();
- }
- id objc_begin_catch(void *exc_gen)
- {
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: handling exception %p at %p",
- exc_gen, __builtin_return_address(0));
- }
- // NOT actually an id in the catch(...) case!
- return (id)__cxa_begin_catch(exc_gen);
- }
- void objc_end_catch(void)
- {
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: finishing handler");
- }
- __cxa_end_catch();
- }
- // `outer` is not passed by the new libcxxabi
- bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
- struct objc_typeinfo *throw_tinfo,
- void **throw_obj_p,
- unsigned outer UNAVAILABLE_ATTRIBUTE)
- {
- id exception;
- if (throw_tinfo->vtable != objc_ehtype_vtable+2) {
- // Only objc types can be caught here.
- if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(?)");
- return false;
- }
- // Adjust exception pointer.
- // Old libcppabi: we lied about __is_pointer_p() so we have to do it here
- // New libcxxabi: we have to do it here regardless
- *throw_obj_p = **(void***)throw_obj_p;
- // `catch (id)` always catches objc types.
- if (catch_tinfo == &OBJC_EHTYPE_id) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(id)");
- return true;
- }
- exception = *(id *)throw_obj_p;
- Class handler_cls = _class_remap(catch_tinfo->cls_unremapped);
- if (!handler_cls) {
- // catch handler's class is weak-linked and missing. Not a match.
- }
- else if ((*exception_matcher)(handler_cls, exception)) {
- if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(%s)",
- handler_cls->nameForLogging());
- return true;
- }
- if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(%s)",
- handler_cls->nameForLogging());
- return false;
- }
- /***********************************************************************
- * _objc_terminate
- * Custom std::terminate handler.
- *
- * The uncaught exception callback is implemented as a std::terminate handler.
- * 1. Check if there's an active exception
- * 2. If so, check if it's an Objective-C exception
- * 3. If so, call our registered callback with the object.
- * 4. Finally, call the previous terminate handler.
- **********************************************************************/
- static void (*old_terminate)(void) = nil;
- static void _objc_terminate(void)
- {
- if (PrintExceptions) {
- _objc_inform("EXCEPTIONS: terminating");
- }
- if (! __cxa_current_exception_type()) {
- // No current exception.
- (*old_terminate)();
- }
- else {
- // There is a current exception. Check if it's an objc exception.
- @try {
- __cxa_rethrow();
- } @catch (id e) {
- // It's an objc object. Call Foundation's handler, if any.
- (*uncaught_handler)((id)e);
- (*old_terminate)();
- } @catch (...) {
- // It's not an objc object. Continue to C++ terminate.
- (*old_terminate)();
- }
- }
- }
- /***********************************************************************
- * objc_terminate
- * Calls std::terminate for clients who don't link to C++ themselves.
- * Called by the compiler if an exception is thrown
- * from a context where exceptions may not be thrown.
- **********************************************************************/
- void objc_terminate(void)
- {
- std::terminate();
- }
- /***********************************************************************
- * alt handler support - zerocost implementation only
- **********************************************************************/
- #if !SUPPORT_ALT_HANDLERS
- void _destroyAltHandlerList(struct alt_handler_list *list)
- {
- }
- static void call_alt_handlers(struct _Unwind_Context *ctx)
- {
- // unsupported in sjlj environments
- }
- #else
- #include <libunwind.h>
- #include <execinfo.h>
- #include <dispatch/dispatch.h>
- // Dwarf eh data encodings
- #define DW_EH_PE_omit 0xff // no data follows
- #define DW_EH_PE_absptr 0x00
- #define DW_EH_PE_uleb128 0x01
- #define DW_EH_PE_udata2 0x02
- #define DW_EH_PE_udata4 0x03
- #define DW_EH_PE_udata8 0x04
- #define DW_EH_PE_sleb128 0x09
- #define DW_EH_PE_sdata2 0x0A
- #define DW_EH_PE_sdata4 0x0B
- #define DW_EH_PE_sdata8 0x0C
- #define DW_EH_PE_pcrel 0x10
- #define DW_EH_PE_textrel 0x20
- #define DW_EH_PE_datarel 0x30
- #define DW_EH_PE_funcrel 0x40
- #define DW_EH_PE_aligned 0x50 // fixme
- #define DW_EH_PE_indirect 0x80 // gcc extension
- /***********************************************************************
- * read_uleb
- * Read a LEB-encoded unsigned integer from the address stored in *pp.
- * Increments *pp past the bytes read.
- * Adapted from DWARF Debugging Information Format 1.1, appendix 4
- **********************************************************************/
- static uintptr_t read_uleb(uintptr_t *pp)
- {
- uintptr_t result = 0;
- uintptr_t shift = 0;
- unsigned char byte;
- do {
- byte = *(const unsigned char *)(*pp)++;
- result |= (byte & 0x7f) << shift;
- shift += 7;
- } while (byte & 0x80);
- return result;
- }
- /***********************************************************************
- * read_sleb
- * Read a LEB-encoded signed integer from the address stored in *pp.
- * Increments *pp past the bytes read.
- * Adapted from DWARF Debugging Information Format 1.1, appendix 4
- **********************************************************************/
- static intptr_t read_sleb(uintptr_t *pp)
- {
- uintptr_t result = 0;
- uintptr_t shift = 0;
- unsigned char byte;
- do {
- byte = *(const unsigned char *)(*pp)++;
- result |= (byte & 0x7f) << shift;
- shift += 7;
- } while (byte & 0x80);
- if ((shift < 8*sizeof(intptr_t)) && (byte & 0x40)) {
- result |= ((intptr_t)-1) << shift;
- }
- return result;
- }
- /***********************************************************************
- * read_address
- * Reads an encoded address from the address stored in *pp.
- * Increments *pp past the bytes read.
- * The data is interpreted according to the given dwarf encoding
- * and base addresses.
- **********************************************************************/
- static uintptr_t read_address(uintptr_t *pp,
- const struct dwarf_eh_bases *bases,
- unsigned char encoding)
- {
- uintptr_t result = 0;
- uintptr_t oldp = *pp;
- // fixme need DW_EH_PE_aligned?
- #define READ(type) \
- result = *(type *)(*pp); \
- *pp += sizeof(type);
- if (encoding == DW_EH_PE_omit) return 0;
- switch (encoding & 0x0f) {
- case DW_EH_PE_absptr:
- READ(uintptr_t);
- break;
- case DW_EH_PE_uleb128:
- result = read_uleb(pp);
- break;
- case DW_EH_PE_udata2:
- READ(uint16_t);
- break;
- case DW_EH_PE_udata4:
- READ(uint32_t);
- break;
- #if __LP64__
- case DW_EH_PE_udata8:
- READ(uint64_t);
- break;
- #endif
- case DW_EH_PE_sleb128:
- result = read_sleb(pp);
- break;
- case DW_EH_PE_sdata2:
- READ(int16_t);
- break;
- case DW_EH_PE_sdata4:
- READ(int32_t);
- break;
- #if __LP64__
- case DW_EH_PE_sdata8:
- READ(int64_t);
- break;
- #endif
- default:
- _objc_inform("unknown DWARF EH encoding 0x%x at %p",
- encoding, (void *)*pp);
- break;
- }
- #undef READ
- if (result) {
- switch (encoding & 0x70) {
- case DW_EH_PE_pcrel:
- // fixme correct?
- result += (uintptr_t)oldp;
- break;
- case DW_EH_PE_textrel:
- result += bases->tbase;
- break;
- case DW_EH_PE_datarel:
- result += bases->dbase;
- break;
- case DW_EH_PE_funcrel:
- result += bases->func;
- break;
- case DW_EH_PE_aligned:
- _objc_inform("unknown DWARF EH encoding 0x%x at %p",
- encoding, (void *)*pp);
- break;
- default:
- // no adjustment
- break;
- }
- if (encoding & DW_EH_PE_indirect) {
- result = *(uintptr_t *)result;
- }
- }
- return (uintptr_t)result;
- }
- struct frame_ips {
- uintptr_t start;
- uintptr_t end;
- };
- struct frame_range {
- uintptr_t ip_start;
- uintptr_t ip_end;
- uintptr_t cfa;
- // precise ranges within ip_start..ip_end; nil or {0,0} terminated
- frame_ips *ips;
- };
- static bool isObjCExceptionCatcher(uintptr_t lsda, uintptr_t ip,
- const struct dwarf_eh_bases* bases,
- struct frame_range *frame)
- {
- unsigned char LPStart_enc = *(const unsigned char *)lsda++;
- if (LPStart_enc != DW_EH_PE_omit) {
- read_address(&lsda, bases, LPStart_enc); // LPStart
- }
- unsigned char TType_enc = *(const unsigned char *)lsda++;
- if (TType_enc != DW_EH_PE_omit) {
- read_uleb(&lsda); // TType
- }
- unsigned char call_site_enc = *(const unsigned char *)lsda++;
- uintptr_t length = read_uleb(&lsda);
- uintptr_t call_site_table = lsda;
- uintptr_t call_site_table_end = call_site_table + length;
- uintptr_t action_record_table = call_site_table_end;
- uintptr_t action_record = 0;
- uintptr_t p = call_site_table;
- uintptr_t try_start;
- uintptr_t try_end;
- uintptr_t try_landing_pad;
- while (p < call_site_table_end) {
- uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func;
- uintptr_t len = read_address(&p, bases, call_site_enc);
- uintptr_t pad = read_address(&p, bases, call_site_enc);
- uintptr_t action = read_uleb(&p);
- if (ip < start) {
- // no more source ranges
- return false;
- }
- else if (ip < start + len) {
- // found the range
- if (!pad) return false; // ...but it has no landing pad
- // found the landing pad
- action_record = action ? action_record_table + action - 1 : 0;
- try_start = start;
- try_end = start + len;
- try_landing_pad = pad;
- break;
- }
- }
-
- if (!action_record) return false; // no catch handlers
- // has handlers, destructors, and/or throws specifications
- // Use this frame if it has any handlers
- bool has_handler = false;
- p = action_record;
- intptr_t offset;
- do {
- intptr_t filter = read_sleb(&p);
- uintptr_t temp = p;
- offset = read_sleb(&temp);
- p += offset;
-
- if (filter < 0) {
- // throws specification - ignore
- } else if (filter == 0) {
- // destructor - ignore
- } else /* filter >= 0 */ {
- // catch handler - use this frame
- has_handler = true;
- break;
- }
- } while (offset);
- if (!has_handler) return false;
-
- // Count the number of source ranges with the same landing pad as our match
- unsigned int range_count = 0;
- p = call_site_table;
- while (p < call_site_table_end) {
- /*start*/ read_address(&p, bases, call_site_enc)/*+bases->func*/;
- /*len*/ read_address(&p, bases, call_site_enc);
- uintptr_t pad = read_address(&p, bases, call_site_enc);
- /*action*/ read_uleb(&p);
-
- if (pad == try_landing_pad) {
- range_count++;
- }
- }
- if (range_count == 1) {
- // No other source ranges with the same landing pad. We're done here.
- frame->ips = nil;
- }
- else {
- // Record all ranges with the same landing pad as our match.
- frame->ips = (frame_ips *)
- malloc((range_count + 1) * sizeof(frame->ips[0]));
- unsigned int r = 0;
- p = call_site_table;
- while (p < call_site_table_end) {
- uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func;
- uintptr_t len = read_address(&p, bases, call_site_enc);
- uintptr_t pad = read_address(&p, bases, call_site_enc);
- /*action*/ read_uleb(&p);
-
- if (pad == try_landing_pad) {
- if (start < try_start) try_start = start;
- if (start+len > try_end) try_end = start+len;
- frame->ips[r].start = start;
- frame->ips[r].end = start+len;
- r++;
- }
- }
- frame->ips[r].start = 0;
- frame->ips[r].end = 0;
- }
- frame->ip_start = try_start;
- frame->ip_end = try_end;
- return true;
- }
- static struct frame_range findHandler(void)
- {
- // walk stack looking for frame with objc catch handler
- unw_context_t uc;
- unw_cursor_t cursor;
- unw_proc_info_t info;
- unw_getcontext(&uc);
- unw_init_local(&cursor, &uc);
- while ( (unw_step(&cursor) > 0) && (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) ) {
- // must use objc personality handler
- if ( info.handler != (uintptr_t)__objc_personality_v0 )
- continue;
- // must have landing pad
- if ( info.lsda == 0 )
- continue;
- // must have landing pad that catches objc exceptions
- struct dwarf_eh_bases bases;
- bases.tbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
- bases.dbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
- bases.func = info.start_ip;
- unw_word_t ip;
- unw_get_reg(&cursor, UNW_REG_IP, &ip);
- ip -= 1;
- struct frame_range try_range = {0, 0, 0, 0};
- if ( isObjCExceptionCatcher(info.lsda, ip, &bases, &try_range) ) {
- unw_word_t cfa;
- unw_get_reg(&cursor, UNW_REG_SP, &cfa);
- try_range.cfa = cfa;
- return try_range;
- }
- }
- return (struct frame_range){0, 0, 0, 0};
- }
- // This data structure assumes the number of
- // active alt handlers per frame is small.
- // for OBJC_DEBUG_ALT_HANDLERS, record the call to objc_addExceptionHandler.
- #define BACKTRACE_COUNT 46
- #define THREADNAME_COUNT 64
- struct alt_handler_debug {
- uintptr_t token;
- int backtraceSize;
- void *backtrace[BACKTRACE_COUNT];
- char thread[THREADNAME_COUNT];
- char queue[THREADNAME_COUNT];
- };
- struct alt_handler_data {
- struct frame_range frame;
- objc_exception_handler fn;
- void *context;
- struct alt_handler_debug *debug;
- };
- struct alt_handler_list {
- unsigned int allocated;
- unsigned int used;
- struct alt_handler_data *handlers;
- struct alt_handler_list *next_DEBUGONLY;
- };
- static struct alt_handler_list *DebugLists;
- static uintptr_t DebugCounter;
- __attribute__((noinline, noreturn))
- void alt_handler_error(uintptr_t token);
- static struct alt_handler_list *
- fetch_handler_list(bool create)
- {
- _objc_pthread_data *data = _objc_fetch_pthread_data(create);
- if (!data) return nil;
- struct alt_handler_list *list = data->handlerList;
- if (!list) {
- if (!create) return nil;
- list = (struct alt_handler_list *)calloc(1, sizeof(*list));
- data->handlerList = list;
- if (DebugAltHandlers) {
- // Save this list so the debug code can find it from other threads
- mutex_locker_t lock(AltHandlerDebugLock);
- list->next_DEBUGONLY = DebugLists;
- DebugLists = list;
- }
- }
- return list;
- }
- void _destroyAltHandlerList(struct alt_handler_list *list)
- {
- if (list) {
- if (DebugAltHandlers) {
- // Detach from the list-of-lists.
- mutex_locker_t lock(AltHandlerDebugLock);
- struct alt_handler_list **listp = &DebugLists;
- while (*listp && *listp != list) listp = &(*listp)->next_DEBUGONLY;
- if (*listp) *listp = (*listp)->next_DEBUGONLY;
- }
- if (list->handlers) {
- for (unsigned int i = 0; i < list->allocated; i++) {
- if (list->handlers[i].frame.ips) {
- free(list->handlers[i].frame.ips);
- }
- }
- free(list->handlers);
- }
- free(list);
- }
- }
- uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
- {
- // Find the closest enclosing frame with objc catch handlers
- struct frame_range target_frame = findHandler();
- if (!target_frame.ip_start) {
- // No suitable enclosing handler found.
- return 0;
- }
- // Record this alt handler for the discovered frame.
- struct alt_handler_list *list = fetch_handler_list(YES);
- unsigned int i = 0;
- if (list->used == list->allocated) {
- list->allocated = list->allocated*2 ?: 4;
- list->handlers = (struct alt_handler_data *)
- realloc(list->handlers,
- list->allocated * sizeof(list->handlers[0]));
- bzero(&list->handlers[list->used], (list->allocated - list->used) * sizeof(list->handlers[0]));
- i = list->used;
- }
- else {
- for (i = 0; i < list->allocated; i++) {
- if (list->handlers[i].frame.ip_start == 0 &&
- list->handlers[i].frame.ip_end == 0 &&
- list->handlers[i].frame.cfa == 0)
- {
- break;
- }
- }
- if (i == list->allocated) {
- _objc_fatal("alt handlers in objc runtime are buggy!");
- }
- }
- struct alt_handler_data *data = &list->handlers[i];
- data->frame = target_frame;
- data->fn = fn;
- data->context = context;
- list->used++;
- uintptr_t token = i+1;
- if (DebugAltHandlers) {
- // Record backtrace in case this handler is misused later.
- mutex_locker_t lock(AltHandlerDebugLock);
- token = DebugCounter++;
- if (token == 0) token = DebugCounter++;
- if (!data->debug) {
- data->debug = (struct alt_handler_debug *)
- calloc(sizeof(*data->debug), 1);
- } else {
- bzero(data->debug, sizeof(*data->debug));
- }
- pthread_getname_np(pthread_self(), data->debug->thread, THREADNAME_COUNT);
- strlcpy(data->debug->queue,
- dispatch_queue_get_label(dispatch_get_current_queue()),
- THREADNAME_COUNT);
- data->debug->backtraceSize =
- backtrace(data->debug->backtrace, BACKTRACE_COUNT);
- data->debug->token = token;
- }
- if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: installing alt handler #%lu %p(%p) on "
- "frame [ip=%p..%p sp=%p]", (unsigned long)token,
- data->fn, data->context, (void *)data->frame.ip_start,
- (void *)data->frame.ip_end, (void *)data->frame.cfa);
- if (data->frame.ips) {
- unsigned int r = 0;
- while (1) {
- uintptr_t start = data->frame.ips[r].start;
- uintptr_t end = data->frame.ips[r].end;
- r++;
- if (start == 0 && end == 0) break;
- _objc_inform("ALT HANDLERS: ip=%p..%p",
- (void*)start, (void*)end);
- }
- }
- }
- if (list->used > 1000) {
- static int warned = 0;
- if (!warned) {
- _objc_inform("ALT HANDLERS: *** over 1000 alt handlers installed; "
- "this is probably a bug");
- warned = 1;
- }
- }
- return token;
- }
- void objc_removeExceptionHandler(uintptr_t token)
- {
- if (!token) {
- // objc_addExceptionHandler failed
- return;
- }
-
- struct alt_handler_list *list = fetch_handler_list(NO);
- if (!list || !list->handlers) {
- // no alt handlers active
- alt_handler_error(token);
- }
- uintptr_t i = token-1;
-
- if (DebugAltHandlers) {
- // search for the token instead of using token-1
- for (i = 0; i < list->allocated; i++) {
- struct alt_handler_data *data = &list->handlers[i];
- if (data->debug && data->debug->token == token) break;
- }
- }
-
- if (i >= list->allocated) {
- // token out of range
- alt_handler_error(token);
- }
- struct alt_handler_data *data = &list->handlers[i];
- if (data->frame.ip_start == 0 && data->frame.ip_end == 0 && data->frame.cfa == 0) {
- // token in range, but invalid
- alt_handler_error(token);
- }
- if (PrintAltHandlers) {
- _objc_inform("ALT HANDLERS: removing alt handler #%lu %p(%p) on "
- "frame [ip=%p..%p sp=%p]", (unsigned long)token,
- data->fn, data->context, (void *)data->frame.ip_start,
- (void *)data->frame.ip_end, (void *)data->frame.cfa);
- }
- if (data->debug) free(data->debug);
- if (data->frame.ips) free(data->frame.ips);
- bzero(data, sizeof(*data));
- list->used--;
- }
- BREAKPOINT_FUNCTION(
- void objc_alt_handler_error(void));
- __attribute__((noinline, noreturn))
- void alt_handler_error(uintptr_t token)
- {
- _objc_inform
- ("objc_removeExceptionHandler() called with unknown alt handler; "
- "this is probably a bug in multithreaded AppKit use. "
- "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
- "or break in objc_alt_handler_error() to debug.");
- if (DebugAltHandlers) {
- AltHandlerDebugLock.lock();
-
- // Search other threads' alt handler lists for this handler.
- struct alt_handler_list *list;
- for (list = DebugLists; list; list = list->next_DEBUGONLY) {
- unsigned h;
- for (h = 0; h < list->allocated; h++) {
- struct alt_handler_data *data = &list->handlers[h];
- if (data->debug && data->debug->token == token) {
- // found it
- int i;
-
- // Build a string from the recorded backtrace
- char *symbolString;
- char **symbols =
- backtrace_symbols(data->debug->backtrace,
- data->debug->backtraceSize);
- size_t len = 1;
- for (i = 0; i < data->debug->backtraceSize; i++){
- len += 4 + strlen(symbols[i]) + 1;
- }
- symbolString = (char *)calloc(len, 1);
- for (i = 0; i < data->debug->backtraceSize; i++){
- strcat(symbolString, " ");
- strcat(symbolString, symbols[i]);
- strcat(symbolString, "\n");
- }
-
- free(symbols);
-
- _objc_inform_now_and_on_crash
- ("The matching objc_addExceptionHandler() was called "
- "by:\nThread '%s': Dispatch queue: '%s': \n%s",
- data->debug->thread, data->debug->queue, symbolString);
- goto done;
- }
- }
- }
- done:
- AltHandlerDebugLock.unlock();
- }
- objc_alt_handler_error();
-
- _objc_fatal
- ("objc_removeExceptionHandler() called with unknown alt handler; "
- "this is probably a bug in multithreaded AppKit use. ");
- }
- // called in order registered, to match 32-bit _NSAddAltHandler2
- // fixme reverse registration order matches c++ destructors better
- static void call_alt_handlers(struct _Unwind_Context *ctx)
- {
- uintptr_t ip = _Unwind_GetIP(ctx) - 1;
- uintptr_t cfa = _Unwind_GetCFA(ctx);
- unsigned int i;
-
- struct alt_handler_list *list = fetch_handler_list(NO);
- if (!list || list->used == 0) return;
- for (i = 0; i < list->allocated; i++) {
- struct alt_handler_data *data = &list->handlers[i];
- if (ip >= data->frame.ip_start && ip < data->frame.ip_end && data->frame.cfa == cfa)
- {
- if (data->frame.ips) {
- unsigned int r = 0;
- bool found;
- while (1) {
- uintptr_t start = data->frame.ips[r].start;
- uintptr_t end = data->frame.ips[r].end;
- r++;
- if (start == 0 && end == 0) {
- found = false;
- break;
- }
- if (ip >= start && ip < end) {
- found = true;
- break;
- }
- }
- if (!found) continue;
- }
- // Copy and clear before the callback, in case the
- // callback manipulates the alt handler list.
- struct alt_handler_data copy = *data;
- bzero(data, sizeof(*data));
- list->used--;
- if (PrintExceptions || PrintAltHandlers) {
- _objc_inform("EXCEPTIONS: calling alt handler %p(%p) from "
- "frame [ip=%p..%p sp=%p]", copy.fn, copy.context,
- (void *)copy.frame.ip_start,
- (void *)copy.frame.ip_end,
- (void *)copy.frame.cfa);
- }
- if (copy.fn) (*copy.fn)(nil, copy.context);
- if (copy.frame.ips) free(copy.frame.ips);
- }
- }
- }
- // SUPPORT_ALT_HANDLERS
- #endif
- /***********************************************************************
- * exception_init
- * Initialize libobjc's exception handling system.
- * Called by map_images().
- **********************************************************************/
- void exception_init(void)
- {
- old_terminate = std::set_terminate(&_objc_terminate);
- }
- // __OBJC2__
- #endif
- // Define this everywhere even if it isn't used, to simplify fork() safety code
- mutex_t AltHandlerDebugLock;
|