]> git.ipfire.org Git - thirdparty/gcc.git/blob - libobjc/exception.c
objc-exception.h: New file.
[thirdparty/gcc.git] / libobjc / exception.c
1 /* The implementation of exception handling primitives for Objective-C.
2 Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 3, or (at your option) any
9 later version.
10
11 GCC is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
15
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
19
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 <http://www.gnu.org/licenses/>. */
24
25 #include <stdlib.h>
26 #include "config.h"
27 #include "objc/objc-api.h"
28 #include "objc/objc-exception.h"
29 #include "unwind.h"
30 #include "unwind-pe.h"
31
32 /* This hook allows libraries to sepecify special actions when an
33 exception is thrown without a handler in place. This is deprecated
34 in favour of objc_set_uncaught_exception_handler ().
35 */
36 void (*_objc_unexpected_exception) (id exception); /* !T:SAFE */
37
38
39 /* 'is_kind_of_exception_matcher' is our default exception matcher -
40 it determines if the object 'exception' is of class 'catch_class',
41 or of a subclass.
42 */
43 static int
44 is_kind_of_exception_matcher (Class catch_class, id exception)
45 {
46 /* NULL catch_class is catch-all (eg, @catch (id object)). */
47 if (catch_class == Nil)
48 return 1;
49
50 /* If exception is nil (eg, @throw nil;), then it can only be catched
51 * by a catch-all (eg, @catch (id object)).
52 */
53 if (exception != nil)
54 {
55 Class c;
56
57 for (c = exception->class_pointer; c != Nil;
58 c = class_get_super_class (c))
59 if (c == catch_class)
60 return 1;
61 }
62 return 0;
63 }
64
65 /* The exception matcher currently in use. */
66 static objc_exception_matcher
67 __objc_exception_matcher = is_kind_of_exception_matcher;
68
69 objc_exception_matcher
70 objc_set_exception_matcher (objc_exception_matcher new_matcher)
71 {
72 objc_exception_matcher old_matcher = __objc_exception_matcher;
73 __objc_exception_matcher = new_matcher;
74 return old_matcher;
75 }
76
77 /* The uncaught exception handler currently in use. */
78 static objc_uncaught_exception_handler
79 __objc_uncaught_exception_handler = NULL;
80
81 objc_uncaught_exception_handler
82 objc_set_uncaught_exception_handler (objc_uncaught_exception_handler
83 new_handler)
84 {
85 objc_uncaught_exception_handler old_handler
86 = __objc_uncaught_exception_handler;
87 __objc_uncaught_exception_handler = new_handler;
88 return old_handler;
89 }
90
91
92 \f
93 #ifdef __ARM_EABI_UNWINDER__
94
95 const _Unwind_Exception_Class __objc_exception_class
96 = {'G', 'N', 'U', 'C', 'O', 'B', 'J', 'C'};
97
98 #else
99
100 /* This is the exception class we report -- "GNUCOBJC". */
101 static const _Unwind_Exception_Class __objc_exception_class
102 = ((((((((_Unwind_Exception_Class) 'G'
103 << 8 | (_Unwind_Exception_Class) 'N')
104 << 8 | (_Unwind_Exception_Class) 'U')
105 << 8 | (_Unwind_Exception_Class) 'C')
106 << 8 | (_Unwind_Exception_Class) 'O')
107 << 8 | (_Unwind_Exception_Class) 'B')
108 << 8 | (_Unwind_Exception_Class) 'J')
109 << 8 | (_Unwind_Exception_Class) 'C');
110
111 #endif
112
113 /* This is the object that is passed around by the Objective C runtime
114 to represent the exception in flight. */
115
116 struct ObjcException
117 {
118 /* This bit is needed in order to interact with the unwind runtime. */
119 struct _Unwind_Exception base;
120
121 /* The actual object we want to throw. Note: must come immediately after
122 unwind header. */
123 id value;
124
125 #ifdef __ARM_EABI_UNWINDER__
126 /* Note: we use the barrier cache defined in the unwind control block for
127 ARM EABI. */
128 #else
129 /* Cache some internal unwind data between phase 1 and phase 2. */
130 _Unwind_Ptr landingPad;
131 int handlerSwitchValue;
132 #endif
133 };
134
135 \f
136
137 struct lsda_header_info
138 {
139 _Unwind_Ptr Start;
140 _Unwind_Ptr LPStart;
141 _Unwind_Ptr ttype_base;
142 const unsigned char *TType;
143 const unsigned char *action_table;
144 unsigned char ttype_encoding;
145 unsigned char call_site_encoding;
146 };
147
148 static const unsigned char *
149 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
150 struct lsda_header_info *info)
151 {
152 _uleb128_t tmp;
153 unsigned char lpstart_encoding;
154
155 info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
156
157 /* Find @LPStart, the base to which landing pad offsets are relative. */
158 lpstart_encoding = *p++;
159 if (lpstart_encoding != DW_EH_PE_omit)
160 p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
161 else
162 info->LPStart = info->Start;
163
164 /* Find @TType, the base of the handler and exception spec type data. */
165 info->ttype_encoding = *p++;
166 if (info->ttype_encoding != DW_EH_PE_omit)
167 {
168 p = read_uleb128 (p, &tmp);
169 info->TType = p + tmp;
170 }
171 else
172 info->TType = 0;
173
174 /* The encoding and length of the call-site table; the action table
175 immediately follows. */
176 info->call_site_encoding = *p++;
177 p = read_uleb128 (p, &tmp);
178 info->action_table = p + tmp;
179
180 return p;
181 }
182
183 #ifdef __ARM_EABI_UNWINDER__
184
185 static Class
186 get_ttype_entry (struct lsda_header_info *info, _uleb128_t i)
187 {
188 _Unwind_Ptr ptr;
189
190 ptr = (_Unwind_Ptr) (info->TType - (i * 4));
191 ptr = _Unwind_decode_target2 (ptr);
192
193 if (ptr)
194 return objc_get_class ((const char *) ptr);
195 else
196 return 0;
197 }
198
199 #else
200
201 static Class
202 get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i)
203 {
204 _Unwind_Ptr ptr;
205
206 i *= size_of_encoded_value (info->ttype_encoding);
207 read_encoded_value_with_base (info->ttype_encoding, info->ttype_base,
208 info->TType - i, &ptr);
209
210 /* NULL ptr means catch-all. */
211 if (ptr)
212 return objc_get_class ((const char *) ptr);
213 else
214 return 0;
215 }
216
217 #endif
218
219 /* Using a different personality function name causes link failures
220 when trying to mix code using different exception handling models. */
221 #ifdef SJLJ_EXCEPTIONS
222 #define PERSONALITY_FUNCTION __gnu_objc_personality_sj0
223 #define __builtin_eh_return_data_regno(x) x
224 #else
225 #define PERSONALITY_FUNCTION __gnu_objc_personality_v0
226 #endif
227
228 #ifdef __ARM_EABI_UNWINDER__
229
230 #define CONTINUE_UNWINDING \
231 do \
232 { \
233 if (__gnu_unwind_frame(ue_header, context) != _URC_OK) \
234 return _URC_FAILURE; \
235 return _URC_CONTINUE_UNWIND; \
236 } \
237 while (0)
238
239 _Unwind_Reason_Code
240 PERSONALITY_FUNCTION (_Unwind_State state,
241 struct _Unwind_Exception *ue_header,
242 struct _Unwind_Context *context)
243 #else
244
245 #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
246
247 _Unwind_Reason_Code
248 PERSONALITY_FUNCTION (int version,
249 _Unwind_Action actions,
250 _Unwind_Exception_Class exception_class,
251 struct _Unwind_Exception *ue_header,
252 struct _Unwind_Context *context)
253 #endif
254 {
255 struct ObjcException *xh = (struct ObjcException *) ue_header;
256
257 struct lsda_header_info info;
258 const unsigned char *language_specific_data;
259 const unsigned char *action_record;
260 const unsigned char *p;
261 _Unwind_Ptr landing_pad, ip;
262 int handler_switch_value;
263 int saw_cleanup = 0, saw_handler, foreign_exception;
264 void *return_object;
265 int ip_before_insn = 0;
266
267 #ifdef __ARM_EABI_UNWINDER__
268 _Unwind_Action actions;
269
270 switch (state & _US_ACTION_MASK)
271 {
272 case _US_VIRTUAL_UNWIND_FRAME:
273 actions = _UA_SEARCH_PHASE;
274 break;
275
276 case _US_UNWIND_FRAME_STARTING:
277 actions = _UA_CLEANUP_PHASE;
278 if (!(state & _US_FORCE_UNWIND)
279 && ue_header->barrier_cache.sp == _Unwind_GetGR (context, 13))
280 actions |= _UA_HANDLER_FRAME;
281 break;
282
283 case _US_UNWIND_FRAME_RESUME:
284 CONTINUE_UNWINDING;
285 break;
286
287 default:
288 abort();
289 }
290 actions |= state & _US_FORCE_UNWIND;
291
292 /* TODO: Foreign exceptions need some attention (e.g. rethrowing doesn't
293 work). */
294 foreign_exception = 0;
295
296 /* The dwarf unwinder assumes the context structure holds things like the
297 function and LSDA pointers. The ARM implementation caches these in
298 the exception header (UCB). To avoid rewriting everything we make the
299 virtual IP register point at the UCB. */
300 ip = (_Unwind_Ptr) ue_header;
301 _Unwind_SetGR (context, 12, ip);
302
303 #else /* !__ARM_EABI_UNWINDER. */
304 /* Interface version check. */
305 if (version != 1)
306 return _URC_FATAL_PHASE1_ERROR;
307
308 foreign_exception = (exception_class != __objc_exception_class);
309 #endif
310
311 /* Shortcut for phase 2 found handler for domestic exception. */
312 if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
313 && !foreign_exception)
314 {
315 #ifdef __ARM_EABI_UNWINDER__
316 handler_switch_value = (int) ue_header->barrier_cache.bitpattern[1];
317 landing_pad = (_Unwind_Ptr) ue_header->barrier_cache.bitpattern[3];
318 #else
319 handler_switch_value = xh->handlerSwitchValue;
320 landing_pad = xh->landingPad;
321 #endif
322 goto install_context;
323 }
324
325 language_specific_data = (const unsigned char *)
326 _Unwind_GetLanguageSpecificData (context);
327
328 /* If no LSDA, then there are no handlers or cleanups. */
329 if (! language_specific_data)
330 CONTINUE_UNWINDING;
331
332 /* Parse the LSDA header. */
333 p = parse_lsda_header (context, language_specific_data, &info);
334 info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
335 #ifdef HAVE_GETIPINFO
336 ip = _Unwind_GetIPInfo (context, &ip_before_insn);
337 #else
338 ip = _Unwind_GetIP (context);
339 #endif
340 if (!ip_before_insn)
341 --ip;
342 landing_pad = 0;
343 action_record = 0;
344 handler_switch_value = 0;
345
346 #ifdef SJLJ_EXCEPTIONS
347 /* The given "IP" is an index into the call-site table, with two
348 exceptions -- -1 means no-action, and 0 means terminate. But
349 since we're using uleb128 values, we've not got random access
350 to the array. */
351 if ((int) ip < 0)
352 return _URC_CONTINUE_UNWIND;
353 else
354 {
355 _uleb128_t cs_lp, cs_action;
356 do
357 {
358 p = read_uleb128 (p, &cs_lp);
359 p = read_uleb128 (p, &cs_action);
360 }
361 while (--ip);
362
363 /* Can never have null landing pad for sjlj -- that would have
364 been indicated by a -1 call site index. */
365 landing_pad = cs_lp + 1;
366 if (cs_action)
367 action_record = info.action_table + cs_action - 1;
368 goto found_something;
369 }
370 #else
371 /* Search the call-site table for the action associated with this IP. */
372 while (p < info.action_table)
373 {
374 _Unwind_Ptr cs_start, cs_len, cs_lp;
375 _uleb128_t cs_action;
376
377 /* Note that all call-site encodings are "absolute" displacements. */
378 p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
379 p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
380 p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
381 p = read_uleb128 (p, &cs_action);
382
383 /* The table is sorted, so if we've passed the ip, stop. */
384 if (ip < info.Start + cs_start)
385 p = info.action_table;
386 else if (ip < info.Start + cs_start + cs_len)
387 {
388 if (cs_lp)
389 landing_pad = info.LPStart + cs_lp;
390 if (cs_action)
391 action_record = info.action_table + cs_action - 1;
392 goto found_something;
393 }
394 }
395 #endif /* SJLJ_EXCEPTIONS */
396
397 /* If ip is not present in the table, C++ would call terminate. */
398 /* ??? As with Java, it's perhaps better to tweek the LSDA to
399 that no-action is mapped to no-entry. */
400 CONTINUE_UNWINDING;
401
402 found_something:
403 saw_cleanup = 0;
404 saw_handler = 0;
405
406 if (landing_pad == 0)
407 {
408 /* If ip is present, and has a null landing pad, there are
409 no cleanups or handlers to be run. */
410 }
411 else if (action_record == 0)
412 {
413 /* If ip is present, has a non-null landing pad, and a null
414 action table offset, then there are only cleanups present.
415 Cleanups use a zero switch value, as set above. */
416 saw_cleanup = 1;
417 }
418 else
419 {
420 /* Otherwise we have a catch handler. */
421 _sleb128_t ar_filter, ar_disp;
422
423 while (1)
424 {
425 p = action_record;
426 p = read_sleb128 (p, &ar_filter);
427 read_sleb128 (p, &ar_disp);
428
429 if (ar_filter == 0)
430 {
431 /* Zero filter values are cleanups. */
432 saw_cleanup = 1;
433 }
434
435 /* During forced unwinding, we only run cleanups. With a
436 foreign exception class, we have no class info to match. */
437 else if ((actions & _UA_FORCE_UNWIND) || foreign_exception)
438 ;
439
440 else if (ar_filter > 0)
441 {
442 /* Positive filter values are handlers. */
443
444 Class catch_type = get_ttype_entry (&info, ar_filter);
445
446 if ((*__objc_exception_matcher) (catch_type, xh->value))
447 {
448 handler_switch_value = ar_filter;
449 saw_handler = 1;
450 break;
451 }
452 }
453 else
454 {
455 /* Negative filter values are exception specifications,
456 which Objective-C does not use. */
457 abort ();
458 }
459
460 if (ar_disp == 0)
461 break;
462 action_record = p + ar_disp;
463 }
464 }
465
466 if (! saw_handler && ! saw_cleanup)
467 CONTINUE_UNWINDING;
468
469 if (actions & _UA_SEARCH_PHASE)
470 {
471 if (!saw_handler)
472 CONTINUE_UNWINDING;
473
474 /* For domestic exceptions, we cache data from phase 1 for phase 2. */
475 if (!foreign_exception)
476 {
477 #ifdef __ARM_EABI_UNWINDER__
478 ue_header->barrier_cache.sp = _Unwind_GetGR (context, 13);
479 ue_header->barrier_cache.bitpattern[1] = (_uw) handler_switch_value;
480 ue_header->barrier_cache.bitpattern[3] = (_uw) landing_pad;
481 #else
482 xh->handlerSwitchValue = handler_switch_value;
483 xh->landingPad = landing_pad;
484 #endif
485 }
486 return _URC_HANDLER_FOUND;
487 }
488
489 install_context:
490 if (saw_cleanup == 0)
491 {
492 return_object = xh->value;
493 if (!(actions & _UA_SEARCH_PHASE))
494 _Unwind_DeleteException(&xh->base);
495 }
496
497 _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
498 __builtin_extend_pointer (saw_cleanup ? xh : return_object));
499 _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
500 handler_switch_value);
501 _Unwind_SetIP (context, landing_pad);
502 return _URC_INSTALL_CONTEXT;
503 }
504
505 static void
506 __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
507 struct _Unwind_Exception *exc)
508 {
509 free (exc);
510 }
511
512 void
513 objc_exception_throw (id exception)
514 {
515 struct ObjcException *header = calloc (1, sizeof (*header));
516
517 memcpy (&header->base.exception_class, &__objc_exception_class,
518 sizeof (__objc_exception_class));
519 header->base.exception_cleanup = __objc_exception_cleanup;
520 header->value = exception;
521
522 #ifdef SJLJ_EXCEPTIONS
523 _Unwind_SjLj_RaiseException (&header->base);
524 #else
525 _Unwind_RaiseException (&header->base);
526 #endif
527
528 /* No exception handler was installed. Call the uncaught exception
529 handler if any is defined.
530 */
531 if (__objc_uncaught_exception_handler != 0)
532 {
533 (*__objc_uncaught_exception_handler) (exception);
534 }
535
536 /* As a last resort support the old, deprecated way of setting an
537 uncaught exception handler.
538 */
539 if (_objc_unexpected_exception != 0)
540 {
541 (*_objc_unexpected_exception) (exception);
542 }
543
544 abort ();
545 }
546