]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgcc/config/i386/w32-unwind.h
Update copyright years.
[thirdparty/gcc.git] / libgcc / config / i386 / w32-unwind.h
CommitLineData
5d0d1564 1/* Definitions for Dwarf2 EH unwind support for Windows32 targets
83ffe9cd 2 Copyright (C) 2007-2023 Free Software Foundation, Inc.
76f5e200
DS
3 Contributed by Pascal Obry <obry@adacore.com>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
748086b7 9Software Foundation; either version 3, or (at your option) any later
76f5e200
DS
10version.
11
76f5e200
DS
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
748086b7
JJ
17Under Section 7 of GPL version 3, you are granted additional
18permissions described in the GCC Runtime Library Exception, version
193.1, as published by the Free Software Foundation.
20
21You should have received a copy of the GNU General Public License and
22a copy of the GCC Runtime Library Exception along with this program;
23see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24<http://www.gnu.org/licenses/>. */
25
76f5e200
DS
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
58cd1d70
RO
84#ifndef __MINGW64__
85
76f5e200
DS
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
122static _Unwind_Reason_Code
123i386_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. */
76f5e200
DS
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. */
76f5e200
DS
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
5d0d1564 155 /* Restore registers. */
146e4591 156 fs->regs.how[0] = REG_SAVED_OFFSET;
76f5e200 157 fs->regs.reg[0].loc.offset = (long)&proc_ctx_->Eax - new_cfa_;
146e4591 158 fs->regs.how[3] = REG_SAVED_OFFSET;
76f5e200 159 fs->regs.reg[3].loc.offset = (long)&proc_ctx_->Ebx - new_cfa_;
146e4591 160 fs->regs.how[1] = REG_SAVED_OFFSET;
76f5e200 161 fs->regs.reg[1].loc.offset = (long)&proc_ctx_->Ecx - new_cfa_;
146e4591 162 fs->regs.how[2] = REG_SAVED_OFFSET;
76f5e200 163 fs->regs.reg[2].loc.offset = (long)&proc_ctx_->Edx - new_cfa_;
146e4591 164 fs->regs.how[6] = REG_SAVED_OFFSET;
76f5e200 165 fs->regs.reg[6].loc.offset = (long)&proc_ctx_->Esi - new_cfa_;
146e4591 166 fs->regs.how[7] = REG_SAVED_OFFSET;
76f5e200 167 fs->regs.reg[7].loc.offset = (long)&proc_ctx_->Edi - new_cfa_;
146e4591 168 fs->regs.how[5] = REG_SAVED_OFFSET;
5d0d1564 169 fs->regs.reg[5].loc.offset = (long)&proc_ctx_->Ebp - new_cfa_;
146e4591 170 fs->regs.how[8] = REG_SAVED_OFFSET;
5d0d1564 171 fs->regs.reg[8].loc.offset = (long)&proc_ctx_->Eip - new_cfa_;
76f5e200 172 fs->retaddr_column = 8;
5d0d1564
EB
173 fs->signal_frame = 1;
174
76f5e200
DS
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. */
76f5e200
DS
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 */
146e4591 192 fs->regs.how[1] = REG_SAVED_OFFSET;
76f5e200
DS
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;
146e4591 197 fs->regs.how[8] = REG_SAVED_OFFSET;
76f5e200 198 fs->regs.reg[8].loc.offset = 0;
5d0d1564
EB
199 fs->signal_frame = 1;
200
76f5e200
DS
201 return _URC_NO_REASON;
202 }
203 else
204 return _URC_END_OF_STACK;
205}
58cd1d70
RO
206
207#endif /* !__MINGW64__ */