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