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