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