OSDN Git Service

1a6b9dab4d18c65c8a5bad4c00ef244d8ee738c6
[pf3gnuchains/gcc-fork.git] / libobjc / exception.c
1 /* The implementation of exception handling primitives for Objective-C.
2    Copyright (C) 2004 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 static const unsigned char *
90 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
91                    struct lsda_header_info *info)
92 {
93   _uleb128_t tmp;
94   unsigned char lpstart_encoding;
95
96   info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
97
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;
104
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)
108     {
109       p = read_uleb128 (p, &tmp);
110       info->TType = p + tmp;
111     }
112   else
113     info->TType = 0;
114
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;
120
121   return p;
122 }
123
124 #ifdef __ARM_EABI_UNWINDER__
125
126 static Class
127 get_ttype_entry (struct lsda_header_info *info, _uleb128_t i)
128 {
129   _Unwind_Ptr ptr;
130   
131   ptr = (_Unwind_Ptr) (info->TType - (i * 4));
132   ptr = _Unwind_decode_target2 (ptr);
133   
134   if (ptr)
135     return objc_get_class ((const char *) ptr);
136   else
137     return 0;
138 }
139
140 #else
141
142 static Class
143 get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i)
144 {
145   _Unwind_Ptr ptr;
146
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);
150
151   /* NULL ptr means catch-all.  */
152   if (ptr)
153     return objc_get_class ((const char *) ptr);
154   else
155     return 0;
156 }
157
158 #endif
159
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?  */
163
164 static int
165 isKindOf (id value, Class target)
166 {
167   Class c;
168
169   /* NULL target is catch-all.  */
170   if (target == 0)
171     return 1;
172
173   for (c = value->class_pointer; c; c = class_get_super_class (c))
174     if (c == target)
175       return 1;
176   return 0;
177 }
178
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
187
188 #ifdef __ARM_EABI_UNWINDER__
189
190 #define CONTINUE_UNWINDING \
191   do                                                            \
192     {                                                           \
193       if (__gnu_unwind_frame(ue_header, context) != _URC_OK)    \
194         return _URC_FAILURE;                                    \
195       return _URC_CONTINUE_UNWIND;                              \
196     }                                                           \
197   while (0)
198
199 _Unwind_Reason_Code
200 PERSONALITY_FUNCTION (_Unwind_State state,
201                       struct _Unwind_Exception *ue_header,
202                       struct _Unwind_Context *context)
203 #else
204
205 #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
206
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
214 {
215   struct ObjcException *xh = (struct ObjcException *) ue_header;
216
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;
226
227 #ifdef __ARM_EABI_UNWINDER__
228   _Unwind_Action actions;
229   
230   switch (state & _US_ACTION_MASK)
231     {
232     case _US_VIRTUAL_UNWIND_FRAME:
233       actions = _UA_SEARCH_PHASE;
234       break;
235
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;
242
243     case _US_UNWIND_FRAME_RESUME:
244       CONTINUE_UNWINDING;
245       break;
246
247     default:
248       abort();
249     }
250   actions |= state & _US_FORCE_UNWIND;
251
252   /* TODO: Foreign exceptions need some attention (e.g. rethrowing doesn't
253      work).  */
254   foreign_exception = 0;
255
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);
262
263 #else  /* !__ARM_EABI_UNWINDER.  */
264   /* Interface version check.  */
265   if (version != 1)
266     return _URC_FATAL_PHASE1_ERROR;
267   
268   foreign_exception = (exception_class != __objc_exception_class);
269 #endif
270
271   /* Shortcut for phase 2 found handler for domestic exception.  */
272   if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
273       && !foreign_exception)
274     {
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;
283     }
284
285   language_specific_data = (const unsigned char *)
286     _Unwind_GetLanguageSpecificData (context);
287
288   /* If no LSDA, then there are no handlers or cleanups.  */
289   if (! language_specific_data)
290     CONTINUE_UNWINDING;
291
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) - 1;
299 #endif
300   if (!ip_before_insn)
301     --ip;
302   landing_pad = 0;
303   action_record = 0;
304   handler_switch_value = 0;
305
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
314     {
315       _uleb128_t cs_lp, cs_action;
316       do
317         {
318           p = read_uleb128 (p, &cs_lp);
319           p = read_uleb128 (p, &cs_action);
320         }
321       while (--ip);
322
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;
329     }
330 #else
331   /* Search the call-site table for the action associated with this IP.  */
332   while (p < info.action_table)
333     {
334       _Unwind_Ptr cs_start, cs_len, cs_lp;
335       _uleb128_t cs_action;
336
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);
342
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)
347         {
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;
353         }
354     }
355 #endif /* SJLJ_EXCEPTIONS  */
356
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;
361
362  found_something:
363   saw_cleanup = 0;
364   saw_handler = 0;
365
366   if (landing_pad == 0)
367     {
368       /* If ip is present, and has a null landing pad, there are
369          no cleanups or handlers to be run.  */
370     }
371   else if (action_record == 0)
372     {
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;
377     }
378   else
379     {
380       /* Otherwise we have a catch handler.  */
381       _sleb128_t ar_filter, ar_disp;
382
383       while (1)
384         {
385           p = action_record;
386           p = read_sleb128 (p, &ar_filter);
387           read_sleb128 (p, &ar_disp);
388
389           if (ar_filter == 0)
390             {
391               /* Zero filter values are cleanups.  */
392               saw_cleanup = 1;
393             }
394
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)
398             ;
399
400           else if (ar_filter > 0)
401             {
402               /* Positive filter values are handlers.  */
403
404               Class catch_type = get_ttype_entry (&info, ar_filter);
405
406               if (isKindOf (xh->value, catch_type))
407                 {
408                   handler_switch_value = ar_filter;
409                   saw_handler = 1;
410                   break;
411                 }
412             }
413           else
414             {
415               /* Negative filter values are exception specifications,
416                  which Objective-C does not use.  */
417               abort ();
418             }
419
420           if (ar_disp == 0)
421             break;
422           action_record = p + ar_disp;
423         }
424     }
425
426   if (! saw_handler && ! saw_cleanup)
427     CONTINUE_UNWINDING;
428
429   if (actions & _UA_SEARCH_PHASE)
430     {
431       if (!saw_handler)
432         CONTINUE_UNWINDING;
433
434       /* For domestic exceptions, we cache data from phase 1 for phase 2.  */
435       if (!foreign_exception)
436         {
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
445         }
446       return _URC_HANDLER_FOUND;
447     }
448
449  install_context:
450   if (saw_cleanup == 0)
451     {
452       return_object = xh->value;
453       if (!(actions & _UA_SEARCH_PHASE))
454         _Unwind_DeleteException(&xh->base);
455     }
456   
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;
463 }
464
465 static void
466 __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
467                           struct _Unwind_Exception *exc)
468 {
469   free (exc);
470 }
471
472 void
473 objc_exception_throw (id value)
474 {
475   struct ObjcException *header = calloc (1, sizeof (*header));
476   
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;
481
482 #ifdef SJLJ_EXCEPTIONS
483   _Unwind_SjLj_RaiseException (&header->base);
484 #else
485   _Unwind_RaiseException (&header->base);
486 #endif
487
488   /* Some sort of unwinding error.  */
489   abort ();
490 }