Home | History | Annotate | Line # | Download | only in i386
w32-unwind.h revision 1.10
      1 /* Definitions for Dwarf2 EH unwind support for Windows32 targets
      2    Copyright (C) 2007-2022 Free Software Foundation, Inc.
      3    Contributed by Pascal Obry  <obry (at) 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.reg[0].how = REG_SAVED_OFFSET;
    157       fs->regs.reg[0].loc.offset = (long)&proc_ctx_->Eax - new_cfa_;
    158       fs->regs.reg[3].how = REG_SAVED_OFFSET;
    159       fs->regs.reg[3].loc.offset = (long)&proc_ctx_->Ebx - new_cfa_;
    160       fs->regs.reg[1].how = REG_SAVED_OFFSET;
    161       fs->regs.reg[1].loc.offset = (long)&proc_ctx_->Ecx - new_cfa_;
    162       fs->regs.reg[2].how = REG_SAVED_OFFSET;
    163       fs->regs.reg[2].loc.offset = (long)&proc_ctx_->Edx - new_cfa_;
    164       fs->regs.reg[6].how = REG_SAVED_OFFSET;
    165       fs->regs.reg[6].loc.offset = (long)&proc_ctx_->Esi - new_cfa_;
    166       fs->regs.reg[7].how = REG_SAVED_OFFSET;
    167       fs->regs.reg[7].loc.offset = (long)&proc_ctx_->Edi - new_cfa_;
    168       fs->regs.reg[5].how = REG_SAVED_OFFSET;
    169       fs->regs.reg[5].loc.offset = (long)&proc_ctx_->Ebp - new_cfa_;
    170       fs->regs.reg[8].how = 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.reg[1].how = 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.reg[8].how = 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__ */
    208