OSDN Git Service

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