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