]> git.saurik.com Git - apple/objc4.git/blame - runtime/objc-exception.mm
objc4-646.tar.gz
[apple/objc4.git] / runtime / objc-exception.mm
CommitLineData
390d5862 1/*
b3962a83 2 * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
390d5862
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
390d5862
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
390d5862 23
7af964d1 24#if !__OBJC2__
b3962a83
A
25
26/***********************************************************************
27* 32-bit implementation
28**********************************************************************/
29
7af964d1 30#include "objc-private.h"
b3962a83 31#include <stdlib.h>
7af964d1 32#include <setjmp.h>
8972963c 33#include <execinfo.h>
390d5862 34
7af964d1 35#include "objc-exception.h"
390d5862
A
36
37static objc_exception_functions_t xtab;
38
39// forward declaration
40static void set_default_handlers();
41
42
390d5862
A
43/*
44 * Exported functions
45 */
46
47// get table; version tells how many
48void objc_exception_get_functions(objc_exception_functions_t *table) {
49 // only version 0 supported at this point
50 if (table && table->version == 0)
51 *table = xtab;
52}
53
54// set table
55void objc_exception_set_functions(objc_exception_functions_t *table) {
56 // only version 0 supported at this point
57 if (table && table->version == 0)
58 xtab = *table;
59}
60
61/*
62 * The following functions are
63 * synthesized by the compiler upon encountering language constructs
64 */
65
66void objc_exception_throw(id exception) {
67 if (!xtab.throw_exc) {
68 set_default_handlers();
69 }
8972963c
A
70
71 if (PrintExceptionThrow) {
72 _objc_inform("EXCEPTIONS: throwing %p (%s)",
7257e56c 73 (void*)exception, object_getClassName(exception));
8972963c
A
74 void* callstack[500];
75 int frameCount = backtrace(callstack, 500);
76 backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
77 }
7af964d1
A
78
79 OBJC_RUNTIME_OBJC_EXCEPTION_THROW(exception); // dtrace probe to log throw activity.
390d5862 80 xtab.throw_exc(exception);
b3962a83 81 _objc_fatal("objc_exception_throw failed");
390d5862
A
82}
83
84void objc_exception_try_enter(void *localExceptionData) {
85 if (!xtab.throw_exc) {
86 set_default_handlers();
87 }
88 xtab.try_enter(localExceptionData);
89}
90
91
92void objc_exception_try_exit(void *localExceptionData) {
93 if (!xtab.throw_exc) {
94 set_default_handlers();
95 }
96 xtab.try_exit(localExceptionData);
97}
98
99
100id objc_exception_extract(void *localExceptionData) {
101 if (!xtab.throw_exc) {
102 set_default_handlers();
103 }
104 return xtab.extract(localExceptionData);
105}
106
107
108int objc_exception_match(Class exceptionClass, id exception) {
109 if (!xtab.throw_exc) {
110 set_default_handlers();
111 }
112 return xtab.match(exceptionClass, exception);
113}
114
115
116// quick and dirty exception handling code
117// default implementation - mostly a toy for use outside/before Foundation
118// provides its implementation
119// Perhaps the default implementation should just complain loudly and quit
120
121
390d5862
A
122extern void _objc_inform(const char *fmt, ...);
123
124typedef struct { jmp_buf buf; void *pointers[4]; } LocalData_t;
125
126typedef struct _threadChain {
127 LocalData_t *topHandler;
7af964d1 128 objc_thread_t perThreadID;
390d5862
A
129 struct _threadChain *next;
130}
131 ThreadChainLink_t;
132
390d5862
A
133static ThreadChainLink_t ThreadChainLink;
134
135static ThreadChainLink_t *getChainLink() {
136 // follow links until thread_self() found (someday) XXX
7af964d1 137 objc_thread_t self = thread_self();
390d5862 138 ThreadChainLink_t *walker = &ThreadChainLink;
7af964d1 139 while (walker->perThreadID != self) {
7257e56c 140 if (walker->next != nil) {
390d5862
A
141 walker = walker->next;
142 continue;
143 }
144 // create a new one
145 // XXX not thread safe (!)
146 // XXX Also, we don't register to deallocate on thread death
147 walker->next = (ThreadChainLink_t *)malloc(sizeof(ThreadChainLink_t));
148 walker = walker->next;
7257e56c
A
149 walker->next = nil;
150 walker->topHandler = nil;
390d5862
A
151 walker->perThreadID = self;
152 }
153 return walker;
154}
155
156static void default_try_enter(void *localExceptionData) {
7af964d1 157 LocalData_t *data = (LocalData_t *)localExceptionData;
390d5862 158 ThreadChainLink_t *chainLink = getChainLink();
7af964d1
A
159 data->pointers[1] = chainLink->topHandler;
160 chainLink->topHandler = data;
b3962a83 161 if (PrintExceptions) _objc_inform("EXCEPTIONS: entered try block %p\n", chainLink->topHandler);
390d5862
A
162}
163
164static void default_throw(id value) {
165 ThreadChainLink_t *chainLink = getChainLink();
7af964d1 166 LocalData_t *led;
390d5862 167 if (value == nil) {
b3962a83 168 if (PrintExceptions) _objc_inform("EXCEPTIONS: objc_exception_throw with nil value\n");
390d5862
A
169 return;
170 }
7257e56c 171 if (chainLink == nil) {
b3962a83 172 if (PrintExceptions) _objc_inform("EXCEPTIONS: No handler in place!\n");
390d5862
A
173 return;
174 }
b3962a83 175 if (PrintExceptions) _objc_inform("EXCEPTIONS: exception thrown, going to handler block %p\n", chainLink->topHandler);
7af964d1
A
176 led = chainLink->topHandler;
177 chainLink->topHandler = (LocalData_t *)
178 led->pointers[1]; // pop top handler
390d5862 179 led->pointers[0] = value; // store exception that is thrown
7af964d1
A
180#if TARGET_OS_WIN32
181 longjmp(led->buf, 1);
182#else
390d5862 183 _longjmp(led->buf, 1);
7af964d1 184#endif
390d5862
A
185}
186
187static void default_try_exit(void *led) {
188 ThreadChainLink_t *chainLink = getChainLink();
189 if (!chainLink || led != chainLink->topHandler) {
b3962a83 190 if (PrintExceptions) _objc_inform("EXCEPTIONS: *** mismatched try block exit handlers\n");
390d5862
A
191 return;
192 }
b3962a83 193 if (PrintExceptions) _objc_inform("EXCEPTIONS: removing try block handler %p\n", chainLink->topHandler);
7af964d1
A
194 chainLink->topHandler = (LocalData_t *)
195 chainLink->topHandler->pointers[1]; // pop top handler
390d5862
A
196}
197
198static id default_extract(void *localExceptionData) {
199 LocalData_t *led = (LocalData_t *)localExceptionData;
200 return (id)led->pointers[0];
201}
202
203static int default_match(Class exceptionClass, id exception) {
204 //return [exception isKindOfClass:exceptionClass];
205 Class cls;
7257e56c 206 for (cls = exception->getIsa(); nil != cls; cls = cls->superclass)
7af964d1 207 if (cls == exceptionClass) return 1;
390d5862
A
208 return 0;
209}
210
211static void set_default_handlers() {
212 objc_exception_functions_t default_functions = {
213 0, default_throw, default_try_enter, default_try_exit, default_extract, default_match };
214
215 // should this always print?
b3962a83 216 if (PrintExceptions) _objc_inform("EXCEPTIONS: *** Setting default (non-Foundation) exception mechanism\n");
390d5862
A
217 objc_exception_set_functions(&default_functions);
218}
b3962a83
A
219
220
cd5f04f5 221void exception_init(void)
b3962a83
A
222{
223 // nothing to do
224}
225
cd5f04f5 226void _destroyAltHandlerList(struct alt_handler_list *list)
b3962a83
A
227{
228 // nothing to do
229}
230
231
7af964d1 232// !__OBJC2__
b3962a83 233#else
7af964d1 234// __OBJC2__
b3962a83
A
235
236/***********************************************************************
237* 64-bit implementation.
238**********************************************************************/
239
b3962a83 240#include "objc-private.h"
7af964d1 241#include <objc/objc-exception.h>
8070259c 242#include <objc/NSObject.h>
8972963c 243#include <execinfo.h>
b3962a83
A
244
245// unwind library types and functions
246// Mostly adapted from Itanium C++ ABI: Exception Handling
247// http://www.codesourcery.com/cxx-abi/abi-eh.html
248
249struct _Unwind_Exception;
250struct _Unwind_Context;
251
252typedef int _Unwind_Action;
253static const _Unwind_Action _UA_SEARCH_PHASE = 1;
254static const _Unwind_Action _UA_CLEANUP_PHASE = 2;
255static const _Unwind_Action _UA_HANDLER_FRAME = 4;
256static const _Unwind_Action _UA_FORCE_UNWIND = 8;
257
8972963c
A
258typedef int _Unwind_Reason_Code;
259static const _Unwind_Reason_Code _URC_NO_REASON = 0;
260static const _Unwind_Reason_Code _URC_FOREIGN_EXCEPTION_CAUGHT = 1;
261static const _Unwind_Reason_Code _URC_FATAL_PHASE2_ERROR = 2;
262static const _Unwind_Reason_Code _URC_FATAL_PHASE1_ERROR = 3;
263static const _Unwind_Reason_Code _URC_NORMAL_STOP = 4;
264static const _Unwind_Reason_Code _URC_END_OF_STACK = 5;
265static const _Unwind_Reason_Code _URC_HANDLER_FOUND = 6;
266static const _Unwind_Reason_Code _URC_INSTALL_CONTEXT = 7;
267static const _Unwind_Reason_Code _URC_CONTINUE_UNWIND = 8;
b3962a83 268
b3962a83
A
269struct dwarf_eh_bases
270{
271 uintptr_t tbase;
272 uintptr_t dbase;
273 uintptr_t func;
274};
275
cd5f04f5
A
276OBJC_EXTERN uintptr_t _Unwind_GetIP (struct _Unwind_Context *);
277OBJC_EXTERN uintptr_t _Unwind_GetCFA (struct _Unwind_Context *);
278OBJC_EXTERN uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *);
b3962a83
A
279
280
281// C++ runtime types and functions
8972963c 282// copied from cxxabi.h
b3962a83 283
cd5f04f5
A
284OBJC_EXTERN void *__cxa_allocate_exception(size_t thrown_size);
285OBJC_EXTERN void __cxa_throw(void *exc, void *typeinfo, void (*destructor)(void *)) __attribute__((noreturn));
286OBJC_EXTERN void *__cxa_begin_catch(void *exc);
287OBJC_EXTERN void __cxa_end_catch(void);
288OBJC_EXTERN void __cxa_rethrow(void);
289OBJC_EXTERN void *__cxa_current_exception_type(void);
b3962a83 290
8972963c
A
291#if SUPPORT_ZEROCOST_EXCEPTIONS
292# define CXX_PERSONALITY __gxx_personality_v0
7af964d1 293#else
8972963c 294# define CXX_PERSONALITY __gxx_personality_sj0
7af964d1
A
295#endif
296
cd5f04f5 297OBJC_EXTERN _Unwind_Reason_Code
7af964d1
A
298CXX_PERSONALITY(int version,
299 _Unwind_Action actions,
300 uint64_t exceptionClass,
301 struct _Unwind_Exception *exceptionObject,
302 struct _Unwind_Context *context);
b3962a83
A
303
304
305// objc's internal exception types and data
306
b3962a83
A
307struct objc_typeinfo {
308 // Position of vtable and name fields must match C++ typeinfo object
7af964d1 309 const void **vtable; // always objc_ehtype_vtable+2
b3962a83
A
310 const char *name; // c++ typeinfo string
311
cd5f04f5 312 Class cls_unremapped;
b3962a83
A
313};
314
315struct objc_exception {
316 id obj;
317 struct objc_typeinfo tinfo;
318};
319
320
321static void _objc_exception_noop(void) { }
cd5f04f5
A
322static bool _objc_exception_false(void) { return 0; }
323// static bool _objc_exception_true(void) { return 1; }
7257e56c
A
324static void _objc_exception_abort1(void) {
325 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 1);
326}
327static void _objc_exception_abort2(void) {
328 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 2);
329}
330static void _objc_exception_abort3(void) {
331 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 3);
332}
333static void _objc_exception_abort4(void) {
334 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 4);
335}
336
cd5f04f5 337static bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
b3962a83
A
338 struct objc_typeinfo *throw_tinfo,
339 void **throw_obj_p,
340 unsigned outer);
341
cd5f04f5
A
342// forward declaration
343OBJC_EXPORT struct objc_typeinfo OBJC_EHTYPE_id;
344
345OBJC_EXPORT
b3962a83 346const void *objc_ehtype_vtable[] = {
7257e56c 347 nil, // typeinfo's vtable? - fixme
cd5f04f5
A
348 (void*)&OBJC_EHTYPE_id, // typeinfo's typeinfo - hack
349 (void*)_objc_exception_noop, // in-place destructor?
350 (void*)_objc_exception_noop, // destructor?
351 (void*)_objc_exception_false, // OLD __is_pointer_p
352 (void*)_objc_exception_false, // OLD __is_function_p
7257e56c
A
353 (void*)_objc_exception_do_catch, // OLD __do_catch, NEW can_catch
354 (void*)_objc_exception_false, // OLD __do_upcast, NEW search_above_dst
355 (void*)_objc_exception_false, // NEW search_below_dst
356 (void*)_objc_exception_abort1, // paranoia: blow up if libc++abi
357 (void*)_objc_exception_abort2, // adds something new
358 (void*)_objc_exception_abort3,
359 (void*)_objc_exception_abort4,
b3962a83
A
360};
361
cd5f04f5 362OBJC_EXPORT
b3962a83
A
363struct objc_typeinfo OBJC_EHTYPE_id = {
364 objc_ehtype_vtable+2,
365 "id",
7257e56c 366 nil
b3962a83
A
367};
368
369
370
371/***********************************************************************
372* Foundation customization
373**********************************************************************/
374
375/***********************************************************************
376* _objc_default_exception_preprocessor
377* Default exception preprocessor. Expected to be overridden by Foundation.
378**********************************************************************/
379static id _objc_default_exception_preprocessor(id exception)
380{
381 return exception;
382}
383static objc_exception_preprocessor exception_preprocessor = _objc_default_exception_preprocessor;
384
385
386/***********************************************************************
387* _objc_default_exception_matcher
388* Default exception matcher. Expected to be overridden by Foundation.
389**********************************************************************/
390static int _objc_default_exception_matcher(Class catch_cls, id exception)
391{
392 Class cls;
7257e56c
A
393 for (cls = exception->getIsa();
394 cls != nil;
395 cls = cls->superclass)
b3962a83
A
396 {
397 if (cls == catch_cls) return 1;
398 }
399
400 return 0;
401}
402static objc_exception_matcher exception_matcher = _objc_default_exception_matcher;
403
404
405/***********************************************************************
406* _objc_default_uncaught_exception_handler
407* Default uncaught exception handler. Expected to be overridden by Foundation.
408**********************************************************************/
409static void _objc_default_uncaught_exception_handler(id exception)
410{
411}
412static objc_uncaught_exception_handler uncaught_handler = _objc_default_uncaught_exception_handler;
413
414
415/***********************************************************************
416* objc_setExceptionPreprocessor
417* Set a handler for preprocessing Objective-C exceptions.
418* Returns the previous handler.
419**********************************************************************/
420objc_exception_preprocessor
421objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
422{
423 objc_exception_preprocessor result = exception_preprocessor;
424 exception_preprocessor = fn;
425 return result;
426}
427
428
429/***********************************************************************
430* objc_setExceptionMatcher
431* Set a handler for matching Objective-C exceptions.
432* Returns the previous handler.
433**********************************************************************/
434objc_exception_matcher
435objc_setExceptionMatcher(objc_exception_matcher fn)
436{
437 objc_exception_matcher result = exception_matcher;
438 exception_matcher = fn;
439 return result;
440}
441
442
443/***********************************************************************
444* objc_setUncaughtExceptionHandler
445* Set a handler for uncaught Objective-C exceptions.
446* Returns the previous handler.
447**********************************************************************/
448objc_uncaught_exception_handler
449objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
450{
451 objc_uncaught_exception_handler result = uncaught_handler;
452 uncaught_handler = fn;
453 return result;
454}
455
456
457/***********************************************************************
458* Exception personality
459**********************************************************************/
460
461static void call_alt_handlers(struct _Unwind_Context *ctx);
462
463_Unwind_Reason_Code
8972963c
A
464__objc_personality_v0(int version,
465 _Unwind_Action actions,
466 uint64_t exceptionClass,
467 struct _Unwind_Exception *exceptionObject,
468 struct _Unwind_Context *context)
b3962a83
A
469{
470 BOOL unwinding = ((actions & _UA_CLEANUP_PHASE) ||
471 (actions & _UA_FORCE_UNWIND));
472
473 if (PrintExceptions) {
474 _objc_inform("EXCEPTIONS: %s through frame [ip=%p sp=%p] "
475 "for exception %p",
476 unwinding ? "unwinding" : "searching",
477 (void*)(_Unwind_GetIP(context)-1),
478 (void*)_Unwind_GetCFA(context), exceptionObject);
479 }
480
481 // If we're executing the unwind, call this frame's alt handlers, if any.
482 if (unwinding) {
483 call_alt_handlers(context);
484 }
485
486 // Let C++ handle the unwind itself.
7af964d1
A
487 return CXX_PERSONALITY(version, actions, exceptionClass,
488 exceptionObject, context);
b3962a83
A
489}
490
491
492/***********************************************************************
493* Compiler ABI
494**********************************************************************/
495
7257e56c
A
496static void _objc_exception_destructor(void *exc_gen)
497{
498 // Release the retain from objc_exception_throw().
499
b3962a83 500 struct objc_exception *exc = (struct objc_exception *)exc_gen;
7257e56c
A
501 id obj = exc->obj;
502
503 if (PrintExceptions) {
504 _objc_inform("EXCEPTIONS: releasing completed exception %p (object %p, a %s)",
505 exc, obj, object_getClassName(obj));
506 }
507
508#if SUPPORT_GC
509 if (UseGC) {
510 if (auto_zone_is_valid_pointer(gc_zone, obj)) {
511 auto_zone_release(gc_zone, exc->obj);
512 }
b3962a83 513 }
7257e56c 514 else
cd5f04f5 515#endif
7257e56c
A
516 {
517 [obj release];
518 }
b3962a83
A
519}
520
521
522void objc_exception_throw(id obj)
523{
cd5f04f5 524 struct objc_exception *exc = (struct objc_exception *)
b3962a83
A
525 __cxa_allocate_exception(sizeof(struct objc_exception));
526
7257e56c
A
527 obj = (*exception_preprocessor)(obj);
528
529 // Retain the exception object during unwinding.
530 // GC: because `exc` is unscanned memory
531 // Non-GC: because otherwise an autorelease pool pop can cause a crash
cd5f04f5 532#if SUPPORT_GC
7257e56c
A
533 if (UseGC) {
534 if (auto_zone_is_valid_pointer(gc_zone, obj)) {
535 auto_zone_retain(gc_zone, obj);
536 }
b3962a83 537 }
7257e56c 538 else
cd5f04f5 539#endif
7257e56c
A
540 {
541 [obj retain];
542 }
b3962a83 543
7257e56c 544 exc->obj = obj;
7af964d1 545 exc->tinfo.vtable = objc_ehtype_vtable+2;
b3962a83 546 exc->tinfo.name = object_getClassName(obj);
7257e56c 547 exc->tinfo.cls_unremapped = obj ? obj->getIsa() : Nil;
b3962a83
A
548
549 if (PrintExceptions) {
550 _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
7257e56c 551 exc, (void*)obj, object_getClassName(obj));
b3962a83 552 }
8972963c
A
553
554 if (PrintExceptionThrow) {
555 if (!PrintExceptions)
556 _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
7257e56c 557 exc, (void*)obj, object_getClassName(obj));
8972963c
A
558 void* callstack[500];
559 int frameCount = backtrace(callstack, 500);
560 backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
561 }
7af964d1
A
562
563 OBJC_RUNTIME_OBJC_EXCEPTION_THROW(obj); // dtrace probe to log throw activity
b3962a83 564 __cxa_throw(exc, &exc->tinfo, &_objc_exception_destructor);
7af964d1 565 __builtin_trap();
b3962a83
A
566}
567
568
569void objc_exception_rethrow(void)
570{
571 // exception_preprocessor doesn't get another bite of the apple
572 if (PrintExceptions) {
573 _objc_inform("EXCEPTIONS: rethrowing current exception");
574 }
7af964d1
A
575
576 OBJC_RUNTIME_OBJC_EXCEPTION_RETHROW(); // dtrace probe to log throw activity.
b3962a83 577 __cxa_rethrow();
7af964d1 578 __builtin_trap();
b3962a83
A
579}
580
581
582id objc_begin_catch(void *exc_gen)
583{
584 if (PrintExceptions) {
585 _objc_inform("EXCEPTIONS: handling exception %p at %p",
586 exc_gen, __builtin_return_address(0));
587 }
588 // NOT actually an id in the catch(...) case!
589 return (id)__cxa_begin_catch(exc_gen);
590}
591
592
593void objc_end_catch(void)
594{
595 if (PrintExceptions) {
596 _objc_inform("EXCEPTIONS: finishing handler");
597 }
598 __cxa_end_catch();
599}
600
601
cd5f04f5
A
602// `outer` is not passed by the new libcxxabi
603static bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
b3962a83
A
604 struct objc_typeinfo *throw_tinfo,
605 void **throw_obj_p,
cd5f04f5 606 unsigned outer UNAVAILABLE_ATTRIBUTE)
b3962a83
A
607{
608 id exception;
609
7af964d1 610 if (throw_tinfo->vtable != objc_ehtype_vtable+2) {
b3962a83 611 // Only objc types can be caught here.
7af964d1 612 if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(?)");
cd5f04f5 613 return false;
b3962a83
A
614 }
615
cd5f04f5
A
616 // Adjust exception pointer.
617 // Old libcppabi: we lied about __is_pointer_p() so we have to do it here
618 // New libcxxabi: we have to do it here regardless
619 *throw_obj_p = **(void***)throw_obj_p;
620
b3962a83
A
621 // `catch (id)` always catches objc types.
622 if (catch_tinfo == &OBJC_EHTYPE_id) {
623 if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(id)");
cd5f04f5 624 return true;
b3962a83
A
625 }
626
627 exception = *(id *)throw_obj_p;
cd5f04f5
A
628
629 Class handler_cls = _class_remap(catch_tinfo->cls_unremapped);
630 if (!handler_cls) {
631 // catch handler's class is weak-linked and missing. Not a match.
632 }
633 else if ((*exception_matcher)(handler_cls, exception)) {
b3962a83 634 if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(%s)",
8070259c 635 handler_cls->nameForLogging());
cd5f04f5 636 return true;
b3962a83
A
637 }
638
7af964d1 639 if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(%s)",
8070259c 640 handler_cls->nameForLogging());
7af964d1 641
cd5f04f5 642 return false;
b3962a83
A
643}
644
645
646/***********************************************************************
647* _objc_terminate
648* Custom std::terminate handler.
649*
650* The uncaught exception callback is implemented as a std::terminate handler.
651* 1. Check if there's an active exception
652* 2. If so, check if it's an Objective-C exception
653* 3. If so, call our registered callback with the object.
654* 4. Finally, call the previous terminate handler.
655**********************************************************************/
7257e56c 656static void (*old_terminate)(void) = nil;
b3962a83
A
657static void _objc_terminate(void)
658{
659 if (PrintExceptions) {
660 _objc_inform("EXCEPTIONS: terminating");
661 }
662
663 if (! __cxa_current_exception_type()) {
664 // No current exception.
665 (*old_terminate)();
666 }
667 else {
668 // There is a current exception. Check if it's an objc exception.
669 @try {
670 __cxa_rethrow();
671 } @catch (id e) {
672 // It's an objc object. Call Foundation's handler, if any.
7257e56c 673 (*uncaught_handler)((id)e);
b3962a83
A
674 (*old_terminate)();
675 } @catch (...) {
676 // It's not an objc object. Continue to C++ terminate.
677 (*old_terminate)();
678 }
679 }
680}
681
682
cd5f04f5
A
683/***********************************************************************
684* objc_terminate
685* Calls std::terminate for clients who don't link to C++ themselves.
686* Called by the compiler if an exception is thrown
687* from a context where exceptions may not be thrown.
688**********************************************************************/
689void objc_terminate(void)
690{
691 std::terminate();
692}
693
694
b3962a83 695/***********************************************************************
7af964d1 696* alt handler support - zerocost implementation only
b3962a83
A
697**********************************************************************/
698
8972963c 699#if !SUPPORT_ALT_HANDLERS
7af964d1 700
cd5f04f5 701void _destroyAltHandlerList(struct alt_handler_list *list)
7af964d1
A
702{
703}
704
705static void call_alt_handlers(struct _Unwind_Context *ctx)
706{
707 // unsupported in sjlj environments
708}
709
710#else
711
712#include <libunwind.h>
8972963c
A
713#include <execinfo.h>
714#include <dispatch/dispatch.h>
b3962a83
A
715
716// Dwarf eh data encodings
717#define DW_EH_PE_omit 0xff // no data follows
718
719#define DW_EH_PE_absptr 0x00
720#define DW_EH_PE_uleb128 0x01
721#define DW_EH_PE_udata2 0x02
722#define DW_EH_PE_udata4 0x03
723#define DW_EH_PE_udata8 0x04
724#define DW_EH_PE_sleb128 0x09
725#define DW_EH_PE_sdata2 0x0A
726#define DW_EH_PE_sdata4 0x0B
727#define DW_EH_PE_sdata8 0x0C
728
729#define DW_EH_PE_pcrel 0x10
730#define DW_EH_PE_textrel 0x20
731#define DW_EH_PE_datarel 0x30
732#define DW_EH_PE_funcrel 0x40
733#define DW_EH_PE_aligned 0x50 // fixme
734
735#define DW_EH_PE_indirect 0x80 // gcc extension
736
737
738/***********************************************************************
739* read_uleb
740* Read a LEB-encoded unsigned integer from the address stored in *pp.
741* Increments *pp past the bytes read.
742* Adapted from DWARF Debugging Information Format 1.1, appendix 4
743**********************************************************************/
744static uintptr_t read_uleb(uintptr_t *pp)
745{
746 uintptr_t result = 0;
747 uintptr_t shift = 0;
748 unsigned char byte;
749 do {
750 byte = *(const unsigned char *)(*pp)++;
751 result |= (byte & 0x7f) << shift;
752 shift += 7;
753 } while (byte & 0x80);
754 return result;
755}
756
757
758/***********************************************************************
759* read_sleb
760* Read a LEB-encoded signed integer from the address stored in *pp.
761* Increments *pp past the bytes read.
762* Adapted from DWARF Debugging Information Format 1.1, appendix 4
763**********************************************************************/
764static intptr_t read_sleb(uintptr_t *pp)
765{
766 uintptr_t result = 0;
767 uintptr_t shift = 0;
768 unsigned char byte;
769 do {
770 byte = *(const unsigned char *)(*pp)++;
771 result |= (byte & 0x7f) << shift;
772 shift += 7;
773 } while (byte & 0x80);
774 if ((shift < 8*sizeof(intptr_t)) && (byte & 0x40)) {
775 result |= ((intptr_t)-1) << shift;
776 }
777 return result;
778}
779
780
b3962a83
A
781/***********************************************************************
782* read_address
783* Reads an encoded address from the address stored in *pp.
784* Increments *pp past the bytes read.
785* The data is interpreted according to the given dwarf encoding
786* and base addresses.
787**********************************************************************/
788static uintptr_t read_address(uintptr_t *pp,
7af964d1 789 const struct dwarf_eh_bases *bases,
b3962a83
A
790 unsigned char encoding)
791{
792 uintptr_t result = 0;
793 uintptr_t oldp = *pp;
794
795 // fixme need DW_EH_PE_aligned?
796
797#define READ(type) \
798 result = *(type *)(*pp); \
799 *pp += sizeof(type);
800
801 if (encoding == DW_EH_PE_omit) return 0;
802
803 switch (encoding & 0x0f) {
804 case DW_EH_PE_absptr:
805 READ(uintptr_t);
806 break;
807 case DW_EH_PE_uleb128:
808 result = read_uleb(pp);
809 break;
810 case DW_EH_PE_udata2:
811 READ(uint16_t);
812 break;
813 case DW_EH_PE_udata4:
814 READ(uint32_t);
815 break;
816#if __LP64__
817 case DW_EH_PE_udata8:
818 READ(uint64_t);
819 break;
820#endif
821 case DW_EH_PE_sleb128:
822 result = read_sleb(pp);
823 break;
824 case DW_EH_PE_sdata2:
825 READ(int16_t);
826 break;
827 case DW_EH_PE_sdata4:
828 READ(int32_t);
829 break;
830#if __LP64__
831 case DW_EH_PE_sdata8:
832 READ(int64_t);
833 break;
834#endif
835 default:
836 _objc_inform("unknown DWARF EH encoding 0x%x at %p",
837 encoding, (void *)*pp);
838 break;
839 }
840
841#undef READ
842
843 if (result) {
844 switch (encoding & 0x70) {
845 case DW_EH_PE_pcrel:
846 // fixme correct?
847 result += (uintptr_t)oldp;
848 break;
849 case DW_EH_PE_textrel:
850 result += bases->tbase;
851 break;
852 case DW_EH_PE_datarel:
853 result += bases->dbase;
854 break;
855 case DW_EH_PE_funcrel:
856 result += bases->func;
857 break;
858 case DW_EH_PE_aligned:
859 _objc_inform("unknown DWARF EH encoding 0x%x at %p",
860 encoding, (void *)*pp);
861 break;
862 default:
863 // no adjustment
864 break;
865 }
866
867 if (encoding & DW_EH_PE_indirect) {
868 result = *(uintptr_t *)result;
869 }
870 }
871
872 return (uintptr_t)result;
873}
874
875
cd5f04f5
A
876struct frame_ips {
877 uintptr_t start;
878 uintptr_t end;
879};
880struct frame_range {
881 uintptr_t ip_start;
882 uintptr_t ip_end;
883 uintptr_t cfa;
7257e56c 884 // precise ranges within ip_start..ip_end; nil or {0,0} terminated
cd5f04f5
A
885 frame_ips *ips;
886};
887
888
7af964d1
A
889static bool isObjCExceptionCatcher(uintptr_t lsda, uintptr_t ip,
890 const struct dwarf_eh_bases* bases,
cd5f04f5 891 struct frame_range *frame)
b3962a83 892{
7af964d1 893 unsigned char LPStart_enc = *(const unsigned char *)lsda++;
b3962a83 894
b3962a83 895 if (LPStart_enc != DW_EH_PE_omit) {
7af964d1 896 read_address(&lsda, bases, LPStart_enc); // LPStart
b3962a83
A
897 }
898
899 unsigned char TType_enc = *(const unsigned char *)lsda++;
900 if (TType_enc != DW_EH_PE_omit) {
901 read_uleb(&lsda); // TType
902 }
903
904 unsigned char call_site_enc = *(const unsigned char *)lsda++;
905 uintptr_t length = read_uleb(&lsda);
906 uintptr_t call_site_table = lsda;
907 uintptr_t call_site_table_end = call_site_table + length;
908 uintptr_t action_record_table = call_site_table_end;
909
910 uintptr_t action_record = 0;
911 uintptr_t p = call_site_table;
912
cd5f04f5
A
913 uintptr_t try_start;
914 uintptr_t try_end;
915 uintptr_t try_landing_pad;
916
b3962a83 917 while (p < call_site_table_end) {
cd5f04f5
A
918 uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func;
919 uintptr_t len = read_address(&p, bases, call_site_enc);
920 uintptr_t pad = read_address(&p, bases, call_site_enc);
921 uintptr_t action = read_uleb(&p);
b3962a83 922
cd5f04f5 923 if (ip < start) {
b3962a83 924 // no more source ranges
7af964d1 925 return false;
b3962a83 926 }
cd5f04f5 927 else if (ip < start + len) {
b3962a83 928 // found the range
7af964d1 929 if (!pad) return false; // ...but it has no landing pad
b3962a83 930 // found the landing pad
b3962a83 931 action_record = action ? action_record_table + action - 1 : 0;
cd5f04f5
A
932 try_start = start;
933 try_end = start + len;
934 try_landing_pad = pad;
b3962a83
A
935 break;
936 }
937 }
938
7af964d1 939 if (!action_record) return false; // no catch handlers
b3962a83
A
940
941 // has handlers, destructors, and/or throws specifications
942 // Use this frame if it has any handlers
7af964d1 943 bool has_handler = false;
b3962a83
A
944 p = action_record;
945 intptr_t offset;
946 do {
947 intptr_t filter = read_sleb(&p);
948 uintptr_t temp = p;
949 offset = read_sleb(&temp);
950 p += offset;
951
952 if (filter < 0) {
953 // throws specification - ignore
954 } else if (filter == 0) {
955 // destructor - ignore
956 } else /* filter >= 0 */ {
957 // catch handler - use this frame
7af964d1 958 has_handler = true;
b3962a83
A
959 break;
960 }
961 } while (offset);
cd5f04f5
A
962
963 if (!has_handler) return false;
b3962a83 964
cd5f04f5
A
965 // Count the number of source ranges with the same landing pad as our match
966 unsigned int range_count = 0;
967 p = call_site_table;
968 while (p < call_site_table_end) {
969 /*start*/ read_address(&p, bases, call_site_enc)/*+bases->func*/;
970 /*len*/ read_address(&p, bases, call_site_enc);
971 uintptr_t pad = read_address(&p, bases, call_site_enc);
972 /*action*/ read_uleb(&p);
973
974 if (pad == try_landing_pad) {
975 range_count++;
976 }
977 }
7af964d1 978
cd5f04f5
A
979 if (range_count == 1) {
980 // No other source ranges with the same landing pad. We're done here.
7257e56c 981 frame->ips = nil;
cd5f04f5
A
982 }
983 else {
984 // Record all ranges with the same landing pad as our match.
985 frame->ips = (frame_ips *)
986 _malloc_internal((range_count + 1) * sizeof(frame->ips[0]));
987 unsigned int r = 0;
988 p = call_site_table;
989 while (p < call_site_table_end) {
990 uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func;
991 uintptr_t len = read_address(&p, bases, call_site_enc);
992 uintptr_t pad = read_address(&p, bases, call_site_enc);
993 /*action*/ read_uleb(&p);
994
995 if (pad == try_landing_pad) {
996 if (start < try_start) try_start = start;
997 if (start+len > try_end) try_end = start+len;
998 frame->ips[r].start = start;
999 frame->ips[r].end = start+len;
1000 r++;
1001 }
1002 }
1003
1004 frame->ips[r].start = 0;
1005 frame->ips[r].end = 0;
1006 }
1007
1008 frame->ip_start = try_start;
1009 frame->ip_end = try_end;
1010
1011 return true;
1012}
7af964d1 1013
b3962a83 1014
7af964d1
A
1015static struct frame_range findHandler(void)
1016{
1017 // walk stack looking for frame with objc catch handler
1018 unw_context_t uc;
1019 unw_cursor_t cursor;
1020 unw_proc_info_t info;
1021 unw_getcontext(&uc);
1022 unw_init_local(&cursor, &uc);
1023 while ( (unw_step(&cursor) > 0) && (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) ) {
1024 // must use objc personality handler
1025 if ( info.handler != (uintptr_t)__objc_personality_v0 )
1026 continue;
1027 // must have landing pad
1028 if ( info.lsda == 0 )
1029 continue;
1030 // must have landing pad that catches objc exceptions
1031 struct dwarf_eh_bases bases;
1032 bases.tbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
1033 bases.dbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
1034 bases.func = info.start_ip;
1035 unw_word_t ip;
1036 unw_get_reg(&cursor, UNW_REG_IP, &ip);
fe06a513 1037 ip -= 1;
cd5f04f5
A
1038 struct frame_range try_range = {0, 0, 0, 0};
1039 if ( isObjCExceptionCatcher(info.lsda, ip, &bases, &try_range) ) {
7af964d1
A
1040 unw_word_t cfa;
1041 unw_get_reg(&cursor, UNW_REG_SP, &cfa);
cd5f04f5
A
1042 try_range.cfa = cfa;
1043 return try_range;
7af964d1
A
1044 }
1045 }
b3962a83 1046
cd5f04f5 1047 return (struct frame_range){0, 0, 0, 0};
b3962a83
A
1048}
1049
1050
1051// This data structure assumes the number of
1052// active alt handlers per frame is small.
8972963c
A
1053
1054// for OBJC_DEBUG_ALT_HANDLERS, record the call to objc_addExceptionHandler.
1055#define BACKTRACE_COUNT 46
1056#define THREADNAME_COUNT 64
1057struct alt_handler_debug {
1058 uintptr_t token;
1059 int backtraceSize;
1060 void *backtrace[BACKTRACE_COUNT];
1061 char thread[THREADNAME_COUNT];
1062 char queue[THREADNAME_COUNT];
1063};
1064
b3962a83 1065struct alt_handler_data {
cd5f04f5 1066 struct frame_range frame;
b3962a83
A
1067 objc_exception_handler fn;
1068 void *context;
8972963c 1069 struct alt_handler_debug *debug;
b3962a83
A
1070};
1071
1072struct alt_handler_list {
1073 unsigned int allocated;
1074 unsigned int used;
1075 struct alt_handler_data *handlers;
8972963c 1076 struct alt_handler_list *next_DEBUGONLY;
b3962a83
A
1077};
1078
8972963c
A
1079static pthread_mutex_t DebugLock = PTHREAD_MUTEX_INITIALIZER;
1080static struct alt_handler_list *DebugLists;
1081static uintptr_t DebugCounter;
1082
cd5f04f5 1083void alt_handler_error(uintptr_t token) __attribute__((noinline));
b3962a83
A
1084
1085static struct alt_handler_list *
1086fetch_handler_list(BOOL create)
1087{
1088 _objc_pthread_data *data = _objc_fetch_pthread_data(create);
7257e56c 1089 if (!data) return nil;
b3962a83
A
1090
1091 struct alt_handler_list *list = data->handlerList;
1092 if (!list) {
7257e56c 1093 if (!create) return nil;
cd5f04f5 1094 list = (struct alt_handler_list *)_calloc_internal(1, sizeof(*list));
b3962a83 1095 data->handlerList = list;
8972963c
A
1096
1097 if (DebugAltHandlers) {
1098 // Save this list so the debug code can find it from other threads
1099 pthread_mutex_lock(&DebugLock);
1100 list->next_DEBUGONLY = DebugLists;
1101 DebugLists = list;
1102 pthread_mutex_unlock(&DebugLock);
1103 }
b3962a83
A
1104 }
1105
1106 return list;
1107}
1108
1109
cd5f04f5 1110void _destroyAltHandlerList(struct alt_handler_list *list)
b3962a83
A
1111{
1112 if (list) {
8972963c
A
1113 if (DebugAltHandlers) {
1114 // Detach from the list-of-lists.
1115 pthread_mutex_lock(&DebugLock);
1116 struct alt_handler_list **listp = &DebugLists;
1117 while (*listp && *listp != list) listp = &(*listp)->next_DEBUGONLY;
1118 if (*listp) *listp = (*listp)->next_DEBUGONLY;
1119 pthread_mutex_unlock(&DebugLock);
1120 }
1121
b3962a83 1122 if (list->handlers) {
cd5f04f5
A
1123 for (unsigned int i = 0; i < list->allocated; i++) {
1124 if (list->handlers[i].frame.ips) {
1125 _free_internal(list->handlers[i].frame.ips);
1126 }
1127 }
b3962a83
A
1128 _free_internal(list->handlers);
1129 }
1130 _free_internal(list);
1131 }
1132}
1133
1134
1135uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
1136{
b3962a83 1137 // Find the closest enclosing frame with objc catch handlers
7af964d1 1138 struct frame_range target_frame = findHandler();
b3962a83
A
1139 if (!target_frame.ip_start) {
1140 // No suitable enclosing handler found.
1141 return 0;
1142 }
1143
1144 // Record this alt handler for the discovered frame.
1145 struct alt_handler_list *list = fetch_handler_list(YES);
1146 unsigned int i = 0;
1147
1148 if (list->used == list->allocated) {
1149 list->allocated = list->allocated*2 ?: 4;
cd5f04f5
A
1150 list->handlers = (struct alt_handler_data *)
1151 _realloc_internal(list->handlers,
1152 list->allocated * sizeof(list->handlers[0]));
b3962a83
A
1153 bzero(&list->handlers[list->used], (list->allocated - list->used) * sizeof(list->handlers[0]));
1154 i = list->used;
1155 }
1156 else {
1157 for (i = 0; i < list->allocated; i++) {
cd5f04f5
A
1158 if (list->handlers[i].frame.ip_start == 0 &&
1159 list->handlers[i].frame.ip_end == 0 &&
1160 list->handlers[i].frame.cfa == 0)
b3962a83
A
1161 {
1162 break;
1163 }
1164 }
1165 if (i == list->allocated) {
1166 _objc_fatal("alt handlers in objc runtime are buggy!");
1167 }
1168 }
1169
1170 struct alt_handler_data *data = &list->handlers[i];
1171
cd5f04f5 1172 data->frame = target_frame;
b3962a83
A
1173 data->fn = fn;
1174 data->context = context;
1175 list->used++;
1176
8972963c
A
1177 uintptr_t token = i+1;
1178
1179 if (DebugAltHandlers) {
1180 // Record backtrace in case this handler is misused later.
1181 pthread_mutex_lock(&DebugLock);
1182
1183 token = DebugCounter++;
1184 if (token == 0) token = DebugCounter++;
1185
1186 if (!data->debug) {
cd5f04f5
A
1187 data->debug = (struct alt_handler_debug *)
1188 _calloc_internal(sizeof(*data->debug), 1);
8972963c
A
1189 } else {
1190 bzero(data->debug, sizeof(*data->debug));
1191 }
1192
1193 pthread_getname_np(pthread_self(), data->debug->thread, THREADNAME_COUNT);
1194 strlcpy(data->debug->queue,
1195 dispatch_queue_get_label(dispatch_get_current_queue()),
1196 THREADNAME_COUNT);
1197 data->debug->backtraceSize =
1198 backtrace(data->debug->backtrace, BACKTRACE_COUNT);
1199 data->debug->token = token;
1200
1201 pthread_mutex_unlock(&DebugLock);
1202 }
1203
b3962a83 1204 if (PrintAltHandlers) {
8972963c
A
1205 _objc_inform("ALT HANDLERS: installing alt handler #%lu %p(%p) on "
1206 "frame [ip=%p..%p sp=%p]", (unsigned long)token,
cd5f04f5
A
1207 data->fn, data->context, (void *)data->frame.ip_start,
1208 (void *)data->frame.ip_end, (void *)data->frame.cfa);
1209 if (data->frame.ips) {
1210 unsigned int r = 0;
1211 while (1) {
1212 uintptr_t start = data->frame.ips[r].start;
1213 uintptr_t end = data->frame.ips[r].end;
1214 r++;
1215 if (start == 0 && end == 0) break;
1216 _objc_inform("ALT HANDLERS: ip=%p..%p",
1217 (void*)start, (void*)end);
1218 }
1219 }
b3962a83
A
1220 }
1221
1222 if (list->used > 1000) {
1223 static int warned = 0;
1224 if (!warned) {
7af964d1 1225 _objc_inform("ALT HANDLERS: *** over 1000 alt handlers installed; "
b3962a83
A
1226 "this is probably a bug");
1227 warned = 1;
1228 }
1229 }
1230
8972963c 1231 return token;
b3962a83
A
1232}
1233
1234
1235void objc_removeExceptionHandler(uintptr_t token)
1236{
1237 if (!token) {
1238 // objc_addExceptionHandler failed
1239 return;
1240 }
b3962a83
A
1241
1242 struct alt_handler_list *list = fetch_handler_list(NO);
8972963c
A
1243 if (!list || !list->handlers) {
1244 // no alt handlers active
1245 alt_handler_error(token);
1246 __builtin_trap();
1247 }
1248
1249 uintptr_t i = token-1;
1250
1251 if (DebugAltHandlers) {
1252 // search for the token instead of using token-1
1253 for (i = 0; i < list->allocated; i++) {
1254 struct alt_handler_data *data = &list->handlers[i];
1255 if (data->debug && data->debug->token == token) break;
b3962a83 1256 }
b3962a83 1257 }
8972963c 1258
b3962a83 1259 if (i >= list->allocated) {
8972963c
A
1260 // token out of range
1261 alt_handler_error(token);
1262 __builtin_trap();
b3962a83
A
1263 }
1264
1265 struct alt_handler_data *data = &list->handlers[i];
8972963c 1266
cd5f04f5 1267 if (data->frame.ip_start == 0 && data->frame.ip_end == 0 && data->frame.cfa == 0) {
8972963c
A
1268 // token in range, but invalid
1269 alt_handler_error(token);
1270 __builtin_trap();
1271 }
1272
b3962a83 1273 if (PrintAltHandlers) {
8972963c
A
1274 _objc_inform("ALT HANDLERS: removing alt handler #%lu %p(%p) on "
1275 "frame [ip=%p..%p sp=%p]", (unsigned long)token,
cd5f04f5
A
1276 data->fn, data->context, (void *)data->frame.ip_start,
1277 (void *)data->frame.ip_end, (void *)data->frame.cfa);
b3962a83 1278 }
8972963c
A
1279
1280 if (data->debug) _free_internal(data->debug);
cd5f04f5 1281 if (data->frame.ips) _free_internal(data->frame.ips);
b3962a83
A
1282 bzero(data, sizeof(*data));
1283 list->used--;
1284}
1285
cd5f04f5 1286void objc_alt_handler_error(void) __attribute__((noinline));
8972963c 1287
cd5f04f5 1288void alt_handler_error(uintptr_t token)
8972963c
A
1289{
1290 if (!DebugAltHandlers) {
1291 _objc_inform_now_and_on_crash
1292 ("objc_removeExceptionHandler() called with unknown alt handler; "
1293 "this is probably a bug in multithreaded AppKit use. "
1294 "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
1295 "or break in objc_alt_handler_error() to debug.");
1296 objc_alt_handler_error();
1297 }
1298
1299 pthread_mutex_lock(&DebugLock);
1300
1301 // Search other threads' alt handler lists for this handler.
1302 struct alt_handler_list *list;
1303 for (list = DebugLists; list; list = list->next_DEBUGONLY) {
cd5f04f5 1304 unsigned h;
8972963c
A
1305 for (h = 0; h < list->allocated; h++) {
1306 struct alt_handler_data *data = &list->handlers[h];
1307 if (data->debug && data->debug->token == token) {
1308 // found it
1309 int i;
1310
1311 // Build a string from the recorded backtrace
1312 char *symbolString;
1313 char **symbols =
1314 backtrace_symbols(data->debug->backtrace,
1315 data->debug->backtraceSize);
1316 size_t len = 1;
1317 for (i = 0; i < data->debug->backtraceSize; i++){
1318 len += 4 + strlen(symbols[i]) + 1;
1319 }
cd5f04f5 1320 symbolString = (char *)_calloc_internal(len, 1);
8972963c
A
1321 for (i = 0; i < data->debug->backtraceSize; i++){
1322 strcat(symbolString, " ");
1323 strcat(symbolString, symbols[i]);
1324 strcat(symbolString, "\n");
1325 }
1326
1327 free(symbols);
1328
1329 _objc_inform_now_and_on_crash
1330 ("objc_removeExceptionHandler() called with "
1331 "unknown alt handler; this is probably a bug in "
1332 "multithreaded AppKit use. \n"
1333 "The matching objc_addExceptionHandler() was called by:\n"
1334 "Thread '%s': Dispatch queue: '%s': \n%s",
1335 data->debug->thread, data->debug->queue, symbolString);
1336
1337 pthread_mutex_unlock(&DebugLock);
1338 _free_internal(symbolString);
1339
1340 objc_alt_handler_error();
1341 }
1342 }
1343 }
1344
1345 pthread_mutex_lock(&DebugLock);
1346
1347 // not found
1348 _objc_inform_now_and_on_crash
1349 ("objc_removeExceptionHandler() called with unknown alt handler; "
1350 "this is probably a bug in multithreaded AppKit use");
1351 objc_alt_handler_error();
1352}
1353
cd5f04f5 1354void objc_alt_handler_error(void)
8972963c
A
1355{
1356 __builtin_trap();
1357}
b3962a83
A
1358
1359// called in order registered, to match 32-bit _NSAddAltHandler2
1360// fixme reverse registration order matches c++ destructors better
1361static void call_alt_handlers(struct _Unwind_Context *ctx)
1362{
1363 uintptr_t ip = _Unwind_GetIP(ctx) - 1;
1364 uintptr_t cfa = _Unwind_GetCFA(ctx);
1365 unsigned int i;
390d5862 1366
b3962a83
A
1367 struct alt_handler_list *list = fetch_handler_list(NO);
1368 if (!list || list->used == 0) return;
1369
1370 for (i = 0; i < list->allocated; i++) {
1371 struct alt_handler_data *data = &list->handlers[i];
cd5f04f5 1372 if (ip >= data->frame.ip_start && ip < data->frame.ip_end && data->frame.cfa == cfa)
b3962a83 1373 {
cd5f04f5
A
1374 if (data->frame.ips) {
1375 unsigned int r = 0;
1376 bool found;
1377 while (1) {
1378 uintptr_t start = data->frame.ips[r].start;
1379 uintptr_t end = data->frame.ips[r].end;
1380 r++;
1381 if (start == 0 && end == 0) {
1382 found = false;
1383 break;
1384 }
1385 if (ip >= start && ip < end) {
1386 found = true;
1387 break;
1388 }
1389 }
1390 if (!found) continue;
1391 }
1392
b3962a83
A
1393 // Copy and clear before the callback, in case the
1394 // callback manipulates the alt handler list.
1395 struct alt_handler_data copy = *data;
1396 bzero(data, sizeof(*data));
1397 list->used--;
1398 if (PrintExceptions || PrintAltHandlers) {
1399 _objc_inform("EXCEPTIONS: calling alt handler %p(%p) from "
1400 "frame [ip=%p..%p sp=%p]", copy.fn, copy.context,
cd5f04f5
A
1401 (void *)copy.frame.ip_start,
1402 (void *)copy.frame.ip_end,
1403 (void *)copy.frame.cfa);
b3962a83
A
1404 }
1405 if (copy.fn) (*copy.fn)(nil, copy.context);
cd5f04f5 1406 if (copy.frame.ips) _free_internal(copy.frame.ips);
b3962a83
A
1407 }
1408 }
1409}
1410
8972963c 1411// SUPPORT_ALT_HANDLERS
7af964d1
A
1412#endif
1413
b3962a83
A
1414
1415/***********************************************************************
1416* exception_init
1417* Initialize libobjc's exception handling system.
1418* Called by map_images().
1419**********************************************************************/
cd5f04f5 1420void exception_init(void)
b3962a83 1421{
cd5f04f5 1422 old_terminate = std::set_terminate(&_objc_terminate);
b3962a83
A
1423}
1424
1425
7af964d1 1426// __OBJC2__
b3962a83 1427#endif