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