linux.h (MD_UNWIND_SUPPORT): Remove.
[gcc.git] / libgcc / config / i386 / w32-unwind.h
1 /* Definitions for Dwarf2 EH unwind support for Windows32 targets
2 Copyright (C) 2007, 2009, 2010, 2011
3 Free Software Foundation, Inc.
4 Contributed by Pascal Obry <obry@adacore.com>
5
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
12
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
21
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see 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 #ifndef __MINGW64__
86
87 #define WIN32_MEAN_AND_LEAN
88 #include <windows.h>
89 /* Patterns found experimentally to be on a Windows signal handler */
90
91 /* In a standard exception filter */
92
93 #define SIG_PAT1 \
94 (pc_[-2] == 0xff && pc_[-1] == 0xd0 /* call %eax */ \
95 && pc_[0] == 0x83 && pc_[1] == 0xf8) /* cmp 0xdepl,%eax */
96
97 #define SIG_PAT2 \
98 (pc_[-5] == 0xe8 && pc_[-4] == 0x68 /* call (depl16) */ \
99 && pc_[0] == 0xc3) /* ret */
100
101 /* In a Win32 SEH handler */
102
103 #define SIG_SEH1 \
104 (pc_[-5] == 0xe8 /* call addr */ \
105 && pc_[0] == 0x83 && pc_[1] == 0xc4 /* add 0xval,%esp */ \
106 && pc_[3] == 0xb8) /* mov 0xval,%eax */
107
108 #define SIG_SEH2 \
109 (pc_[-5] == 0x8b && pc_[-4] == 0x4d /* mov depl(%ebp),%ecx */ \
110 && pc_[0] == 0x64 && pc_[1] == 0x8b) /* mov %fs:(0),<reg> */ \
111
112 /* In the GCC alloca (stack probing) */
113
114 #define SIG_ALLOCA \
115 (pc_[-1] == 0x83 /* orl $0x0,(%ecx) */ \
116 && pc_[0] == 0x9 && pc_[1] == 0 \
117 && pc_[2] == 0x2d && pc_[3] == 0 /* subl $0x1000,%eax */ \
118 && pc_[4] == 0x10 && pc_[5] == 0)
119
120
121 #define MD_FALLBACK_FRAME_STATE_FOR i386_w32_fallback_frame_state
122
123 static _Unwind_Reason_Code
124 i386_w32_fallback_frame_state (struct _Unwind_Context *context,
125 _Unwind_FrameState *fs)
126
127 {
128 void * ctx_ra_ = (void *)(context->ra); /* return address */
129 void * ctx_cfa_ = (void *)(context->cfa); /* context frame address */
130 unsigned char * pc_ = (unsigned char *) ctx_ra_;
131
132 /* In the test below we look for two specific patterns found
133 experimentally to be in the Windows signal handler. */
134 if (SIG_PAT1 || SIG_PAT2 || SIG_SEH1 || SIG_SEH2)
135 {
136 PEXCEPTION_POINTERS weinfo_;
137 PCONTEXT proc_ctx_;
138 long new_cfa_;
139
140 if (SIG_SEH1)
141 proc_ctx_ = (PCONTEXT) (*(int*)(ctx_cfa_ + 56));
142 else if (SIG_SEH2)
143 proc_ctx_ = (PCONTEXT) (*(int*)(ctx_cfa_ + 8));
144 else
145 {
146 weinfo_ = (PEXCEPTION_POINTERS) (*(int*)ctx_cfa_);
147 proc_ctx_ = weinfo_->ContextRecord;
148 }
149
150 /* The new context frame address is the stack pointer. */
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 /* Restore registers. */
157 fs->regs.reg[0].how = REG_SAVED_OFFSET;
158 fs->regs.reg[0].loc.offset = (long)&proc_ctx_->Eax - new_cfa_;
159 fs->regs.reg[3].how = REG_SAVED_OFFSET;
160 fs->regs.reg[3].loc.offset = (long)&proc_ctx_->Ebx - new_cfa_;
161 fs->regs.reg[1].how = REG_SAVED_OFFSET;
162 fs->regs.reg[1].loc.offset = (long)&proc_ctx_->Ecx - new_cfa_;
163 fs->regs.reg[2].how = REG_SAVED_OFFSET;
164 fs->regs.reg[2].loc.offset = (long)&proc_ctx_->Edx - new_cfa_;
165 fs->regs.reg[6].how = REG_SAVED_OFFSET;
166 fs->regs.reg[6].loc.offset = (long)&proc_ctx_->Esi - new_cfa_;
167 fs->regs.reg[7].how = REG_SAVED_OFFSET;
168 fs->regs.reg[7].loc.offset = (long)&proc_ctx_->Edi - new_cfa_;
169 fs->regs.reg[5].how = REG_SAVED_OFFSET;
170 fs->regs.reg[5].loc.offset = (long)&proc_ctx_->Ebp - new_cfa_;
171 fs->regs.reg[8].how = REG_SAVED_OFFSET;
172 fs->regs.reg[8].loc.offset = (long)&proc_ctx_->Eip - new_cfa_;
173 fs->retaddr_column = 8;
174 fs->signal_frame = 1;
175
176 return _URC_NO_REASON;
177 }
178
179 /* Unwinding through _alloca, propagating from a trap triggered by
180 one of it's probes prior to the real SP adjustment. The only
181 operations of interest performed is "pushl %ecx", followed by
182 ecx clobbering. */
183 else if (SIG_ALLOCA)
184 {
185 /* Only one push between entry in _alloca and the probe trap. */
186 long new_cfa_ = (long) ctx_cfa_ + 4;
187
188 fs->regs.cfa_how = CFA_REG_OFFSET;
189 fs->regs.cfa_reg = __builtin_dwarf_sp_column();
190 fs->regs.cfa_offset = new_cfa_ - (long) ctx_cfa_;
191
192 /* The saved value of %ecx is at CFA - 4 */
193 fs->regs.reg[1].how = REG_SAVED_OFFSET;
194 fs->regs.reg[1].loc.offset = -4;
195
196 /* and what is stored at the CFA is the return address. */
197 fs->retaddr_column = 8;
198 fs->regs.reg[8].how = REG_SAVED_OFFSET;
199 fs->regs.reg[8].loc.offset = 0;
200 fs->signal_frame = 1;
201
202 return _URC_NO_REASON;
203 }
204 else
205 return _URC_END_OF_STACK;
206 }
207
208 #endif /* !__MINGW64__ */