(root)/
gcc-13.2.0/
libgcc/
config/
i386/
w32-unwind.h
       1  /* Definitions for Dwarf2 EH unwind support for Windows32 targets
       2     Copyright (C) 2007-2023 Free Software Foundation, Inc.
       3     Contributed by Pascal Obry  <obry@adacore.com>
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify it under
       8  the terms of the GNU General Public License as published by the Free
       9  Software Foundation; either version 3, or (at your option) any later
      10  version.
      11  
      12  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15  for more details.
      16  
      17  Under Section 7 of GPL version 3, you are granted additional
      18  permissions described in the GCC Runtime Library Exception, version
      19  3.1, as published by the Free Software Foundation.
      20  
      21  You should have received a copy of the GNU General Public License and
      22  a copy of the GCC Runtime Library Exception along with this program;
      23  see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      24  <http://www.gnu.org/licenses/>.  */
      25  
      26  
      27  /* This file implements the md_fallback_frame_state_for routine for
      28     Windows, triggered when the GCC table based unwinding process hits a
      29     frame for which no unwind info has been registered. This typically
      30     occurs when raising an exception from a signal handler, because the
      31     handler is actually called from the OS kernel.
      32  
      33     The basic idea is to detect that we are indeed trying to unwind past a
      34     signal handler and to fill out the GCC internal unwinding structures for
      35     the OS kernel frame as if it had been directly called from the
      36     interrupted context.
      37  
      38     This is all assuming that the code to set the handler asked the kernel
      39     to pass a pointer to such context information.
      40  
      41     There is three main parts.
      42  
      43     1) The first thing to do is to check if we are in a signal context. If
      44        not we can just return as there is nothing to do. We are probably on
      45        some foreign code for which no unwind frame can be found. If this is
      46        a call from the Windows signal handler, then:
      47  
      48     2) We must get the signal context information. 
      49  
      50        * With the standard exception filter:
      51  
      52        This is on Windows pointed to by an EXCEPTION_POINTERS. We know that
      53        the signal handle will call an UnhandledExceptionFilter with this
      54        parameter. The spec for this routine is:
      55  
      56           LONG WINAPI UnhandledExceptionFilter(struct _EXCEPTION_POINTERS*);
      57  
      58        So the pointer to struct _EXCEPTION_POINTERS must be somewhere on the
      59        stack.
      60  
      61        This was found experimentally to always be at offset 0 of the context
      62        frame in all cases handled by this implementation.
      63  
      64        * With the SEH exception handler:
      65  
      66        In this case the signal context is directly on the stack as the SEH
      67        exception handler has the following prototype:
      68  
      69           DWORD
      70           SEH_error_handler (PEXCEPTION_RECORD ExceptionRecord,
      71                              PVOID EstablisherFrame,
      72                              PCONTEXT ContextRecord,
      73                              PVOID DispatcherContext)
      74  
      75        This was found experimentally to always be at offset 56 of the
      76        context frame in all cases handled by this implementation.
      77  
      78     3) When we have the signal context we just have to save some registers
      79        and set the return address based on the program counter (Eip).
      80  
      81     Note that this implementation follows closely the same principles as the
      82     GNU/Linux and OSF ones.  */
      83  
      84  #ifndef __MINGW64__
      85  
      86  #define WIN32_MEAN_AND_LEAN
      87  #include <windows.h>
      88  /* Patterns found experimentally to be on a Windows signal handler  */
      89  
      90  /* In a standard exception filter  */
      91  
      92  #define SIG_PAT1 \
      93        (pc_[-2] == 0xff && pc_[-1] == 0xd0     /* call %eax           */ \
      94        && pc_[0] == 0x83 && pc_[1] == 0xf8)    /* cmp 0xdepl,%eax     */
      95  
      96  #define SIG_PAT2 \
      97          (pc_[-5] == 0xe8 && pc_[-4] == 0x68   /* call (depl16)       */ \
      98           && pc_[0] == 0xc3)                   /* ret                 */
      99  
     100  /* In a Win32 SEH handler  */
     101  
     102  #define SIG_SEH1 \
     103          (pc_[-5] == 0xe8                      /* call addr           */ \
     104           && pc_[0] == 0x83 && pc_[1] == 0xc4  /* add 0xval,%esp      */ \
     105           && pc_[3] == 0xb8)                   /* mov 0xval,%eax      */
     106  
     107  #define SIG_SEH2 \
     108          (pc_[-5] == 0x8b && pc_[-4] == 0x4d   /* mov depl(%ebp),%ecx */ \
     109           && pc_[0] == 0x64 && pc_[1] == 0x8b) /* mov %fs:(0),<reg>   */ \
     110  
     111  /* In the GCC alloca (stack probing)  */
     112  
     113  #define SIG_ALLOCA \
     114            (pc_[-1] == 0x83                    /* orl $0x0,(%ecx)     */ \
     115  	   && pc_[0] == 0x9 && pc_[1] == 0                              \
     116  	   && pc_[2] == 0x2d && pc_[3] == 0   /* subl $0x1000,%eax   */ \
     117  	   && pc_[4] == 0x10 && pc_[5] == 0)
     118  
     119  
     120  #define MD_FALLBACK_FRAME_STATE_FOR i386_w32_fallback_frame_state
     121  
     122  static _Unwind_Reason_Code
     123  i386_w32_fallback_frame_state (struct _Unwind_Context *context, 
     124  			       _Unwind_FrameState *fs)
     125  
     126  {
     127    void * ctx_ra_  = (void *)(context->ra);  /* return address */
     128    void * ctx_cfa_ = (void *)(context->cfa); /* context frame address */
     129    unsigned char * pc_ = (unsigned char *) ctx_ra_;
     130  
     131    /* In the test below we look for two specific patterns found
     132       experimentally to be in the Windows signal handler.  */
     133    if (SIG_PAT1 || SIG_PAT2 || SIG_SEH1 || SIG_SEH2)
     134      {
     135        PEXCEPTION_POINTERS weinfo_;
     136        PCONTEXT proc_ctx_;
     137        long new_cfa_;
     138  
     139        if (SIG_SEH1) 
     140  	proc_ctx_ = (PCONTEXT) (*(int*)(ctx_cfa_ + 56));
     141        else if (SIG_SEH2)
     142  	proc_ctx_ = (PCONTEXT) (*(int*)(ctx_cfa_ + 8));
     143        else
     144  	{
     145  	  weinfo_ = (PEXCEPTION_POINTERS) (*(int*)ctx_cfa_);
     146  	  proc_ctx_ = weinfo_->ContextRecord;
     147  	}
     148  
     149        /* The new context frame address is the stack pointer.  */
     150        new_cfa_ = proc_ctx_->Esp;
     151        fs->regs.cfa_how = CFA_REG_OFFSET;
     152        fs->regs.cfa_reg = __builtin_dwarf_sp_column();
     153        fs->regs.cfa_offset = new_cfa_ - (long) ctx_cfa_;
     154  
     155        /* Restore registers.  */
     156        fs->regs.how[0] = REG_SAVED_OFFSET;
     157        fs->regs.reg[0].loc.offset = (long)&proc_ctx_->Eax - new_cfa_;
     158        fs->regs.how[3] = REG_SAVED_OFFSET;
     159        fs->regs.reg[3].loc.offset = (long)&proc_ctx_->Ebx - new_cfa_;
     160        fs->regs.how[1] = REG_SAVED_OFFSET;
     161        fs->regs.reg[1].loc.offset = (long)&proc_ctx_->Ecx - new_cfa_;
     162        fs->regs.how[2] = REG_SAVED_OFFSET;
     163        fs->regs.reg[2].loc.offset = (long)&proc_ctx_->Edx - new_cfa_;
     164        fs->regs.how[6] = REG_SAVED_OFFSET;
     165        fs->regs.reg[6].loc.offset = (long)&proc_ctx_->Esi - new_cfa_;
     166        fs->regs.how[7] = REG_SAVED_OFFSET;
     167        fs->regs.reg[7].loc.offset = (long)&proc_ctx_->Edi - new_cfa_;
     168        fs->regs.how[5] = REG_SAVED_OFFSET;
     169        fs->regs.reg[5].loc.offset = (long)&proc_ctx_->Ebp - new_cfa_;
     170        fs->regs.how[8] = REG_SAVED_OFFSET;
     171        fs->regs.reg[8].loc.offset = (long)&proc_ctx_->Eip - new_cfa_;
     172        fs->retaddr_column = 8;
     173        fs->signal_frame = 1;
     174  
     175        return _URC_NO_REASON;
     176      }
     177  
     178    /* Unwinding through _alloca, propagating from a trap triggered by
     179       one of it's probes prior to the real SP adjustment. The only
     180       operations of interest performed is "pushl %ecx", followed by
     181       ecx clobbering.  */
     182    else if (SIG_ALLOCA) 
     183      {
     184        /* Only one push between entry in _alloca and the probe trap.  */ 
     185        long new_cfa_ = (long) ctx_cfa_ + 4;
     186  
     187        fs->regs.cfa_how = CFA_REG_OFFSET;
     188        fs->regs.cfa_reg = __builtin_dwarf_sp_column();
     189        fs->regs.cfa_offset = new_cfa_ - (long) ctx_cfa_;
     190  
     191        /* The saved value of %ecx is at CFA - 4 */
     192        fs->regs.how[1] = REG_SAVED_OFFSET;
     193        fs->regs.reg[1].loc.offset = -4;
     194  
     195        /* and what is stored at the CFA is the return address.  */
     196        fs->retaddr_column = 8;
     197        fs->regs.how[8] = REG_SAVED_OFFSET;
     198        fs->regs.reg[8].loc.offset = 0;
     199        fs->signal_frame = 1;
     200  
     201        return _URC_NO_REASON;
     202      }
     203    else
     204      return _URC_END_OF_STACK;
     205  }
     206  
     207  #endif /* !__MINGW64__ */