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