]> git.saurik.com Git - apple/objc4.git/blame - runtime/objc-exception.mm
objc4-818.2.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() {
1807f628
A
136 // follow links until objc_thread_self() found (someday) XXX
137 objc_thread_t self = objc_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"
66799735 241#include <objc/objc-abi.h>
7af964d1 242#include <objc/objc-exception.h>
8070259c 243#include <objc/NSObject.h>
8972963c 244#include <execinfo.h>
b3962a83
A
245
246// unwind library types and functions
247// Mostly adapted from Itanium C++ ABI: Exception Handling
248// http://www.codesourcery.com/cxx-abi/abi-eh.html
249
250struct _Unwind_Exception;
251struct _Unwind_Context;
252
253typedef int _Unwind_Action;
31875a97
A
254enum : _Unwind_Action {
255 _UA_SEARCH_PHASE = 1,
256 _UA_CLEANUP_PHASE = 2,
257 _UA_HANDLER_FRAME = 4,
258 _UA_FORCE_UNWIND = 8
259};
b3962a83 260
8972963c 261typedef int _Unwind_Reason_Code;
31875a97
A
262enum : _Unwind_Reason_Code {
263 _URC_NO_REASON = 0,
264 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
265 _URC_FATAL_PHASE2_ERROR = 2,
266 _URC_FATAL_PHASE1_ERROR = 3,
267 _URC_NORMAL_STOP = 4,
268 _URC_END_OF_STACK = 5,
269 _URC_HANDLER_FOUND = 6,
270 _URC_INSTALL_CONTEXT = 7,
271 _URC_CONTINUE_UNWIND = 8
272};
b3962a83 273
b3962a83
A
274struct dwarf_eh_bases
275{
276 uintptr_t tbase;
277 uintptr_t dbase;
278 uintptr_t func;
279};
280
cd5f04f5
A
281OBJC_EXTERN uintptr_t _Unwind_GetIP (struct _Unwind_Context *);
282OBJC_EXTERN uintptr_t _Unwind_GetCFA (struct _Unwind_Context *);
283OBJC_EXTERN uintptr_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *);
b3962a83
A
284
285
286// C++ runtime types and functions
8972963c 287// copied from cxxabi.h
b3962a83 288
cd5f04f5
A
289OBJC_EXTERN void *__cxa_allocate_exception(size_t thrown_size);
290OBJC_EXTERN void __cxa_throw(void *exc, void *typeinfo, void (*destructor)(void *)) __attribute__((noreturn));
291OBJC_EXTERN void *__cxa_begin_catch(void *exc);
292OBJC_EXTERN void __cxa_end_catch(void);
293OBJC_EXTERN void __cxa_rethrow(void);
294OBJC_EXTERN void *__cxa_current_exception_type(void);
b3962a83 295
8972963c
A
296#if SUPPORT_ZEROCOST_EXCEPTIONS
297# define CXX_PERSONALITY __gxx_personality_v0
7af964d1 298#else
8972963c 299# define CXX_PERSONALITY __gxx_personality_sj0
7af964d1
A
300#endif
301
cd5f04f5 302OBJC_EXTERN _Unwind_Reason_Code
7af964d1
A
303CXX_PERSONALITY(int version,
304 _Unwind_Action actions,
305 uint64_t exceptionClass,
306 struct _Unwind_Exception *exceptionObject,
307 struct _Unwind_Context *context);
b3962a83
A
308
309
310// objc's internal exception types and data
311
b3962a83
A
312struct objc_typeinfo {
313 // Position of vtable and name fields must match C++ typeinfo object
66799735 314 const void ** __ptrauth_cxx_vtable_pointer vtable; // objc_ehtype_vtable+2
b3962a83
A
315 const char *name; // c++ typeinfo string
316
cd5f04f5 317 Class cls_unremapped;
b3962a83
A
318};
319
320struct objc_exception {
321 id obj;
322 struct objc_typeinfo tinfo;
323};
324
1807f628 325extern "C" {
b3962a83 326
1807f628
A
327__attribute__((used))
328void _objc_exception_noop(void) { }
329__attribute__((used))
330bool _objc_exception_false(void) { return 0; }
331// bool _objc_exception_true(void) { return 1; }
332__attribute__((used))
333void _objc_exception_abort1(void) {
334 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 1);
335}
336__attribute__((used))
337void _objc_exception_abort2(void) {
338 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 2);
339}
340__attribute__((used))
341void _objc_exception_abort3(void) {
342 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 3);
343}
344__attribute__((used))
345void _objc_exception_abort4(void) {
346 _objc_fatal("unexpected call into objc exception typeinfo vtable %d", 4);
347}
348__attribute__((used))
349bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
350 struct objc_typeinfo *throw_tinfo,
351 void **throw_obj_p,
352 unsigned outer);
353}
66799735
A
354
355// C++ pointers to vtables are signed with no extra data.
356// C++ vtable entries are signed with a number derived from the function name.
357// For this fake vtable, we hardcode number as deciphered from the
358// assembly output during libc++abi's build.
359#if __has_feature(ptrauth_calls)
360# define VTABLE_PTR_AUTH "@AUTH(da, 0)"
361# define VTABLE_ENTRY_AUTH(x) "@AUTH(ia," #x ",addr)"
362#else
363# define VTABLE_PTR_AUTH ""
364# define VTABLE_ENTRY_AUTH(x) ""
365#endif
b3962a83 366
66799735
A
367#if __LP64__
368# define PTR ".quad "
369# define TWOPTRSIZE "16"
370#else
371# define PTR ".long "
372# define TWOPTRSIZE "8"
373#endif
b3962a83 374
66799735
A
375// Hand-built vtable for objc exception typeinfo.
376// "OLD" is GNU libcpp, "NEW" is libc++abi.
377
378asm(
379 "\n .cstring"
380 "\n l_.id_str: .asciz \"id\""
381
382 "\n .section __DATA,__const"
383 "\n .globl _OBJC_EHTYPE_id"
384 "\n .globl _objc_ehtype_vtable"
385 "\n .p2align 4"
386
387 "\n _OBJC_EHTYPE_id:"
388 "\n " PTR "(_objc_ehtype_vtable+" TWOPTRSIZE ") " VTABLE_PTR_AUTH
389 "\n " PTR "l_.id_str"
390 "\n " PTR "0"
391
392 "\n _objc_ehtype_vtable:"
393 "\n " PTR "0"
394 // typeinfo's typeinfo - fixme hack
395 "\n " PTR "_OBJC_EHTYPE_id"
396 // destructor and in-place destructor
397 "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(52634)
398 "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(10344)
399 // OLD __is_pointer_p
400 "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(6889)
401 // OLD __is_function_p
402 "\n " PTR "__objc_exception_noop" VTABLE_ENTRY_AUTH(23080)
403 // OLD __do_catch, NEW can_catch
404 "\n " PTR "__objc_exception_do_catch" VTABLE_ENTRY_AUTH(27434)
405 // OLD __do_upcast, NEW search_above_dst
406 "\n " PTR "__objc_exception_false" VTABLE_ENTRY_AUTH(48481)
407 // NEW search_below_dst
408 "\n " PTR "__objc_exception_false" VTABLE_ENTRY_AUTH(41165)
409 // NEW has_unambiguous_public_base (fixme need this?)
410 "\n " PTR "__objc_exception_abort1" VTABLE_ENTRY_AUTH(14357)
411 // paranoia: die if libcxxabi adds anything else
412 "\n " PTR "__objc_exception_abort2"
413 "\n " PTR "__objc_exception_abort3"
414 "\n " PTR "__objc_exception_abort4"
415 );
b3962a83
A
416
417
418
419/***********************************************************************
420* Foundation customization
421**********************************************************************/
422
423/***********************************************************************
424* _objc_default_exception_preprocessor
425* Default exception preprocessor. Expected to be overridden by Foundation.
426**********************************************************************/
427static id _objc_default_exception_preprocessor(id exception)
428{
429 return exception;
430}
431static objc_exception_preprocessor exception_preprocessor = _objc_default_exception_preprocessor;
432
433
434/***********************************************************************
435* _objc_default_exception_matcher
436* Default exception matcher. Expected to be overridden by Foundation.
437**********************************************************************/
438static int _objc_default_exception_matcher(Class catch_cls, id exception)
439{
440 Class cls;
7257e56c
A
441 for (cls = exception->getIsa();
442 cls != nil;
34d5b5e8 443 cls = cls->getSuperclass())
b3962a83
A
444 {
445 if (cls == catch_cls) return 1;
446 }
447
448 return 0;
449}
450static objc_exception_matcher exception_matcher = _objc_default_exception_matcher;
451
452
453/***********************************************************************
454* _objc_default_uncaught_exception_handler
455* Default uncaught exception handler. Expected to be overridden by Foundation.
456**********************************************************************/
457static void _objc_default_uncaught_exception_handler(id exception)
458{
459}
460static objc_uncaught_exception_handler uncaught_handler = _objc_default_uncaught_exception_handler;
461
462
463/***********************************************************************
464* objc_setExceptionPreprocessor
465* Set a handler for preprocessing Objective-C exceptions.
466* Returns the previous handler.
467**********************************************************************/
468objc_exception_preprocessor
469objc_setExceptionPreprocessor(objc_exception_preprocessor fn)
470{
471 objc_exception_preprocessor result = exception_preprocessor;
472 exception_preprocessor = fn;
473 return result;
474}
475
476
477/***********************************************************************
478* objc_setExceptionMatcher
479* Set a handler for matching Objective-C exceptions.
480* Returns the previous handler.
481**********************************************************************/
482objc_exception_matcher
483objc_setExceptionMatcher(objc_exception_matcher fn)
484{
485 objc_exception_matcher result = exception_matcher;
486 exception_matcher = fn;
487 return result;
488}
489
490
491/***********************************************************************
492* objc_setUncaughtExceptionHandler
493* Set a handler for uncaught Objective-C exceptions.
494* Returns the previous handler.
495**********************************************************************/
496objc_uncaught_exception_handler
497objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
498{
499 objc_uncaught_exception_handler result = uncaught_handler;
500 uncaught_handler = fn;
501 return result;
502}
503
504
505/***********************************************************************
506* Exception personality
507**********************************************************************/
508
509static void call_alt_handlers(struct _Unwind_Context *ctx);
510
511_Unwind_Reason_Code
8972963c
A
512__objc_personality_v0(int version,
513 _Unwind_Action actions,
514 uint64_t exceptionClass,
515 struct _Unwind_Exception *exceptionObject,
516 struct _Unwind_Context *context)
b3962a83 517{
31875a97 518 bool unwinding = ((actions & _UA_CLEANUP_PHASE) ||
b3962a83
A
519 (actions & _UA_FORCE_UNWIND));
520
521 if (PrintExceptions) {
522 _objc_inform("EXCEPTIONS: %s through frame [ip=%p sp=%p] "
523 "for exception %p",
524 unwinding ? "unwinding" : "searching",
525 (void*)(_Unwind_GetIP(context)-1),
526 (void*)_Unwind_GetCFA(context), exceptionObject);
527 }
528
529 // If we're executing the unwind, call this frame's alt handlers, if any.
530 if (unwinding) {
531 call_alt_handlers(context);
532 }
533
534 // Let C++ handle the unwind itself.
7af964d1
A
535 return CXX_PERSONALITY(version, actions, exceptionClass,
536 exceptionObject, context);
b3962a83
A
537}
538
539
540/***********************************************************************
541* Compiler ABI
542**********************************************************************/
543
7257e56c
A
544static void _objc_exception_destructor(void *exc_gen)
545{
546 // Release the retain from objc_exception_throw().
547
b3962a83 548 struct objc_exception *exc = (struct objc_exception *)exc_gen;
7257e56c
A
549 id obj = exc->obj;
550
551 if (PrintExceptions) {
552 _objc_inform("EXCEPTIONS: releasing completed exception %p (object %p, a %s)",
553 exc, obj, object_getClassName(obj));
554 }
555
c1e772c4 556 [obj release];
b3962a83
A
557}
558
559
560void objc_exception_throw(id obj)
561{
cd5f04f5 562 struct objc_exception *exc = (struct objc_exception *)
b3962a83
A
563 __cxa_allocate_exception(sizeof(struct objc_exception));
564
7257e56c
A
565 obj = (*exception_preprocessor)(obj);
566
c1e772c4
A
567 // Retain the exception object during unwinding
568 // because otherwise an autorelease pool pop can cause a crash
569 [obj retain];
b3962a83 570
7257e56c 571 exc->obj = obj;
7af964d1 572 exc->tinfo.vtable = objc_ehtype_vtable+2;
b3962a83 573 exc->tinfo.name = object_getClassName(obj);
7257e56c 574 exc->tinfo.cls_unremapped = obj ? obj->getIsa() : Nil;
b3962a83
A
575
576 if (PrintExceptions) {
577 _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
7257e56c 578 exc, (void*)obj, object_getClassName(obj));
b3962a83 579 }
8972963c
A
580
581 if (PrintExceptionThrow) {
582 if (!PrintExceptions)
583 _objc_inform("EXCEPTIONS: throwing %p (object %p, a %s)",
7257e56c 584 exc, (void*)obj, object_getClassName(obj));
8972963c
A
585 void* callstack[500];
586 int frameCount = backtrace(callstack, 500);
587 backtrace_symbols_fd(callstack, frameCount, fileno(stderr));
588 }
7af964d1
A
589
590 OBJC_RUNTIME_OBJC_EXCEPTION_THROW(obj); // dtrace probe to log throw activity
b3962a83 591 __cxa_throw(exc, &exc->tinfo, &_objc_exception_destructor);
7af964d1 592 __builtin_trap();
b3962a83
A
593}
594
595
596void objc_exception_rethrow(void)
597{
598 // exception_preprocessor doesn't get another bite of the apple
599 if (PrintExceptions) {
600 _objc_inform("EXCEPTIONS: rethrowing current exception");
601 }
7af964d1
A
602
603 OBJC_RUNTIME_OBJC_EXCEPTION_RETHROW(); // dtrace probe to log throw activity.
b3962a83 604 __cxa_rethrow();
7af964d1 605 __builtin_trap();
b3962a83
A
606}
607
608
609id objc_begin_catch(void *exc_gen)
610{
611 if (PrintExceptions) {
612 _objc_inform("EXCEPTIONS: handling exception %p at %p",
613 exc_gen, __builtin_return_address(0));
614 }
615 // NOT actually an id in the catch(...) case!
616 return (id)__cxa_begin_catch(exc_gen);
617}
618
619
620void objc_end_catch(void)
621{
622 if (PrintExceptions) {
623 _objc_inform("EXCEPTIONS: finishing handler");
624 }
625 __cxa_end_catch();
626}
627
628
cd5f04f5 629// `outer` is not passed by the new libcxxabi
66799735
A
630bool _objc_exception_do_catch(struct objc_typeinfo *catch_tinfo,
631 struct objc_typeinfo *throw_tinfo,
632 void **throw_obj_p,
633 unsigned outer UNAVAILABLE_ATTRIBUTE)
b3962a83
A
634{
635 id exception;
636
7af964d1 637 if (throw_tinfo->vtable != objc_ehtype_vtable+2) {
b3962a83 638 // Only objc types can be caught here.
7af964d1 639 if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(?)");
cd5f04f5 640 return false;
b3962a83
A
641 }
642
cd5f04f5
A
643 // Adjust exception pointer.
644 // Old libcppabi: we lied about __is_pointer_p() so we have to do it here
645 // New libcxxabi: we have to do it here regardless
646 *throw_obj_p = **(void***)throw_obj_p;
647
b3962a83
A
648 // `catch (id)` always catches objc types.
649 if (catch_tinfo == &OBJC_EHTYPE_id) {
650 if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(id)");
cd5f04f5 651 return true;
b3962a83
A
652 }
653
654 exception = *(id *)throw_obj_p;
cd5f04f5
A
655
656 Class handler_cls = _class_remap(catch_tinfo->cls_unremapped);
657 if (!handler_cls) {
658 // catch handler's class is weak-linked and missing. Not a match.
659 }
660 else if ((*exception_matcher)(handler_cls, exception)) {
b3962a83 661 if (PrintExceptions) _objc_inform("EXCEPTIONS: catch(%s)",
8070259c 662 handler_cls->nameForLogging());
cd5f04f5 663 return true;
b3962a83
A
664 }
665
7af964d1 666 if (PrintExceptions) _objc_inform("EXCEPTIONS: skipping catch(%s)",
8070259c 667 handler_cls->nameForLogging());
7af964d1 668
cd5f04f5 669 return false;
b3962a83
A
670}
671
672
673/***********************************************************************
674* _objc_terminate
675* Custom std::terminate handler.
676*
677* The uncaught exception callback is implemented as a std::terminate handler.
678* 1. Check if there's an active exception
679* 2. If so, check if it's an Objective-C exception
680* 3. If so, call our registered callback with the object.
681* 4. Finally, call the previous terminate handler.
682**********************************************************************/
7257e56c 683static void (*old_terminate)(void) = nil;
b3962a83
A
684static void _objc_terminate(void)
685{
686 if (PrintExceptions) {
687 _objc_inform("EXCEPTIONS: terminating");
688 }
689
690 if (! __cxa_current_exception_type()) {
691 // No current exception.
692 (*old_terminate)();
693 }
694 else {
695 // There is a current exception. Check if it's an objc exception.
696 @try {
697 __cxa_rethrow();
698 } @catch (id e) {
699 // It's an objc object. Call Foundation's handler, if any.
7257e56c 700 (*uncaught_handler)((id)e);
b3962a83
A
701 (*old_terminate)();
702 } @catch (...) {
703 // It's not an objc object. Continue to C++ terminate.
704 (*old_terminate)();
705 }
706 }
707}
708
709
cd5f04f5
A
710/***********************************************************************
711* objc_terminate
712* Calls std::terminate for clients who don't link to C++ themselves.
713* Called by the compiler if an exception is thrown
714* from a context where exceptions may not be thrown.
715**********************************************************************/
716void objc_terminate(void)
717{
718 std::terminate();
719}
720
721
b3962a83 722/***********************************************************************
7af964d1 723* alt handler support - zerocost implementation only
b3962a83
A
724**********************************************************************/
725
8972963c 726#if !SUPPORT_ALT_HANDLERS
7af964d1 727
cd5f04f5 728void _destroyAltHandlerList(struct alt_handler_list *list)
7af964d1
A
729{
730}
731
732static void call_alt_handlers(struct _Unwind_Context *ctx)
733{
734 // unsupported in sjlj environments
735}
736
737#else
738
739#include <libunwind.h>
8972963c
A
740#include <execinfo.h>
741#include <dispatch/dispatch.h>
b3962a83
A
742
743// Dwarf eh data encodings
744#define DW_EH_PE_omit 0xff // no data follows
745
746#define DW_EH_PE_absptr 0x00
747#define DW_EH_PE_uleb128 0x01
748#define DW_EH_PE_udata2 0x02
749#define DW_EH_PE_udata4 0x03
750#define DW_EH_PE_udata8 0x04
751#define DW_EH_PE_sleb128 0x09
752#define DW_EH_PE_sdata2 0x0A
753#define DW_EH_PE_sdata4 0x0B
754#define DW_EH_PE_sdata8 0x0C
755
756#define DW_EH_PE_pcrel 0x10
757#define DW_EH_PE_textrel 0x20
758#define DW_EH_PE_datarel 0x30
759#define DW_EH_PE_funcrel 0x40
760#define DW_EH_PE_aligned 0x50 // fixme
761
762#define DW_EH_PE_indirect 0x80 // gcc extension
763
764
765/***********************************************************************
766* read_uleb
767* Read a LEB-encoded unsigned integer from the address stored in *pp.
768* Increments *pp past the bytes read.
769* Adapted from DWARF Debugging Information Format 1.1, appendix 4
770**********************************************************************/
771static uintptr_t read_uleb(uintptr_t *pp)
772{
773 uintptr_t result = 0;
774 uintptr_t shift = 0;
775 unsigned char byte;
776 do {
777 byte = *(const unsigned char *)(*pp)++;
778 result |= (byte & 0x7f) << shift;
779 shift += 7;
780 } while (byte & 0x80);
781 return result;
782}
783
784
785/***********************************************************************
786* read_sleb
787* Read a LEB-encoded signed integer from the address stored in *pp.
788* Increments *pp past the bytes read.
789* Adapted from DWARF Debugging Information Format 1.1, appendix 4
790**********************************************************************/
791static intptr_t read_sleb(uintptr_t *pp)
792{
793 uintptr_t result = 0;
794 uintptr_t shift = 0;
795 unsigned char byte;
796 do {
797 byte = *(const unsigned char *)(*pp)++;
798 result |= (byte & 0x7f) << shift;
799 shift += 7;
800 } while (byte & 0x80);
801 if ((shift < 8*sizeof(intptr_t)) && (byte & 0x40)) {
802 result |= ((intptr_t)-1) << shift;
803 }
804 return result;
805}
806
807
b3962a83
A
808/***********************************************************************
809* read_address
810* Reads an encoded address from the address stored in *pp.
811* Increments *pp past the bytes read.
812* The data is interpreted according to the given dwarf encoding
813* and base addresses.
814**********************************************************************/
815static uintptr_t read_address(uintptr_t *pp,
7af964d1 816 const struct dwarf_eh_bases *bases,
b3962a83
A
817 unsigned char encoding)
818{
819 uintptr_t result = 0;
820 uintptr_t oldp = *pp;
821
822 // fixme need DW_EH_PE_aligned?
823
824#define READ(type) \
825 result = *(type *)(*pp); \
826 *pp += sizeof(type);
827
828 if (encoding == DW_EH_PE_omit) return 0;
829
830 switch (encoding & 0x0f) {
831 case DW_EH_PE_absptr:
832 READ(uintptr_t);
833 break;
834 case DW_EH_PE_uleb128:
835 result = read_uleb(pp);
836 break;
837 case DW_EH_PE_udata2:
838 READ(uint16_t);
839 break;
840 case DW_EH_PE_udata4:
841 READ(uint32_t);
842 break;
843#if __LP64__
844 case DW_EH_PE_udata8:
845 READ(uint64_t);
846 break;
847#endif
848 case DW_EH_PE_sleb128:
849 result = read_sleb(pp);
850 break;
851 case DW_EH_PE_sdata2:
852 READ(int16_t);
853 break;
854 case DW_EH_PE_sdata4:
855 READ(int32_t);
856 break;
857#if __LP64__
858 case DW_EH_PE_sdata8:
859 READ(int64_t);
860 break;
861#endif
862 default:
863 _objc_inform("unknown DWARF EH encoding 0x%x at %p",
864 encoding, (void *)*pp);
865 break;
866 }
867
868#undef READ
869
870 if (result) {
871 switch (encoding & 0x70) {
872 case DW_EH_PE_pcrel:
873 // fixme correct?
874 result += (uintptr_t)oldp;
875 break;
876 case DW_EH_PE_textrel:
877 result += bases->tbase;
878 break;
879 case DW_EH_PE_datarel:
880 result += bases->dbase;
881 break;
882 case DW_EH_PE_funcrel:
883 result += bases->func;
884 break;
885 case DW_EH_PE_aligned:
886 _objc_inform("unknown DWARF EH encoding 0x%x at %p",
887 encoding, (void *)*pp);
888 break;
889 default:
890 // no adjustment
891 break;
892 }
893
894 if (encoding & DW_EH_PE_indirect) {
895 result = *(uintptr_t *)result;
896 }
897 }
898
899 return (uintptr_t)result;
900}
901
902
cd5f04f5
A
903struct frame_ips {
904 uintptr_t start;
905 uintptr_t end;
906};
907struct frame_range {
908 uintptr_t ip_start;
909 uintptr_t ip_end;
910 uintptr_t cfa;
7257e56c 911 // precise ranges within ip_start..ip_end; nil or {0,0} terminated
cd5f04f5
A
912 frame_ips *ips;
913};
914
915
7af964d1
A
916static bool isObjCExceptionCatcher(uintptr_t lsda, uintptr_t ip,
917 const struct dwarf_eh_bases* bases,
cd5f04f5 918 struct frame_range *frame)
b3962a83 919{
7af964d1 920 unsigned char LPStart_enc = *(const unsigned char *)lsda++;
b3962a83 921
b3962a83 922 if (LPStart_enc != DW_EH_PE_omit) {
7af964d1 923 read_address(&lsda, bases, LPStart_enc); // LPStart
b3962a83
A
924 }
925
926 unsigned char TType_enc = *(const unsigned char *)lsda++;
927 if (TType_enc != DW_EH_PE_omit) {
928 read_uleb(&lsda); // TType
929 }
930
931 unsigned char call_site_enc = *(const unsigned char *)lsda++;
932 uintptr_t length = read_uleb(&lsda);
933 uintptr_t call_site_table = lsda;
934 uintptr_t call_site_table_end = call_site_table + length;
935 uintptr_t action_record_table = call_site_table_end;
936
937 uintptr_t action_record = 0;
938 uintptr_t p = call_site_table;
939
cd5f04f5
A
940 uintptr_t try_start;
941 uintptr_t try_end;
942 uintptr_t try_landing_pad;
943
b3962a83 944 while (p < call_site_table_end) {
cd5f04f5
A
945 uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func;
946 uintptr_t len = read_address(&p, bases, call_site_enc);
947 uintptr_t pad = read_address(&p, bases, call_site_enc);
948 uintptr_t action = read_uleb(&p);
b3962a83 949
cd5f04f5 950 if (ip < start) {
b3962a83 951 // no more source ranges
7af964d1 952 return false;
b3962a83 953 }
cd5f04f5 954 else if (ip < start + len) {
b3962a83 955 // found the range
7af964d1 956 if (!pad) return false; // ...but it has no landing pad
b3962a83 957 // found the landing pad
b3962a83 958 action_record = action ? action_record_table + action - 1 : 0;
cd5f04f5
A
959 try_start = start;
960 try_end = start + len;
961 try_landing_pad = pad;
b3962a83
A
962 break;
963 }
964 }
965
7af964d1 966 if (!action_record) return false; // no catch handlers
b3962a83
A
967
968 // has handlers, destructors, and/or throws specifications
969 // Use this frame if it has any handlers
7af964d1 970 bool has_handler = false;
b3962a83
A
971 p = action_record;
972 intptr_t offset;
973 do {
974 intptr_t filter = read_sleb(&p);
975 uintptr_t temp = p;
976 offset = read_sleb(&temp);
977 p += offset;
978
979 if (filter < 0) {
980 // throws specification - ignore
981 } else if (filter == 0) {
982 // destructor - ignore
983 } else /* filter >= 0 */ {
984 // catch handler - use this frame
7af964d1 985 has_handler = true;
b3962a83
A
986 break;
987 }
988 } while (offset);
cd5f04f5
A
989
990 if (!has_handler) return false;
b3962a83 991
cd5f04f5
A
992 // Count the number of source ranges with the same landing pad as our match
993 unsigned int range_count = 0;
994 p = call_site_table;
995 while (p < call_site_table_end) {
996 /*start*/ read_address(&p, bases, call_site_enc)/*+bases->func*/;
997 /*len*/ read_address(&p, bases, call_site_enc);
998 uintptr_t pad = read_address(&p, bases, call_site_enc);
999 /*action*/ read_uleb(&p);
1000
1001 if (pad == try_landing_pad) {
1002 range_count++;
1003 }
1004 }
7af964d1 1005
cd5f04f5
A
1006 if (range_count == 1) {
1007 // No other source ranges with the same landing pad. We're done here.
7257e56c 1008 frame->ips = nil;
cd5f04f5
A
1009 }
1010 else {
1011 // Record all ranges with the same landing pad as our match.
1012 frame->ips = (frame_ips *)
31875a97 1013 malloc((range_count + 1) * sizeof(frame->ips[0]));
cd5f04f5
A
1014 unsigned int r = 0;
1015 p = call_site_table;
1016 while (p < call_site_table_end) {
1017 uintptr_t start = read_address(&p, bases, call_site_enc)+bases->func;
1018 uintptr_t len = read_address(&p, bases, call_site_enc);
1019 uintptr_t pad = read_address(&p, bases, call_site_enc);
1020 /*action*/ read_uleb(&p);
1021
1022 if (pad == try_landing_pad) {
1023 if (start < try_start) try_start = start;
1024 if (start+len > try_end) try_end = start+len;
1025 frame->ips[r].start = start;
1026 frame->ips[r].end = start+len;
1027 r++;
1028 }
1029 }
1030
1031 frame->ips[r].start = 0;
1032 frame->ips[r].end = 0;
1033 }
1034
1035 frame->ip_start = try_start;
1036 frame->ip_end = try_end;
1037
1038 return true;
1039}
7af964d1 1040
b3962a83 1041
7af964d1
A
1042static struct frame_range findHandler(void)
1043{
1044 // walk stack looking for frame with objc catch handler
1045 unw_context_t uc;
1046 unw_cursor_t cursor;
1047 unw_proc_info_t info;
1048 unw_getcontext(&uc);
1049 unw_init_local(&cursor, &uc);
1050 while ( (unw_step(&cursor) > 0) && (unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) ) {
1051 // must use objc personality handler
1052 if ( info.handler != (uintptr_t)__objc_personality_v0 )
1053 continue;
1054 // must have landing pad
1055 if ( info.lsda == 0 )
1056 continue;
1057 // must have landing pad that catches objc exceptions
1058 struct dwarf_eh_bases bases;
1059 bases.tbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
1060 bases.dbase = 0; // from unwind-dw2-fde-darwin.c:examine_objects()
1061 bases.func = info.start_ip;
1062 unw_word_t ip;
1063 unw_get_reg(&cursor, UNW_REG_IP, &ip);
fe06a513 1064 ip -= 1;
cd5f04f5
A
1065 struct frame_range try_range = {0, 0, 0, 0};
1066 if ( isObjCExceptionCatcher(info.lsda, ip, &bases, &try_range) ) {
7af964d1
A
1067 unw_word_t cfa;
1068 unw_get_reg(&cursor, UNW_REG_SP, &cfa);
cd5f04f5
A
1069 try_range.cfa = cfa;
1070 return try_range;
7af964d1
A
1071 }
1072 }
b3962a83 1073
cd5f04f5 1074 return (struct frame_range){0, 0, 0, 0};
b3962a83
A
1075}
1076
1077
1078// This data structure assumes the number of
1079// active alt handlers per frame is small.
8972963c
A
1080
1081// for OBJC_DEBUG_ALT_HANDLERS, record the call to objc_addExceptionHandler.
1082#define BACKTRACE_COUNT 46
1083#define THREADNAME_COUNT 64
1084struct alt_handler_debug {
1085 uintptr_t token;
1086 int backtraceSize;
1087 void *backtrace[BACKTRACE_COUNT];
1088 char thread[THREADNAME_COUNT];
1089 char queue[THREADNAME_COUNT];
1090};
1091
b3962a83 1092struct alt_handler_data {
cd5f04f5 1093 struct frame_range frame;
b3962a83
A
1094 objc_exception_handler fn;
1095 void *context;
8972963c 1096 struct alt_handler_debug *debug;
b3962a83
A
1097};
1098
1099struct alt_handler_list {
1100 unsigned int allocated;
1101 unsigned int used;
1102 struct alt_handler_data *handlers;
8972963c 1103 struct alt_handler_list *next_DEBUGONLY;
b3962a83
A
1104};
1105
8972963c
A
1106static struct alt_handler_list *DebugLists;
1107static uintptr_t DebugCounter;
1108
c1e772c4
A
1109__attribute__((noinline, noreturn))
1110void alt_handler_error(uintptr_t token);
b3962a83
A
1111
1112static struct alt_handler_list *
31875a97 1113fetch_handler_list(bool create)
b3962a83
A
1114{
1115 _objc_pthread_data *data = _objc_fetch_pthread_data(create);
7257e56c 1116 if (!data) return nil;
b3962a83
A
1117
1118 struct alt_handler_list *list = data->handlerList;
1119 if (!list) {
7257e56c 1120 if (!create) return nil;
31875a97 1121 list = (struct alt_handler_list *)calloc(1, sizeof(*list));
b3962a83 1122 data->handlerList = list;
8972963c
A
1123
1124 if (DebugAltHandlers) {
1125 // Save this list so the debug code can find it from other threads
bd8dfcfc 1126 mutex_locker_t lock(AltHandlerDebugLock);
8972963c
A
1127 list->next_DEBUGONLY = DebugLists;
1128 DebugLists = list;
8972963c 1129 }
b3962a83
A
1130 }
1131
1132 return list;
1133}
1134
1135
cd5f04f5 1136void _destroyAltHandlerList(struct alt_handler_list *list)
b3962a83
A
1137{
1138 if (list) {
8972963c
A
1139 if (DebugAltHandlers) {
1140 // Detach from the list-of-lists.
bd8dfcfc 1141 mutex_locker_t lock(AltHandlerDebugLock);
8972963c
A
1142 struct alt_handler_list **listp = &DebugLists;
1143 while (*listp && *listp != list) listp = &(*listp)->next_DEBUGONLY;
1144 if (*listp) *listp = (*listp)->next_DEBUGONLY;
8972963c
A
1145 }
1146
b3962a83 1147 if (list->handlers) {
cd5f04f5
A
1148 for (unsigned int i = 0; i < list->allocated; i++) {
1149 if (list->handlers[i].frame.ips) {
31875a97 1150 free(list->handlers[i].frame.ips);
cd5f04f5
A
1151 }
1152 }
31875a97 1153 free(list->handlers);
b3962a83 1154 }
31875a97 1155 free(list);
b3962a83
A
1156 }
1157}
1158
1159
1160uintptr_t objc_addExceptionHandler(objc_exception_handler fn, void *context)
1161{
b3962a83 1162 // Find the closest enclosing frame with objc catch handlers
7af964d1 1163 struct frame_range target_frame = findHandler();
b3962a83
A
1164 if (!target_frame.ip_start) {
1165 // No suitable enclosing handler found.
1166 return 0;
1167 }
1168
1169 // Record this alt handler for the discovered frame.
1170 struct alt_handler_list *list = fetch_handler_list(YES);
1171 unsigned int i = 0;
1172
1173 if (list->used == list->allocated) {
1174 list->allocated = list->allocated*2 ?: 4;
cd5f04f5 1175 list->handlers = (struct alt_handler_data *)
31875a97 1176 realloc(list->handlers,
cd5f04f5 1177 list->allocated * sizeof(list->handlers[0]));
b3962a83
A
1178 bzero(&list->handlers[list->used], (list->allocated - list->used) * sizeof(list->handlers[0]));
1179 i = list->used;
1180 }
1181 else {
1182 for (i = 0; i < list->allocated; i++) {
cd5f04f5
A
1183 if (list->handlers[i].frame.ip_start == 0 &&
1184 list->handlers[i].frame.ip_end == 0 &&
1185 list->handlers[i].frame.cfa == 0)
b3962a83
A
1186 {
1187 break;
1188 }
1189 }
1190 if (i == list->allocated) {
1191 _objc_fatal("alt handlers in objc runtime are buggy!");
1192 }
1193 }
1194
1195 struct alt_handler_data *data = &list->handlers[i];
1196
cd5f04f5 1197 data->frame = target_frame;
b3962a83
A
1198 data->fn = fn;
1199 data->context = context;
1200 list->used++;
1201
8972963c
A
1202 uintptr_t token = i+1;
1203
1204 if (DebugAltHandlers) {
1205 // Record backtrace in case this handler is misused later.
bd8dfcfc 1206 mutex_locker_t lock(AltHandlerDebugLock);
8972963c
A
1207
1208 token = DebugCounter++;
1209 if (token == 0) token = DebugCounter++;
1210
1211 if (!data->debug) {
cd5f04f5 1212 data->debug = (struct alt_handler_debug *)
31875a97 1213 calloc(sizeof(*data->debug), 1);
8972963c
A
1214 } else {
1215 bzero(data->debug, sizeof(*data->debug));
1216 }
1217
1807f628
A
1218 pthread_getname_np(objc_thread_self(), data->debug->thread, THREADNAME_COUNT);
1219 strlcpy(data->debug->queue,
1220 dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL),
8972963c
A
1221 THREADNAME_COUNT);
1222 data->debug->backtraceSize =
1223 backtrace(data->debug->backtrace, BACKTRACE_COUNT);
1224 data->debug->token = token;
8972963c
A
1225 }
1226
b3962a83 1227 if (PrintAltHandlers) {
8972963c
A
1228 _objc_inform("ALT HANDLERS: installing alt handler #%lu %p(%p) on "
1229 "frame [ip=%p..%p sp=%p]", (unsigned long)token,
cd5f04f5
A
1230 data->fn, data->context, (void *)data->frame.ip_start,
1231 (void *)data->frame.ip_end, (void *)data->frame.cfa);
1232 if (data->frame.ips) {
1233 unsigned int r = 0;
1234 while (1) {
1235 uintptr_t start = data->frame.ips[r].start;
1236 uintptr_t end = data->frame.ips[r].end;
1237 r++;
1238 if (start == 0 && end == 0) break;
1239 _objc_inform("ALT HANDLERS: ip=%p..%p",
1240 (void*)start, (void*)end);
1241 }
1242 }
b3962a83
A
1243 }
1244
1245 if (list->used > 1000) {
1246 static int warned = 0;
1247 if (!warned) {
7af964d1 1248 _objc_inform("ALT HANDLERS: *** over 1000 alt handlers installed; "
b3962a83
A
1249 "this is probably a bug");
1250 warned = 1;
1251 }
1252 }
1253
8972963c 1254 return token;
b3962a83
A
1255}
1256
1257
1258void objc_removeExceptionHandler(uintptr_t token)
1259{
1260 if (!token) {
1261 // objc_addExceptionHandler failed
1262 return;
1263 }
b3962a83
A
1264
1265 struct alt_handler_list *list = fetch_handler_list(NO);
8972963c
A
1266 if (!list || !list->handlers) {
1267 // no alt handlers active
1268 alt_handler_error(token);
8972963c
A
1269 }
1270
1271 uintptr_t i = token-1;
1272
1273 if (DebugAltHandlers) {
1274 // search for the token instead of using token-1
1275 for (i = 0; i < list->allocated; i++) {
1276 struct alt_handler_data *data = &list->handlers[i];
1277 if (data->debug && data->debug->token == token) break;
b3962a83 1278 }
b3962a83 1279 }
8972963c 1280
b3962a83 1281 if (i >= list->allocated) {
8972963c
A
1282 // token out of range
1283 alt_handler_error(token);
b3962a83
A
1284 }
1285
1286 struct alt_handler_data *data = &list->handlers[i];
8972963c 1287
cd5f04f5 1288 if (data->frame.ip_start == 0 && data->frame.ip_end == 0 && data->frame.cfa == 0) {
8972963c
A
1289 // token in range, but invalid
1290 alt_handler_error(token);
8972963c
A
1291 }
1292
b3962a83 1293 if (PrintAltHandlers) {
8972963c
A
1294 _objc_inform("ALT HANDLERS: removing alt handler #%lu %p(%p) on "
1295 "frame [ip=%p..%p sp=%p]", (unsigned long)token,
cd5f04f5
A
1296 data->fn, data->context, (void *)data->frame.ip_start,
1297 (void *)data->frame.ip_end, (void *)data->frame.cfa);
b3962a83 1298 }
8972963c 1299
31875a97
A
1300 if (data->debug) free(data->debug);
1301 if (data->frame.ips) free(data->frame.ips);
b3962a83
A
1302 bzero(data, sizeof(*data));
1303 list->used--;
1304}
1305
8972963c 1306
c1e772c4
A
1307BREAKPOINT_FUNCTION(
1308void objc_alt_handler_error(void));
1309
1310__attribute__((noinline, noreturn))
cd5f04f5 1311void alt_handler_error(uintptr_t token)
8972963c 1312{
c1e772c4
A
1313 _objc_inform
1314 ("objc_removeExceptionHandler() called with unknown alt handler; "
1315 "this is probably a bug in multithreaded AppKit use. "
1316 "Set environment variable OBJC_DEBUG_ALT_HANDLERS=YES "
1317 "or break in objc_alt_handler_error() to debug.");
8972963c 1318
c1e772c4 1319 if (DebugAltHandlers) {
bd8dfcfc 1320 AltHandlerDebugLock.lock();
c1e772c4
A
1321
1322 // Search other threads' alt handler lists for this handler.
1323 struct alt_handler_list *list;
1324 for (list = DebugLists; list; list = list->next_DEBUGONLY) {
1325 unsigned h;
1326 for (h = 0; h < list->allocated; h++) {
1327 struct alt_handler_data *data = &list->handlers[h];
1328 if (data->debug && data->debug->token == token) {
1329 // found it
1330 int i;
1331
1332 // Build a string from the recorded backtrace
1333 char *symbolString;
1334 char **symbols =
1335 backtrace_symbols(data->debug->backtrace,
1336 data->debug->backtraceSize);
1337 size_t len = 1;
1338 for (i = 0; i < data->debug->backtraceSize; i++){
1339 len += 4 + strlen(symbols[i]) + 1;
1340 }
1341 symbolString = (char *)calloc(len, 1);
1342 for (i = 0; i < data->debug->backtraceSize; i++){
1343 strcat(symbolString, " ");
1344 strcat(symbolString, symbols[i]);
1345 strcat(symbolString, "\n");
1346 }
1347
1348 free(symbols);
1349
1350 _objc_inform_now_and_on_crash
1351 ("The matching objc_addExceptionHandler() was called "
1352 "by:\nThread '%s': Dispatch queue: '%s': \n%s",
1353 data->debug->thread, data->debug->queue, symbolString);
1354
1355 goto done;
8972963c 1356 }
8972963c
A
1357 }
1358 }
c1e772c4 1359 done:
bd8dfcfc 1360 AltHandlerDebugLock.unlock();
8972963c
A
1361 }
1362
8972963c 1363
8972963c 1364 objc_alt_handler_error();
c1e772c4
A
1365
1366 _objc_fatal
1367 ("objc_removeExceptionHandler() called with unknown alt handler; "
1368 "this is probably a bug in multithreaded AppKit use. ");
8972963c 1369}
b3962a83
A
1370
1371// called in order registered, to match 32-bit _NSAddAltHandler2
1372// fixme reverse registration order matches c++ destructors better
1373static void call_alt_handlers(struct _Unwind_Context *ctx)
1374{
1375 uintptr_t ip = _Unwind_GetIP(ctx) - 1;
1376 uintptr_t cfa = _Unwind_GetCFA(ctx);
1377 unsigned int i;
390d5862 1378
b3962a83
A
1379 struct alt_handler_list *list = fetch_handler_list(NO);
1380 if (!list || list->used == 0) return;
1381
1382 for (i = 0; i < list->allocated; i++) {
1383 struct alt_handler_data *data = &list->handlers[i];
cd5f04f5 1384 if (ip >= data->frame.ip_start && ip < data->frame.ip_end && data->frame.cfa == cfa)
b3962a83 1385 {
cd5f04f5
A
1386 if (data->frame.ips) {
1387 unsigned int r = 0;
1388 bool found;
1389 while (1) {
1390 uintptr_t start = data->frame.ips[r].start;
1391 uintptr_t end = data->frame.ips[r].end;
1392 r++;
1393 if (start == 0 && end == 0) {
1394 found = false;
1395 break;
1396 }
1397 if (ip >= start && ip < end) {
1398 found = true;
1399 break;
1400 }
1401 }
1402 if (!found) continue;
1403 }
1404
b3962a83
A
1405 // Copy and clear before the callback, in case the
1406 // callback manipulates the alt handler list.
1407 struct alt_handler_data copy = *data;
1408 bzero(data, sizeof(*data));
1409 list->used--;
1410 if (PrintExceptions || PrintAltHandlers) {
1411 _objc_inform("EXCEPTIONS: calling alt handler %p(%p) from "
1412 "frame [ip=%p..%p sp=%p]", copy.fn, copy.context,
cd5f04f5
A
1413 (void *)copy.frame.ip_start,
1414 (void *)copy.frame.ip_end,
1415 (void *)copy.frame.cfa);
b3962a83
A
1416 }
1417 if (copy.fn) (*copy.fn)(nil, copy.context);
31875a97 1418 if (copy.frame.ips) free(copy.frame.ips);
b3962a83
A
1419 }
1420 }
1421}
1422
8972963c 1423// SUPPORT_ALT_HANDLERS
7af964d1
A
1424#endif
1425
b3962a83
A
1426
1427/***********************************************************************
1428* exception_init
1429* Initialize libobjc's exception handling system.
1430* Called by map_images().
1431**********************************************************************/
cd5f04f5 1432void exception_init(void)
b3962a83 1433{
cd5f04f5 1434 old_terminate = std::set_terminate(&_objc_terminate);
b3962a83
A
1435}
1436
1437
7af964d1 1438// __OBJC2__
b3962a83 1439#endif
bd8dfcfc
A
1440
1441// Define this everywhere even if it isn't used, to simplify fork() safety code
1442mutex_t AltHandlerDebugLock;