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