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