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