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