Home | History | Annotate | Line # | Download | only in libobjc
exception.c revision 1.1
      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 
     32 #ifdef __ARM_EABI_UNWINDER__
     34 
     35 const _Unwind_Exception_Class __objc_exception_class
     36   = {'G', 'N', 'U', 'C', 'O', 'B', 'J', 'C'};
     37 
     38 #else
     39 
     40 /* This is the exception class we report -- "GNUCOBJC".  */
     41 static const _Unwind_Exception_Class __objc_exception_class
     42   = ((((((((_Unwind_Exception_Class) 'G'
     43             << 8 | (_Unwind_Exception_Class) 'N')
     44            << 8 | (_Unwind_Exception_Class) 'U')
     45           << 8 | (_Unwind_Exception_Class) 'C')
     46          << 8 | (_Unwind_Exception_Class) 'O')
     47         << 8 | (_Unwind_Exception_Class) 'B')
     48        << 8 | (_Unwind_Exception_Class) 'J')
     49       << 8 | (_Unwind_Exception_Class) 'C');
     50 
     51 #endif
     52 
     53 /* This is the object that is passed around by the Objective C runtime
     54    to represent the exception in flight.  */
     55 
     56 struct ObjcException
     57 {
     58   /* This bit is needed in order to interact with the unwind runtime.  */
     59   struct _Unwind_Exception base;
     60 
     61   /* The actual object we want to throw. Note: must come immediately after
     62      unwind header.  */
     63   id value;
     64 
     65 #ifdef __ARM_EABI_UNWINDER__
     66   /* Note: we use the barrier cache defined in the unwind control block for
     67      ARM EABI.  */
     68 #else
     69   /* Cache some internal unwind data between phase 1 and phase 2.  */
     70   _Unwind_Ptr landingPad;
     71   int handlerSwitchValue;
     72 #endif
     73 };
     74 
     75 
     76 
     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 }
    500