OSDN Git Service

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