w32-unwind.h revision 1.3 1 1.1 mrg /* Definitions for Dwarf2 EH unwind support for Windows32 targets
2 1.3 mrg Copyright (C) 2007-2015 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