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