(root)/
gcc-13.2.0/
libgcc/
unwind-seh.c
       1  /* Structured Exception Handling (SEH) runtime interface routines.
       2     Copyright (C) 2010-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
       8     the Free Software Foundation; either version 3, or (at your option)
       9     any 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
      13     or 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 "tconfig.h"
      26  #include "tsystem.h"
      27  #include "coretypes.h"
      28  #include "tm.h"
      29  #include "unwind.h"
      30  
      31  #if defined (__SEH__) && !defined (__USING_SJLJ_EXCEPTIONS__)
      32  
      33  /* At the moment everything is written for x64, but in theory this could
      34     also be used for i386, arm, mips and other extant embedded Windows.  */
      35  #ifndef __x86_64__
      36  #error "Unsupported architecture."
      37  #endif
      38  
      39  /* Define GCC's exception codes.  See
      40       http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx
      41     In particular, MS defines bits:
      42       [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
      43       [29]    = 1 (user-defined)
      44       [28]    = 0 (reserved)
      45     We define bits:
      46       [24:27] = type
      47       [0:23]  = magic
      48     We set "magic" to "GCC", which is similar to MVC++ which uses "msc"
      49     as the low 3 bytes of its user-defined codes for C++ exceptions.
      50  
      51     We define the ExceptionInformation entries as follows:
      52       [0] = _Unwind_Exception pointer
      53       [1] = target frame
      54       [2] = target ip
      55       [3] = target rdx
      56  */
      57  
      58  #define STATUS_USER_DEFINED		(1U << 29)
      59  
      60  #define GCC_MAGIC			(('G' << 16) | ('C' << 8) | 'C')
      61  #define GCC_EXCEPTION(TYPE)		\
      62         (STATUS_USER_DEFINED | ((TYPE) << 24) | GCC_MAGIC)
      63  
      64  #define STATUS_GCC_THROW		GCC_EXCEPTION (0)
      65  #define STATUS_GCC_UNWIND		GCC_EXCEPTION (1)
      66  #define STATUS_GCC_FORCED		GCC_EXCEPTION (2)
      67  
      68  
      69  struct _Unwind_Context
      70  {
      71    _Unwind_Word cfa;
      72    _Unwind_Word ra;
      73    _Unwind_Word reg[2];
      74    PDISPATCHER_CONTEXT disp;
      75  };
      76  
      77  /* Get the value of register INDEX as saved in CONTEXT.  */
      78  
      79  _Unwind_Word
      80  _Unwind_GetGR (struct _Unwind_Context *c, int index)
      81  {
      82    if (index < 0 || index >= 2)
      83      abort ();
      84    return c->reg[index];
      85  }
      86  
      87  /* Overwrite the saved value for register INDEX in CONTEXT with VAL.  */
      88  
      89  void
      90  _Unwind_SetGR (struct _Unwind_Context *c, int index, _Unwind_Word val)
      91  {
      92    if (index < 0 || index >= 2)
      93      abort ();
      94    c->reg[index] = val;
      95  }
      96  
      97  /* Get the value of the CFA as saved in CONTEXT.  */
      98  
      99  _Unwind_Word
     100  _Unwind_GetCFA (struct _Unwind_Context *c)
     101  {
     102    return c->cfa;
     103  }
     104  
     105  /* Retrieve the return address for CONTEXT.  */
     106  
     107  _Unwind_Ptr
     108  _Unwind_GetIP (struct _Unwind_Context *c)
     109  {
     110    return c->ra;
     111  }
     112  
     113  /* Retrieve the return address and flag whether that IP is before
     114     or after first not yet fully executed instruction.  */
     115  
     116  _Unwind_Ptr
     117  _Unwind_GetIPInfo (struct _Unwind_Context *c, int *ip_before_insn)
     118  {
     119    /* ??? Is there a concept of a signal context properly?  There's
     120       obviously an UNWP_PUSH_MACHFRAME opcode, but the runtime might
     121       have arranged for that not to matter, really.  */
     122    *ip_before_insn = 0;
     123    return c->ra;
     124  }
     125  
     126  /* Overwrite the return address for CONTEXT with VAL.  */
     127  
     128  void
     129  _Unwind_SetIP (struct _Unwind_Context *c, _Unwind_Ptr val)
     130  {
     131    c->ra = val;
     132  }
     133  
     134  void *
     135  _Unwind_GetLanguageSpecificData (struct _Unwind_Context *c)
     136  {
     137    return c->disp->HandlerData;
     138  }
     139  
     140  _Unwind_Ptr
     141  _Unwind_GetRegionStart (struct _Unwind_Context *c)
     142  {
     143    return c->disp->FunctionEntry->BeginAddress + c->disp->ImageBase;
     144  }
     145  
     146  void *
     147  _Unwind_FindEnclosingFunction (void *pc)
     148  {
     149    PRUNTIME_FUNCTION entry;
     150    ULONG64 ImageBase;
     151  
     152    entry = RtlLookupFunctionEntry ((ULONG64)pc, &ImageBase, NULL);
     153  
     154    return (entry ? (void *)(entry->BeginAddress + ImageBase) : NULL);
     155  }
     156  
     157  _Unwind_Ptr
     158  _Unwind_GetDataRelBase (struct _Unwind_Context *c ATTRIBUTE_UNUSED)
     159  {
     160    return 0;
     161  }
     162  
     163  _Unwind_Ptr
     164  _Unwind_GetTextRelBase (struct _Unwind_Context *c)
     165  {
     166    return c->disp->ImageBase;
     167  }
     168  
     169  
     170  /* The two-phase unwind process that GCC uses is ordered differently
     171     from the two-phase unwind process that SEH uses.  The mechansism
     172     that GCC uses is to have the filter return _URC_HANDER_FOUND; the
     173     mechanism that SEH uses is for the filter function call back into
     174     the unwinder.
     175  
     176     An Ideal port to SEH would have GCC emit handler functions that
     177     can be called, given a pointer to the "EstablisherFrame" (i.e.
     178     the frame pointer base of the user-level function) can manipulate
     179     the user-level variables within the user-level function's stack
     180     frame.  Once done manipulating the variables, it would return
     181     a ExceptionContinueSearch, and the unwind process would continue.
     182  
     183     GCC has always done things a bit differently.  We continue to
     184     transfer control back into the user-level function which, once
     185     done manipulating the user-level variables, re-throws the exception.  */
     186  
     187  /* The "real" language-specific personality handler forwards to here
     188     where we handle the MS SEH state and transforms it into the GCC
     189     unwind state as per GCC's <unwind.h>, at which point we defer to
     190     the regular language-specfic exception handler, which is passed in.  */
     191  
     192  EXCEPTION_DISPOSITION
     193  _GCC_specific_handler (PEXCEPTION_RECORD ms_exc, void *this_frame,
     194  		       PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp,
     195  		       _Unwind_Personality_Fn gcc_per)
     196  {
     197    DWORD ms_flags = ms_exc->ExceptionFlags;
     198    DWORD ms_code = ms_exc->ExceptionCode;
     199  
     200    struct _Unwind_Exception *gcc_exc
     201      = (struct _Unwind_Exception *) ms_exc->ExceptionInformation[0];
     202    struct _Unwind_Context gcc_context;
     203    _Unwind_Action gcc_action;
     204    _Unwind_Reason_Code gcc_reason;
     205  
     206    if (ms_flags & EXCEPTION_TARGET_UNWIND)
     207      {
     208        /* This frame is known to be the target frame.  We've already
     209           "installed" the target_ip and RAX value via the arguments
     210           to RtlUnwindEx.  All that's left is to set the RDX value
     211           and "continue" to have the context installed.  */
     212        ms_disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];
     213        return ExceptionContinueSearch;
     214      }
     215  
     216    if (ms_code == STATUS_GCC_UNWIND)
     217      {
     218        /* This is a colliding exception that we threw so that we could
     219           cancel the already in-flight exception and stop in a frame
     220  	 that wanted to perform some unwind action.  The only relevant
     221  	 test is that we're the target frame.  */
     222        if (ms_exc->ExceptionInformation[1] == (_Unwind_Ptr) this_frame)
     223  	{
     224  	  RtlUnwindEx (this_frame, (PVOID) ms_exc->ExceptionInformation[2],
     225  		       ms_exc, gcc_exc, ms_orig_context,
     226  		       ms_disp->HistoryTable);
     227  	  abort ();
     228  	}
     229        return ExceptionContinueSearch;
     230      }
     231  
     232    gcc_context.cfa = ms_disp->ContextRecord->Rsp;
     233    gcc_context.ra = ms_disp->ControlPc;
     234    gcc_context.reg[0] = 0xdeadbeef;	/* These are write-only.  */
     235    gcc_context.reg[1] = 0xdeadbeef;
     236    gcc_context.disp = ms_disp;
     237  
     238    if (ms_code == STATUS_GCC_FORCED)
     239      {
     240         _Unwind_Stop_Fn stop = (_Unwind_Stop_Fn) gcc_exc->private_[0];
     241         void *stop_argument = (void *) gcc_exc->private_[4];
     242  
     243         gcc_action = _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE;
     244  
     245         stop (1, gcc_action, gcc_exc->exception_class, gcc_exc,
     246               &gcc_context, stop_argument);
     247  
     248         goto phase2;
     249      }
     250  
     251    /* ??? TODO: handling non-gcc user-defined exceptions as foreign.  */
     252    if (ms_code != STATUS_GCC_THROW)
     253      return ExceptionContinueSearch;
     254  
     255    if (ms_flags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))
     256      {
     257        /* This is Phase 2.  */
     258        /* We know this isn't the target frame because we've already tested
     259  	 EXCEPTION_TARGET_UNWIND.  The remaining possibility is that the
     260  	 gcc personality has unwind code to run.  */
     261  
     262        gcc_action = _UA_CLEANUP_PHASE;
     263      phase2:
     264        gcc_reason = gcc_per (1, gcc_action, gcc_exc->exception_class,
     265  			    gcc_exc, &gcc_context);
     266  
     267        if (gcc_reason == _URC_CONTINUE_UNWIND)
     268  	return ExceptionContinueSearch;
     269  
     270        if (gcc_reason == _URC_INSTALL_CONTEXT)
     271  	{
     272  	  /* Scratch space for the bits for the unwind catch.  */
     273  	  ms_exc->ExceptionInformation[1] = (_Unwind_Ptr) this_frame;
     274  	  ms_exc->ExceptionInformation[2] = gcc_context.ra;
     275  	  ms_exc->ExceptionInformation[3] = gcc_context.reg[1];
     276  
     277  	  /* Cancel the current exception by raising another.  */
     278  	  RaiseException (STATUS_GCC_UNWIND, EXCEPTION_NONCONTINUABLE,
     279  			  4, ms_exc->ExceptionInformation);
     280  
     281  	  /* Is RaiseException declared noreturn?  */
     282  	}
     283  
     284        /* In _Unwind_RaiseException_Phase2 we return _URC_FATAL_PHASE2_ERROR. */
     285      }
     286    else
     287      {
     288        /* This is Phase 1.  */
     289        gcc_reason = gcc_per (1, _UA_SEARCH_PHASE, gcc_exc->exception_class,
     290  			    gcc_exc, &gcc_context);
     291  
     292        if (gcc_reason == _URC_CONTINUE_UNWIND)
     293  	return ExceptionContinueSearch;
     294  
     295        if (gcc_reason == _URC_HANDLER_FOUND)
     296  	{
     297  	  /* We really need some of the information that GCC's personality
     298  	     routines compute during phase 2 right now, like the target IP.
     299  	     Go ahead and ask for it now, and cache it.  */
     300  	  gcc_reason = gcc_per (1, _UA_CLEANUP_PHASE | _UA_HANDLER_FRAME,
     301  				gcc_exc->exception_class, gcc_exc,
     302  				&gcc_context);
     303  	  if (gcc_reason != _URC_INSTALL_CONTEXT)
     304  	    abort ();
     305  
     306  	  gcc_exc->private_[1] = (_Unwind_Ptr) this_frame;
     307  	  gcc_exc->private_[2] = gcc_context.ra;
     308  	  gcc_exc->private_[3] = gcc_context.reg[1];
     309  
     310  	  ms_exc->NumberParameters = 4;
     311  	  ms_exc->ExceptionInformation[1] = (_Unwind_Ptr) this_frame;
     312  	  ms_exc->ExceptionInformation[2] = gcc_context.ra;
     313  	  ms_exc->ExceptionInformation[3] = gcc_context.reg[1];
     314  
     315  	  /* Begin phase 2.  Perform the unwinding.  */
     316  	  RtlUnwindEx (this_frame, (PVOID)gcc_context.ra, ms_exc,
     317  		       (PVOID)gcc_context.reg[0], ms_orig_context,
     318  		       ms_disp->HistoryTable);
     319  	}
     320  
     321        /* In _Unwind_RaiseException we return _URC_FATAL_PHASE1_ERROR.  */
     322      }
     323    abort ();
     324  }
     325  
     326  /* Raise an exception, passing along the given exception object.  */
     327  
     328  _Unwind_Reason_Code
     329  _Unwind_RaiseException (struct _Unwind_Exception *exc)
     330  {
     331    memset (exc->private_, 0, sizeof (exc->private_));
     332  
     333    /* The ExceptionInformation array will have only 1 element, EXC.  */
     334    RaiseException (STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exc);
     335  
     336    /* The exception handler installed in crt0 will continue any GCC
     337       exception that reaches there (and isn't marked non-continuable).
     338       Returning allows the C++ runtime to call std::terminate.  */
     339    return _URC_END_OF_STACK;
     340  }
     341  
     342  /* Resume propagation of an existing exception.  This is used after
     343     e.g. executing cleanup code, and not to implement rethrowing.  */
     344  
     345  void
     346  _Unwind_Resume (struct _Unwind_Exception *gcc_exc)
     347  {
     348    UNWIND_HISTORY_TABLE ms_history;
     349    EXCEPTION_RECORD ms_exc;
     350    CONTEXT ms_context;
     351  
     352    memset (&ms_exc, 0, sizeof(ms_exc));
     353    memset (&ms_history, 0, sizeof(ms_history));
     354  
     355    /* ??? Not 100% perfect, since we aren't passing on the *original*
     356       exception context, but should be good enough.  */
     357    ms_exc.ExceptionCode = STATUS_GCC_THROW;
     358    ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
     359    ms_exc.NumberParameters = 4;
     360    ms_exc.ExceptionInformation[0] = (ULONG_PTR) gcc_exc;
     361    ms_exc.ExceptionInformation[1] = gcc_exc->private_[1];
     362    ms_exc.ExceptionInformation[2] = gcc_exc->private_[2];
     363    ms_exc.ExceptionInformation[3] = gcc_exc->private_[3];
     364  
     365    ms_context.ContextFlags = CONTEXT_ALL;
     366    RtlCaptureContext (&ms_context);
     367  
     368    RtlUnwindEx ((void *) gcc_exc->private_[1], (PVOID)gcc_exc->private_[2],
     369  	       &ms_exc, gcc_exc, &ms_context, &ms_history);
     370  
     371    /* Is RtlUnwindEx declared noreturn?  */
     372    abort ();
     373  }
     374  
     375  static _Unwind_Reason_Code
     376  _Unwind_ForcedUnwind_Phase2 (struct _Unwind_Exception *exc)
     377  {
     378    _Unwind_Stop_Fn stop;
     379    void * stop_argument;
     380  
     381    RaiseException (STATUS_GCC_FORCED, 0, 1, (ULONG_PTR *)&exc);
     382  
     383    /* If we get here, we got to top-of-stack.  */
     384    /* ??? We no longer have a context pointer to pass in.  */
     385  
     386    stop = (_Unwind_Stop_Fn) exc->private_[0];
     387    stop_argument = (void *) exc->private_[4];
     388    stop (1, _UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK,
     389  	exc->exception_class, exc, NULL, stop_argument);
     390  
     391    return _UA_END_OF_STACK;
     392  }
     393  
     394  _Unwind_Reason_Code
     395  _Unwind_Resume_or_Rethrow (struct _Unwind_Exception *exc)
     396  {
     397    if (exc->private_[0] == 0)
     398      _Unwind_RaiseException (exc);
     399    else
     400      _Unwind_ForcedUnwind_Phase2 (exc);
     401    abort ();
     402  }
     403  
     404  /* Raise an exception for forced unwinding.  */
     405  
     406  _Unwind_Reason_Code
     407  _Unwind_ForcedUnwind (struct _Unwind_Exception *exc,
     408  		      _Unwind_Stop_Fn stop, void * stop_argument)
     409  {
     410    /* ??? This is a hack that only works with _GCC_specific_handler.
     411       There's no way to invoke STOP within frames that use a different
     412       exception handler.  This is essentially just good enough to run
     413       the code within the gcc testsuite.  */
     414  
     415    memset (exc->private_, 0, sizeof (exc->private_));
     416    exc->private_[0] = (_Unwind_Ptr) stop;
     417    exc->private_[4] = (_Unwind_Ptr) stop_argument;
     418  
     419    return _Unwind_ForcedUnwind_Phase2 (exc);
     420  }
     421  
     422  /* A convenience function that calls the exception_cleanup field.  */
     423  
     424  void
     425  _Unwind_DeleteException (struct _Unwind_Exception *exc)
     426  {
     427    if (exc->exception_cleanup)
     428      (*exc->exception_cleanup) (_URC_FOREIGN_EXCEPTION_CAUGHT, exc);
     429  }
     430  
     431  /* Perform stack backtrace through unwind data.  */
     432  
     433  _Unwind_Reason_Code
     434  _Unwind_Backtrace(_Unwind_Trace_Fn trace,
     435  		  void *trace_argument)
     436  {
     437    UNWIND_HISTORY_TABLE ms_history;
     438    CONTEXT ms_context;
     439    struct _Unwind_Context gcc_context;
     440    DISPATCHER_CONTEXT disp_context;
     441  
     442    memset (&ms_history, 0, sizeof(ms_history));
     443    memset (&gcc_context, 0, sizeof(gcc_context));
     444    memset (&disp_context, 0, sizeof(disp_context));
     445  
     446    ms_context.ContextFlags = CONTEXT_ALL;
     447    RtlCaptureContext (&ms_context);
     448  
     449    gcc_context.disp = &disp_context;
     450    gcc_context.disp->ContextRecord = &ms_context;
     451    gcc_context.disp->HistoryTable = &ms_history;
     452  
     453    while (1)
     454      {
     455        gcc_context.disp->ControlPc = ms_context.Rip;
     456        gcc_context.disp->FunctionEntry
     457  	= RtlLookupFunctionEntry (ms_context.Rip, &gcc_context.disp->ImageBase,
     458  				  &ms_history);
     459  
     460        if (!gcc_context.disp->FunctionEntry)
     461  	return _URC_END_OF_STACK;
     462  
     463        gcc_context.disp->LanguageHandler
     464  	= RtlVirtualUnwind (0, gcc_context.disp->ImageBase, ms_context.Rip,
     465  			    gcc_context.disp->FunctionEntry, &ms_context,
     466  			    &gcc_context.disp->HandlerData,
     467  			    &gcc_context.disp->EstablisherFrame, NULL);
     468  
     469        /* Set values that the callback can inspect via _Unwind_GetIP
     470         * and _Unwind_GetCFA. */
     471        gcc_context.ra = ms_context.Rip;
     472        gcc_context.cfa = ms_context.Rsp;
     473  
     474        /* Call trace function.  */
     475        if (trace (&gcc_context, trace_argument) != _URC_NO_REASON)
     476  	return _URC_FATAL_PHASE1_ERROR;
     477  
     478        /* ??? Check for invalid stack pointer.  */
     479        if (ms_context.Rip == 0)
     480  	return _URC_END_OF_STACK;
     481      }
     482  }
     483  #endif /* __SEH__  && !defined (__USING_SJLJ_EXCEPTIONS__)  */