Import gcc-4.4.1
[dragonfly.git] / contrib / gcc-4.4 / 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 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 <stdlib.h>
26 #include "config.h"
27 #include "objc/objc-api.h"
28 #include "unwind.h"
29 #include "unwind-pe.h"
30
31 \f
32 #ifdef __ARM_EABI_UNWINDER__
33
34 const _Unwind_Exception_Class __objc_exception_class
35   = {'G', 'N', 'U', 'C', 'O', 'B', 'J', 'C'};
36   
37 #else
38
39 /* This is the exception class we report -- "GNUCOBJC".  */
40 static 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
51
52 /* This is the object that is passed around by the Objective C runtime
53    to represent the exception in flight.  */
54
55 struct ObjcException
56 {
57   /* This bit is needed in order to interact with the unwind runtime.  */
58   struct _Unwind_Exception base;
59
60   /* The actual object we want to throw. Note: must come immediately after
61      unwind header.  */
62   id value;
63
64 #ifdef __ARM_EABI_UNWINDER__
65   /* Note: we use the barrier cache defined in the unwind control block for
66      ARM EABI.  */
67 #else
68   /* Cache some internal unwind data between phase 1 and phase 2.  */
69   _Unwind_Ptr landingPad;
70   int handlerSwitchValue;
71 #endif
72 };
73
74 \f
75
76 struct 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
87 /* This hook allows libraries to sepecify special actions when an
88    exception is thrown without a handler in place.
89  */
90 void (*_objc_unexpected_exception) (id exception); /* !T:SAFE */
91
92 static const unsigned char *
93 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
94                    struct lsda_header_info *info)
95 {
96   _uleb128_t tmp;
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
127 #ifdef __ARM_EABI_UNWINDER__
128
129 static Class
130 get_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
145 static Class
146 get_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
161 #endif
162
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
167 static int
168 isKindOf (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
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
203 PERSONALITY_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
210 _Unwind_Reason_Code
211 PERSONALITY_FUNCTION (int version,
212                       _Unwind_Action actions,
213                       _Unwind_Exception_Class exception_class,
214                       struct _Unwind_Exception *ue_header,
215                       struct _Unwind_Context *context)
216 #endif
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;
226   int saw_cleanup = 0, saw_handler, foreign_exception;
227   void *return_object;
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;
258
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.  */
267   /* Interface version check.  */
268   if (version != 1)
269     return _URC_FATAL_PHASE1_ERROR;
270   
271   foreign_exception = (exception_class != __objc_exception_class);
272 #endif
273
274   /* Shortcut for phase 2 found handler for domestic exception.  */
275   if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
276       && !foreign_exception)
277     {
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
282       handler_switch_value = xh->handlerSwitchValue;
283       landing_pad = xh->landingPad;
284 #endif
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)
293     CONTINUE_UNWINDING;
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);
298 #ifdef HAVE_GETIPINFO
299   ip = _Unwind_GetIPInfo (context, &ip_before_insn);
300 #else
301   ip = _Unwind_GetIP (context);
302 #endif
303   if (!ip_before_insn)
304     --ip;
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     {
318       _uleb128_t cs_lp, cs_action;
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;
338       _uleb128_t cs_action;
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.  */
363   CONTINUE_UNWINDING;
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.  */
384       _sleb128_t ar_filter, ar_disp;
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.  */
400           else if ((actions & _UA_FORCE_UNWIND) || foreign_exception)
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)
430     CONTINUE_UNWINDING;
431
432   if (actions & _UA_SEARCH_PHASE)
433     {
434       if (!saw_handler)
435         CONTINUE_UNWINDING;
436
437       /* For domestic exceptions, we cache data from phase 1 for phase 2.  */
438       if (!foreign_exception)
439         {
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
445           xh->handlerSwitchValue = handler_switch_value;
446           xh->landingPad = landing_pad;
447 #endif
448         }
449       return _URC_HANDLER_FOUND;
450     }
451
452  install_context:
453   if (saw_cleanup == 0)
454     {
455       return_object = xh->value;
456       if (!(actions & _UA_SEARCH_PHASE))
457         _Unwind_DeleteException(&xh->base);
458     }
459   
460   _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
461                  __builtin_extend_pointer (saw_cleanup ? xh : return_object));
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
468 static void
469 __objc_exception_cleanup (_Unwind_Reason_Code code __attribute__((unused)),
470                           struct _Unwind_Exception *exc)
471 {
472   free (exc);
473 }
474
475 void
476 objc_exception_throw (id value)
477 {
478   struct ObjcException *header = calloc (1, sizeof (*header));
479   
480   memcpy (&header->base.exception_class, &__objc_exception_class,
481           sizeof (__objc_exception_class));
482   header->base.exception_cleanup = __objc_exception_cleanup;
483   header->value = value;
484
485 #ifdef SJLJ_EXCEPTIONS
486   _Unwind_SjLj_RaiseException (&header->base);
487 #else
488   _Unwind_RaiseException (&header->base);
489 #endif
490
491   /* Some sort of unwinding error.  */
492   if (_objc_unexpected_exception != 0)
493     {
494       (*_objc_unexpected_exception) (value);
495     }
496   abort ();
497 }