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