2009-01-21 Vladimir Makarov <vmakarov@redhat.com>
[official-gcc.git] / libobjc / exception.c
blobbc59aa743cb9e3f86f8a29531aeeea1fd20062b0
1 /* The implementation of exception handling primitives for Objective-C.
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GCC.
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 2, or (at your option) any
9 later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING. If not, write to
18 the Free Software Foundation, 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA. */
21 /* As a special exception, if you link this library with files compiled
22 with GCC to produce an executable, this does not cause the resulting
23 executable to be covered by the GNU General Public License. This
24 exception does not however invalidate any other reasons why the
25 executable file might be covered by the GNU General Public License. */
27 #include <stdlib.h>
28 #include "config.h"
29 #include "objc/objc-api.h"
30 #include "unwind.h"
31 #include "unwind-pe.h"
34 #ifdef __ARM_EABI_UNWINDER__
36 const _Unwind_Exception_Class __objc_exception_class
37 = {'G', 'N', 'U', 'C', 'O', 'B', 'J', 'C'};
39 #else
41 /* This is the exception class we report -- "GNUCOBJC". */
42 static const _Unwind_Exception_Class __objc_exception_class
43 = ((((((((_Unwind_Exception_Class) 'G'
44 << 8 | (_Unwind_Exception_Class) 'N')
45 << 8 | (_Unwind_Exception_Class) 'U')
46 << 8 | (_Unwind_Exception_Class) 'C')
47 << 8 | (_Unwind_Exception_Class) 'O')
48 << 8 | (_Unwind_Exception_Class) 'B')
49 << 8 | (_Unwind_Exception_Class) 'J')
50 << 8 | (_Unwind_Exception_Class) 'C');
52 #endif
54 /* This is the object that is passed around by the Objective C runtime
55 to represent the exception in flight. */
57 struct ObjcException
59 /* This bit is needed in order to interact with the unwind runtime. */
60 struct _Unwind_Exception base;
62 /* The actual object we want to throw. Note: must come immediately after
63 unwind header. */
64 id value;
66 #ifdef __ARM_EABI_UNWINDER__
67 /* Note: we use the barrier cache defined in the unwind control block for
68 ARM EABI. */
69 #else
70 /* Cache some internal unwind data between phase 1 and phase 2. */
71 _Unwind_Ptr landingPad;
72 int handlerSwitchValue;
73 #endif
78 struct lsda_header_info
80 _Unwind_Ptr Start;
81 _Unwind_Ptr LPStart;
82 _Unwind_Ptr ttype_base;
83 const unsigned char *TType;
84 const unsigned char *action_table;
85 unsigned char ttype_encoding;
86 unsigned char call_site_encoding;
89 static const unsigned char *
90 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
91 struct lsda_header_info *info)
93 _uleb128_t tmp;
94 unsigned char lpstart_encoding;
96 info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
98 /* Find @LPStart, the base to which landing pad offsets are relative. */
99 lpstart_encoding = *p++;
100 if (lpstart_encoding != DW_EH_PE_omit)
101 p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
102 else
103 info->LPStart = info->Start;
105 /* Find @TType, the base of the handler and exception spec type data. */
106 info->ttype_encoding = *p++;
107 if (info->ttype_encoding != DW_EH_PE_omit)
109 p = read_uleb128 (p, &tmp);
110 info->TType = p + tmp;
112 else
113 info->TType = 0;
115 /* The encoding and length of the call-site table; the action table
116 immediately follows. */
117 info->call_site_encoding = *p++;
118 p = read_uleb128 (p, &tmp);
119 info->action_table = p + tmp;
121 return p;
124 #ifdef __ARM_EABI_UNWINDER__
126 static Class
127 get_ttype_entry (struct lsda_header_info *info, _uleb128_t i)
129 _Unwind_Ptr ptr;
131 ptr = (_Unwind_Ptr) (info->TType - (i * 4));
132 ptr = _Unwind_decode_target2 (ptr);
134 if (ptr)
135 return objc_get_class ((const char *) ptr);
136 else
137 return 0;
140 #else
142 static Class
143 get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i)
145 _Unwind_Ptr ptr;
147 i *= size_of_encoded_value (info->ttype_encoding);
148 read_encoded_value_with_base (info->ttype_encoding, info->ttype_base,
149 info->TType - i, &ptr);
151 /* NULL ptr means catch-all. */
152 if (ptr)
153 return objc_get_class ((const char *) ptr);
154 else
155 return 0;
158 #endif
160 /* Like unto the method of the same name on Object, but takes an id. */
161 /* ??? Does this bork the meta-type system? Can/should we look up an
162 isKindOf method on the id? */
164 static int
165 isKindOf (id value, Class target)
167 Class c;
169 /* NULL target is catch-all. */
170 if (target == 0)
171 return 1;
173 for (c = value->class_pointer; c; c = class_get_super_class (c))
174 if (c == target)
175 return 1;
176 return 0;
179 /* Using a different personality function name causes link failures
180 when trying to mix code using different exception handling models. */
181 #ifdef SJLJ_EXCEPTIONS
182 #define PERSONALITY_FUNCTION __gnu_objc_personality_sj0
183 #define __builtin_eh_return_data_regno(x) x
184 #else
185 #define PERSONALITY_FUNCTION __gnu_objc_personality_v0
186 #endif
188 #ifdef __ARM_EABI_UNWINDER__
190 #define CONTINUE_UNWINDING \
191 do \
193 if (__gnu_unwind_frame(ue_header, context) != _URC_OK) \
194 return _URC_FAILURE; \
195 return _URC_CONTINUE_UNWIND; \
197 while (0)
199 _Unwind_Reason_Code
200 PERSONALITY_FUNCTION (_Unwind_State state,
201 struct _Unwind_Exception *ue_header,
202 struct _Unwind_Context *context)
203 #else
205 #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
207 _Unwind_Reason_Code
208 PERSONALITY_FUNCTION (int version,
209 _Unwind_Action actions,
210 _Unwind_Exception_Class exception_class,
211 struct _Unwind_Exception *ue_header,
212 struct _Unwind_Context *context)
213 #endif
215 struct ObjcException *xh = (struct ObjcException *) ue_header;
217 struct lsda_header_info info;
218 const unsigned char *language_specific_data;
219 const unsigned char *action_record;
220 const unsigned char *p;
221 _Unwind_Ptr landing_pad, ip;
222 int handler_switch_value;
223 int saw_cleanup = 0, saw_handler, foreign_exception;
224 void *return_object;
225 int ip_before_insn = 0;
227 #ifdef __ARM_EABI_UNWINDER__
228 _Unwind_Action actions;
230 switch (state & _US_ACTION_MASK)
232 case _US_VIRTUAL_UNWIND_FRAME:
233 actions = _UA_SEARCH_PHASE;
234 break;
236 case _US_UNWIND_FRAME_STARTING:
237 actions = _UA_CLEANUP_PHASE;
238 if (!(state & _US_FORCE_UNWIND)
239 && ue_header->barrier_cache.sp == _Unwind_GetGR (context, 13))
240 actions |= _UA_HANDLER_FRAME;
241 break;
243 case _US_UNWIND_FRAME_RESUME:
244 CONTINUE_UNWINDING;
245 break;
247 default:
248 abort();
250 actions |= state & _US_FORCE_UNWIND;
252 /* TODO: Foreign exceptions need some attention (e.g. rethrowing doesn't
253 work). */
254 foreign_exception = 0;
256 /* The dwarf unwinder assumes the context structure holds things like the
257 function and LSDA pointers. The ARM implementation caches these in
258 the exception header (UCB). To avoid rewriting everything we make the
259 virtual IP register point at the UCB. */
260 ip = (_Unwind_Ptr) ue_header;
261 _Unwind_SetGR (context, 12, ip);
263 #else /* !__ARM_EABI_UNWINDER. */
264 /* Interface version check. */
265 if (version != 1)
266 return _URC_FATAL_PHASE1_ERROR;
268 foreign_exception = (exception_class != __objc_exception_class);
269 #endif
271 /* Shortcut for phase 2 found handler for domestic exception. */
272 if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
273 && !foreign_exception)
275 #ifdef __ARM_EABI_UNWINDER__
276 handler_switch_value = (int) ue_header->barrier_cache.bitpattern[1];
277 landing_pad = (_Unwind_Ptr) ue_header->barrier_cache.bitpattern[3];
278 #else
279 handler_switch_value = xh->handlerSwitchValue;
280 landing_pad = xh->landingPad;
281 #endif
282 goto install_context;
285 language_specific_data = (const unsigned char *)
286 _Unwind_GetLanguageSpecificData (context);
288 /* If no LSDA, then there are no handlers or cleanups. */
289 if (! language_specific_data)
290 CONTINUE_UNWINDING;
292 /* Parse the LSDA header. */
293 p = parse_lsda_header (context, language_specific_data, &info);
294 info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
295 #ifdef HAVE_GETIPINFO
296 ip = _Unwind_GetIPInfo (context, &ip_before_insn);
297 #else
298 ip = _Unwind_GetIP (context);
299 #endif
300 if (!ip_before_insn)
301 --ip;
302 landing_pad = 0;
303 action_record = 0;
304 handler_switch_value = 0;
306 #ifdef SJLJ_EXCEPTIONS
307 /* The given "IP" is an index into the call-site table, with two
308 exceptions -- -1 means no-action, and 0 means terminate. But
309 since we're using uleb128 values, we've not got random access
310 to the array. */
311 if ((int) ip < 0)
312 return _URC_CONTINUE_UNWIND;
313 else
315 _uleb128_t cs_lp, cs_action;
318 p = read_uleb128 (p, &cs_lp);
319 p = read_uleb128 (p, &cs_action);
321 while (--ip);
323 /* Can never have null landing pad for sjlj -- that would have
324 been indicated by a -1 call site index. */
325 landing_pad = cs_lp + 1;
326 if (cs_action)
327 action_record = info.action_table + cs_action - 1;
328 goto found_something;
330 #else
331 /* Search the call-site table for the action associated with this IP. */
332 while (p < info.action_table)
334 _Unwind_Ptr cs_start, cs_len, cs_lp;
335 _uleb128_t cs_action;
337 /* Note that all call-site encodings are "absolute" displacements. */
338 p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
339 p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
340 p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
341 p = read_uleb128 (p, &cs_action);
343 /* The table is sorted, so if we've passed the ip, stop. */
344 if (ip < info.Start + cs_start)
345 p = info.action_table;
346 else if (ip < info.Start + cs_start + cs_len)
348 if (cs_lp)
349 landing_pad = info.LPStart + cs_lp;
350 if (cs_action)
351 action_record = info.action_table + cs_action - 1;
352 goto found_something;
355 #endif /* SJLJ_EXCEPTIONS */
357 /* If ip is not present in the table, C++ would call terminate. */
358 /* ??? As with Java, it's perhaps better to tweek the LSDA to
359 that no-action is mapped to no-entry. */
360 CONTINUE_UNWINDING;
362 found_something:
363 saw_cleanup = 0;
364 saw_handler = 0;
366 if (landing_pad == 0)
368 /* If ip is present, and has a null landing pad, there are
369 no cleanups or handlers to be run. */
371 else if (action_record == 0)
373 /* If ip is present, has a non-null landing pad, and a null
374 action table offset, then there are only cleanups present.
375 Cleanups use a zero switch value, as set above. */
376 saw_cleanup = 1;
378 else
380 /* Otherwise we have a catch handler. */
381 _sleb128_t ar_filter, ar_disp;
383 while (1)
385 p = action_record;
386 p = read_sleb128 (p, &ar_filter);
387 read_sleb128 (p, &ar_disp);
389 if (ar_filter == 0)
391 /* Zero filter values are cleanups. */
392 saw_cleanup = 1;
395 /* During forced unwinding, we only run cleanups. With a
396 foreign exception class, we have no class info to match. */
397 else if ((actions & _UA_FORCE_UNWIND) || foreign_exception)
400 else if (ar_filter > 0)
402 /* Positive filter values are handlers. */
404 Class catch_type = get_ttype_entry (&info, ar_filter);
406 if (isKindOf (xh->value, catch_type))
408 handler_switch_value = ar_filter;
409 saw_handler = 1;
410 break;
413 else
415 /* Negative filter values are exception specifications,
416 which Objective-C does not use. */
417 abort ();
420 if (ar_disp == 0)
421 break;
422 action_record = p + ar_disp;
426 if (! saw_handler && ! saw_cleanup)
427 CONTINUE_UNWINDING;
429 if (actions & _UA_SEARCH_PHASE)
431 if (!saw_handler)
432 CONTINUE_UNWINDING;
434 /* For domestic exceptions, we cache data from phase 1 for phase 2. */
435 if (!foreign_exception)
437 #ifdef __ARM_EABI_UNWINDER__
438 ue_header->barrier_cache.sp = _Unwind_GetGR (context, 13);
439 ue_header->barrier_cache.bitpattern[1] = (_uw) handler_switch_value;
440 ue_header->barrier_cache.bitpattern[3] = (_uw) landing_pad;
441 #else
442 xh->handlerSwitchValue = handler_switch_value;
443 xh->landingPad = landing_pad;
444 #endif
446 return _URC_HANDLER_FOUND;
449 install_context:
450 if (saw_cleanup == 0)
452 return_object = xh->value;
453 if (!(actions & _UA_SEARCH_PHASE))
454 _Unwind_DeleteException(&xh->base);
457 _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
458 __builtin_extend_pointer (saw_cleanup ? xh : return_object));
459 _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
460 handler_switch_value);
461 _Unwind_SetIP (context, landing_pad);
462 return _URC_INSTALL_CONTEXT;
465 static void
466 __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
467 struct _Unwind_Exception *exc)
469 free (exc);
472 void
473 objc_exception_throw (id value)
475 struct ObjcException *header = calloc (1, sizeof (*header));
477 memcpy (&header->base.exception_class, &__objc_exception_class,
478 sizeof (__objc_exception_class));
479 header->base.exception_cleanup = __objc_exception_cleanup;
480 header->value = value;
482 #ifdef SJLJ_EXCEPTIONS
483 _Unwind_SjLj_RaiseException (&header->base);
484 #else
485 _Unwind_RaiseException (&header->base);
486 #endif
488 /* Some sort of unwinding error. */
489 abort ();