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