source: trunk/src/kernel32/seh/sehutil.s@ 21787

Last change on this file since 21787 was 21662, checked in by dmik, 14 years ago

kernel32: SEH: Disabled unwinding Win32 exception handlers in response to the OS/2 unwind procedure to prevent endless recursive crashes inside OS2RtlUnwind() happening due to stack corruption under SMP kernel when too many threads are being unwound at once. Closes #37.

File size: 10.0 KB
Line 
1/*
2 * Project Odin Software License can be found in LICENSE.TXT
3 *
4 * Compiler-level Win32 SEH support for OS/2
5 *
6 * Copyright 2010 Dmitriy Kuminov
7 */
8
9.global ___seh_handler
10.global ___seh_get_prev_frame
11
12/*
13 * extern "C"
14 * int __seh_handler(PEXCEPTION_RECORD pRec,
15 * struct ___seh_EXCEPTION_FRAME *pFrame,
16 * PCONTEXT pContext, PVOID)
17 *
18 * Win32 structured exception handler that implements the __try/__except
19 * functionality for GCC.
20 *
21 * NOTE: This is a heavily platform specific stuff. The code depends on the
22 * struct ___seh_EXCEPTION_FRAME layout so be very careful and keep both
23 * in sync!
24 *
25 * __cdecl: EAX/ECX/EDX are not preserved, result in EAX/EDX, caller cleans up
26 * the stack.
27 */
28
29___seh_handler:
30
31 pushl %ebp
32 movl %esp, %ebp
33
34 /*
35 * 8(%ebp) - pRec
36 * 12(%ebp) - pFrame
37 * 16(%ebp) - pContext
38 * 20(%ebp) - pVoid
39 */
40
41 /* preserve used registers */
42 pushl %ebx
43 pushl %edi
44 pushl %esi
45
46 movl %fs, %eax
47 andl $0x0000FFFF, %eax
48 cmpl $Dos32TIB, %eax /* Running along the OS/2 chain? */
49 jne ___seh_handler_Win32 /* No, assume the Win32 chain */
50
51 /* Note: Unwinding is disabled here since a) it is more correct to do
52 * centralized unwinding from OS2ExceptionHandler2ndLevel() (i.e. not only
53 * for SEH frames) and b) it crashes under SMP kernel due to stack being
54 * corrupt by the time when unwinding happens. See comments in
55 * OS2ExceptionHandler2ndLevel() for more details. */
56
57#if 0
58
59 movl 8(%ebp), %eax
60 movl 4(%eax), %eax /* fHandlerFlags */
61 testl $0x02, %eax /* EH_UNWINDING? */
62 jne ___seh_handler_OS2_Unwind
63
64 /* restore the OS/2 chain in our frame */
65 movl 12(%ebp), %eax
66 movl 44(%eax), %ecx /* pPrevFrameOS2 */
67 movl %ecx, 0(%eax) /* pPrev */
68
69 xorl %eax, %eax /* return XCPT_CONTINUE_SEARCH (0) */
70 jmp ___seh_handler_Return
71
72___seh_handler_OS2_Unwind:
73
74 /* unwind the Win32 chain including our frame as someone's definitely
75 * jumping outside it if we're being unwound by OS/2 */
76 movl 12(%ebp), %eax
77 cmpl $0, 64(%eax) /* Win32FS == 0? */
78 je ___seh_handler_OS2_Unwind_End /* Yes, we already unwound this frame */
79
80 /* restore the Win32 chain in our frame */
81 movl 60(%eax), %ebx /* pPrevFrameWin32 */
82 movl %ebx, 0(%eax) /* pPrev */
83
84 pushl %fs
85
86 pushl 64(%eax) /* Win32FS */
87 popl %fs
88
89 pushl $0 /* DWORD (unused) */
90 pushl $0 /* PEXCEPTION_RECORD */
91 pushl $0 /* LPVOID (unused) */
92 pushl %ebx /* PEXCEPTION_FRAME */
93 call _RtlUnwind@16 /* _stdcall, rtl, callee cleans stack */
94
95 popl %fs
96
97 /* restore the OS/2 chain in our frame */
98 movl 12(%ebp), %eax
99 movl 44(%eax), %ecx /* pPrevFrameOS2 */
100 movl %ecx, 0(%eax) /* pPrev */
101
102___seh_handler_OS2_Unwind_End:
103
104 xor %eax, %eax /* return code is irrelevant for EH_UNWINDING */
105 jmp ___seh_handler_Return
106
107#else
108
109 /* restore the OS/2 chain in our frame */
110 movl 12(%ebp), %eax
111 movl 44(%eax), %ecx /* pPrevFrameOS2 */
112 movl %ecx, 0(%eax) /* pPrev */
113
114 xorl %eax, %eax /* return XCPT_CONTINUE_SEARCH (0) */
115 jmp ___seh_handler_Return
116
117#endif
118
119___seh_handler_Win32:
120
121 /* restore the Win32 chain in our frame */
122 movl 12(%ebp), %eax
123 movl 60(%eax), %ecx /* pPrevFrameWin32 */
124 movl %ecx, 0(%eax) /* pPrev */
125
126 /* skip EH_UNWINDING calls (for compatibility with MSVC) */
127 movl 8(%ebp), %ebx
128 movl 4(%ebx), %eax /* pRec->ExceptionFlags */
129 testl $0x2, %eax /* EH_UNWINDING? */
130 je ___seh_handler_Win32_NotUnwinding /* No, continue normally */
131
132 /* See the comment above */
133#if 0
134 /* clear out the Win32FS field so that we will not attempt to unwind twice
135 * (first, as a result of forced unwind from ExitProcess/ExitThread/etc and
136 * then from the OS/2 EH_UNWINDING call of our handler) */
137 movl 12(%ebp), %eax
138 movl $0, 64(%eax) /* Win32FS */
139#endif
140
141 movl $1, %eax /* ExceptionContinueSearch */
142 jmp ___seh_handler_Return
143
144___seh_handler_Win32_NotUnwinding:
145
146 /* save handler's context */
147 pushl %ebp
148 pushl $0 /* reserve space for length, must be saved right before ESP! */
149 pushl %esp /* ESP must be saved last! */
150
151 movl 12(%ebp), %ebx
152 movl $0f, 12(%ebx) /* pFrame->pHandlerCallback */
153
154 /* get the size of the handler's stack */
155 movl 40(%ebx), %ecx /* pFrame->pTryRegs[4] is ESP */
156 subl %esp, %ecx
157 jle ___seh_handler_Error /* Invalid stack! */
158 movl %ecx, 4(%esp) /* save length */
159
160 /* check that EXCEPTION_RECORD and CONTEXT are on our stack
161 * and save their offsets in pFrame */
162 movl 8(%ebp), %eax
163 subl %esp, %eax
164 jl ___seh_handler_Error /* Invalid stack! */
165 cmpl %ecx, %eax
166 jg ___seh_handler_Error /* Invalid stack! */
167 movl %eax, 48(%ebx) /* pFrame->Pointers.ExceptionRecord */
168
169 movl 16(%ebp), %eax
170 subl %esp, %eax
171 jl ___seh_handler_Error /* Invalid stack! */
172 cmpl %ecx, %eax
173 jg ___seh_handler_Error /* Invalid stack! */
174 movl %eax, 52(%ebx) /* pFrame->Pointers.ContextRecord */
175
176 /* save the handler's stack on heap */
177 movl %ecx, %eax /* size_t */
178 subl $4, %esp
179 movl %eax, 0(%esp)
180 call odin_malloc /* _Optlink, rtl, EAX/EDX/ECX-in, caller cleans stack */
181 addl $4, %esp
182 testl %eax, %eax
183 je ___seh_handler_Error /* No memory! */
184 movl 4(%esp), %ecx
185 movl %eax, %edi
186 movl %edi, 16(%ebx) /* pFrame->pHandlerContext */
187 movl %esp, %esi
188 rep movsb
189
190 /* correct Pointers offsets to point to the saved stack on heap */
191 movl 16(%ebx), %eax /* pFrame->pHandlerContext */
192 addl %eax, 48(%ebx) /* pFrame->Pointers.ExceptionRecord */
193 addl %eax, 52(%ebx) /* pFrame->Pointers.ContextRecord */
194
195 /* restore __try/__catch context */
196 movl 12(%ebp), %eax
197 movl 24(%eax), %ebx /* pFrame->pTryRegs */
198 movl 28(%eax), %esi
199 movl 32(%eax), %edi
200 movl 36(%eax), %ebp
201 movl 40(%eax), %esp
202
203 /* jump to the filter callback */
204 movl $1, 56(%eax) /* pFrame->state */
205 jmp *8(%eax) /* pFrame->pFilterCallback */
206
2070:
208 /* restore handler's context (we assume that the callback puts the address
209 * of pFrame back to EBX!) */
210 movl 16(%ebx), %esi /* pFrame->pHandlerContext */
211 movl 0(%esi), %esp /* restore saved ESP */
212 movl 4(%esi), %ecx /* saved stack length */
213 subl $4, %esp /* correct ESP to compensate for PUSH ESP logic */
214 movl %esp, %edi
215 rep movsb
216
217 popl %esp
218 addl $4, %esp
219 popl %ebp
220
221 /* free heap block */
222 movl 16(%ebx), %eax /* pFrame->pHandlerContext */
223 subl $4, %esp
224 movl %eax, 0(%esp)
225 call odin_free /* _Optlink, rtl, EAX/EDX/ECX-in, caller cleans stack */
226 addl $4, %esp
227
228 /* analyze filter result */
229 movl 20(%ebx), %eax /* pFrame->filterResult */
230 cmpl $1, %eax /* EXCEPTION_EXECUTE_HANDLER? */
231 je ___seh_handler_Unwind
232 cmpl $-1, %eax /* EXCEPTION_CONTINUE_EXECUTION? */
233 jne 1f
234 movl $0, 56(%ebx) /* pFrame->state */
235 movl $0, %eax /* ExceptionContinueExecution */
236 jmp ___seh_handler_Return
2371:
238 /* assume EXCEPTION_CONTINUE_SEARCH (0) */
239 movl $1, %eax /* ExceptionContinueSearch */
240 jmp ___seh_handler_Return
241
242___seh_handler_Unwind:
243
244 /* unwind Win32 exception chain up to ours */
245 pushl $0 /* DWORD (unused) */
246 pushl 8(%ebp) /* PEXCEPTION_RECORD */
247 pushl $0 /* LPVOID (unused) */
248 pushl 12(%ebp) /* PEXCEPTION_FRAME */
249 call _RtlUnwind@16 /* _stdcall, rtl, callee cleans stack */
250
251 /* restore the OS/2 chain in our frame */
252 movl 12(%ebp), %eax
253 movl 44(%eax), %ecx /* pPrevFrameOS2 */
254 movl %ecx, 0(%eax) /* pPrev */
255
256 /* unwind OS/2 exception chain up to ours */
257 movl %fs, %ebx
258 pushl $Dos32TIB
259 popl %fs
260 pushl $0 /* PEXCEPTIONREPORTRECORD */
261 pushl $1f /* PVOID pTargetIP */
262 pushl 12(%ebp) /* PEXCEPTIONREGISTRATIONRECORD */
263 call _DosUnwindException /* _syscall, rtl, but stack is not restored! */
2641:
265 movl %ebx, %fs
266
267 /* restore the Win32 chain in our frame */
268 movl 12(%ebp), %eax
269 movl 60(%eax), %ecx /* pPrevFrameWin32 */
270 movl %ecx, 0(%eax) /* pPrev */
271
272 /* restore __try/__except context */
273 movl 12(%ebp), %eax
274 movl 24(%eax), %ebx
275 movl 28(%eax), %esi
276 movl 32(%eax), %edi
277 movl 36(%eax), %ebp
278 movl 40(%eax), %esp
279
280 /* jump to __except */
281 movl $2, 56(%eax) /* pFrame->state */
282 jmp *8(%eax) /* pFrame->pFilterCallback */
283
284___seh_handler_Error:
285
286 addl $8, %esp
287 popl %ebp
288
289 movl $1, %eax /* ExceptionContinueSearch */
290
291___seh_handler_Return:
292
293 popl %esi
294 popl %edi
295 popl %ebx
296
297 popl %ebp
298 ret
299
300/*
301 * extern "C"
302 * EXCEPTION_FRAME *__seh_get_prev_frame(EXCEPTION_FRAME *pFrame)
303 *
304 * Returns the previous Win32 exception frame given an existing frame.
305 *
306 * If SEH is used, the pFrame->Prev expression will not always return a correct
307 * result: the SEH handler is installed on both the Win32 and OS/2 exception
308 * chains and it dynamically substitutes the Prev field in its frame with the
309 * Win32 or OS/2 tail depending on the source of the exception. This function
310 * takes this into account and always returns the correct pointer to the
311 * previous Win32 frame.
312 *
313 * NOTE: This is a heavily platform specific stuff. The code depends on the
314 * struct ___seh_EXCEPTION_FRAME layout so be very careful and keep both
315 * in sync!
316 *
317 * __cdecl: EAX/ECX/EDX are not preserved, result in EAX/EDX, caller cleans up
318 * the stack.
319 */
320
321___seh_get_prev_frame:
322
323 /*
324 * 4(%esp) - pFrame
325 */
326
327 movl 4(%esp), %eax
328 /*leal ___seh_handler, %ecx*/
329 movl 4(%eax), %ecx /* pFrame->Handler */
330 cmpl $___seh_handler, %ecx
331 jne ___seh_get_prev_frame_Normal
332
333 movl 60(%eax), %eax /* pPrevFrameWin32 */
334 ret
335
336___seh_get_prev_frame_Normal:
337
338 movl 0(%eax), %eax /* pFrame->Prev */
339 ret
340
Note: See TracBrowser for help on using the repository browser.