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