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

Last change on this file since 22011 was 22004, checked in by dmik, 13 years ago

kernel32: Pick up register changes in try/except filter.

This is a regression of r21999. Fixes endless exceptions eventually
leading to program termination.

File size: 17.2 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_handler_filter
11.global ___seh_handler_win32
12.global ___seh_get_prev_frame_win32
13
14#define sizeof_WINEXCEPTION_RECORD 80
15#define sizeof_WINCONTEXT 296
16
17/*
18 * extern "C"
19 * int __seh_handler(PEXCEPTIONREPORTRECORD pRec,
20 * struct ___seh_EXCEPTION_FRAME *pFrame,
21 * PCONTEXTRECORD pContext, PVOID)
22 *
23 * OS/2 exception handler that implements the __try/__except
24 * functionality for GCC in non-ODIN_FORCE_WIN32_TIB mode.
25 *
26 * NOTE: This is a heavily platform specific stuff. The code depends on the
27 * struct ___seh_EXCEPTION_FRAME layout so be very careful and keep both
28 * in sync!
29 *
30 * __cdecl: EAX/ECX/EDX are not preserved, result in EAX/EDX, caller cleans up
31 * the stack.
32 */
33___seh_handler:
34
35 /* call the common handler to do the job */
36 jmp OS2ExceptionHandler2ndLevel
37
38/*
39 * extern "C"
40 * BOOL __seh_handler_filter(PEXCEPTIONREPORTRECORD pRec,
41 * struct ___seh_EXCEPTION_FRAME *pFrame,
42 * PCONTEXTRECORD pContext)
43 *
44 * Calls the filter expression of the __try/__except block
45 * in non-ODIN_FORCE_WIN32_TIB mode.
46 *
47 * Return TRUE if the filter asks to continue execution and FALSE
48 * otherwise. Note that if the filter chooses to execute the __except block,
49 * this function does not return.
50 *
51 * NOTE: This is a heavily platform specific stuff. The code depends on the
52 * struct ___seh_EXCEPTION_FRAME layout so be very careful and keep both
53 * in sync!
54 *
55 * __cdecl: EAX/ECX/EDX are not preserved, result in EAX/EDX, caller cleans up
56 * the stack.
57 */
58
59___seh_handler_filter:
60
61 /*
62 * 8(%ebp) - pRec
63 * 12(%ebp) - pFrame
64 * 16(%ebp) - pContext
65 */
66
67 pushl %ebp
68 movl %esp, %ebp
69
70 /* preserve used registers */
71 pushl %ebx
72 pushl %edi
73 pushl %esi
74
75 /* save handler's context */
76 pushl %ebp
77 pushl $0 /* reserve space for length, must be saved right before ESP! */
78 pushl %esp /* ESP must be saved last! */
79
80 movl 12(%ebp), %ebx
81 movl $0f, 12(%ebx) /* pFrame->pHandlerCallback */
82
83 /* get the size of the handler's stack */
84 movl 40(%ebx), %ecx /* pFrame->ESP */
85 subl %esp, %ecx
86 jle ___seh_handler_Error /* Invalid stack! */
87 movl %ecx, 4(%esp) /* save length */
88
89 /* save the handler's stack on heap */
90 movl %ecx, %eax /* size_t */
91 /* also reserve space for Win32 exeption info structs */
92 addl $(sizeof_WINEXCEPTION_RECORD + sizeof_WINCONTEXT), %eax
93 subl $4, %esp
94 movl %eax, 0(%esp)
95 call _malloc /* __cdecl (and _Optlink compatible -> EAX/EDX/ECX-in) */
96 addl $4, %esp
97 testl %eax, %eax
98 je ___seh_handler_Error /* No memory! */
99 movl 4(%esp), %ecx
100 movl %eax, %edi
101 movl %edi, 16(%ebx) /* pFrame->pHandlerContext */
102 movl %esp, %esi
103 rep movsb
104
105 /* convert OS/2 exception info -> Win32 */
106 movl 16(%ebx), %eax /* pFrame->pHandlerContext */
107 addl 4(%esp), %eax /* + size of stack = ptr to WINCONTEXT */
108 movl %eax, 48(%ebx) /* pFrame->Pointers.ContextRecord */
109 addl $sizeof_WINCONTEXT, %eax/* = ptr to WINEXCEPTION_RECORD */
110 movl %eax, 44(%ebx) /* pFrame->Pointers.ExceptionRecord */
111 pushl $0 /* TEB* */
112 pushl 48(%ebx) /* WINCONTEXT */
113 pushl 44(%ebx) /* WINEXCEPTION_RECORD */
114 pushl 16(%ebp) /* pContext */
115 pushl 8(%ebp) /* pRec */
116 call OSLibConvertExceptionInfo
117 addl $20, %esp
118 jne ___seh_handler_CallFilter/* conversion successful? */
119
120 /* free heap block */
121 movl 16(%ebx), %eax /* pFrame->pHandlerContext */
122 subl $4, %esp
123 movl %eax, 0(%esp)
124 call _free /* __cdecl (and _Optlink compatible -> EAX/EDX/ECX-in) */
125 addl $4, %esp
126 jmp ___seh_handler_Error /* Info conversion error! */
127
128___seh_handler_CallFilter:
129
130 /* restore __try/__catch context */
131 movl 12(%ebp), %eax
132 movl 24(%eax), %ebx /* pFrame->EBX */
133 movl 28(%eax), %esi /* pFrame->ESI */
134 movl 32(%eax), %edi /* pFrame->EDI */
135 movl 36(%eax), %ebp /* pFrame->EBP */
136 movl 40(%eax), %esp /* pFrame->ESP */
137
138 /* jump to the filter callback */
139 movl $1, 52(%eax) /* pFrame->state */
140 jmp *8(%eax) /* pFrame->pFilterCallback */
141
1420:
143 /* restore handler's context (we assume that the callback puts the address
144 * of pFrame back to EBX!) */
145 movl 16(%ebx), %esi /* pFrame->pHandlerContext */
146 movl 0(%esi), %esp /* restore saved ESP */
147 movl 4(%esi), %ecx /* saved stack length */
148 subl $4, %esp /* correct ESP to compensate for PUSH ESP logic */
149 movl %esp, %edi
150 rep movsb
151
152 popl %esp
153 addl $4, %esp
154 popl %ebp
155
156 /* analyze filter result */
157 movl 20(%ebx), %eax /* pFrame->filterResult */
158 cmpl $1, %eax /* EXCEPTION_EXECUTE_HANDLER? */
159 je ___seh_handler_FreeMem
160 cmpl $-1, %eax /* EXCEPTION_CONTINUE_EXECUTION? */
161 jne 1f
162 movl $0, %eax /* ExceptionContinueExecution */
163 jmp 2f
1641:
165 /* Assume EXCEPTION_CONTIUNE_SEARCH */
166 movl $1, %eax /* ExceptionContinueSearch */
1672:
168
169 /* convert Win32 exception info back to OS/2 */
170 pushl 16(%ebp) /* pContext */
171 pushl 48(%ebx) /* pFrame->Pointers.ContextRecord */
172 pushl %eax /* rc */
173 call OSLibConvertExceptionResult
174 addl $12, %esp
175 pushl %eax /* save result */
176
177___seh_handler_FreeMem:
178
179 /* free heap block */
180 movl 16(%ebx), %eax /* pFrame->pHandlerContext */
181 subl $4, %esp
182 movl %eax, 0(%esp)
183 call _free /* __cdecl (and _Optlink compatible -> EAX/EDX/ECX-in) */
184 addl $4, %esp
185
186 /* analyze filter result again */
187 movl 20(%ebx), %eax /* pFrame->filterResult */
188 cmpl $1, %eax /* EXCEPTION_EXECUTE_HANDLER? */
189 je ___seh_handler_Unwind
190
191 popl %eax /* restore OSLibConvertExceptionResult result */
192
193 /* %eax already contains TRUE if the execution should continue
194 * and FALSE otherwise */
195 cmp $0, %eax /* FALSE (= continue search)? */
196 je 1f
197 movl $0, 52(%ebx) /* pFrame->state */
198 movl $1, %eax /* TRUE */
199 jmp ___seh_handler_Return
2001:
201 xorl %eax, %eax /* FALSE */
202 jmp ___seh_handler_Return
203
204___seh_handler_Unwind:
205
206 /* unwind OS/2 exception chain up to ours */
207 pushl $0 /* PEXCEPTIONREPORTRECORD */
208 pushl $1f /* PVOID pTargetIP */
209 pushl 12(%ebp) /* PEXCEPTIONREGISTRATIONRECORD */
210 call _DosUnwindException /* _syscall, rtl, but stack is not restored! */
2111:
212 /* restore __try/__except context */
213 movl 12(%ebp), %eax
214 movl 24(%eax), %ebx
215 movl 28(%eax), %esi
216 movl 32(%eax), %edi
217 movl 36(%eax), %ebp
218 movl 40(%eax), %esp
219
220 /* jump to __except */
221 movl $2, 52(%eax) /* pFrame->state */
222 jmp *8(%eax) /* pFrame->pFilterCallback */
223
224___seh_handler_Error:
225
226 addl $8, %esp
227 popl %ebp
228
229 xorl %eax, %eax /* return XCPT_CONTINUE_SEARCH (0) */
230
231___seh_handler_Return:
232
233 popl %esi
234 popl %edi
235 popl %ebx
236
237 popl %ebp
238 ret
239
240/*
241 * extern "C"
242 * int __seh_handler_win32(PEXCEPTION_RECORD pRec,
243 * struct ___seh_EXCEPTION_FRAME *pFrame,
244 * PCONTEXT pContext, PVOID)
245 *
246 * Win32 structured exception handler that implements the __try/__except
247 * functionality for GCC in ODIN_FORCE_WIN32_TIB mode.
248 *
249 * NOTE: This is a heavily platform specific stuff. The code depends on the
250 * struct ___seh_EXCEPTION_FRAME layout so be very careful and keep both
251 * in sync!
252 *
253 * __cdecl: EAX/ECX/EDX are not preserved, result in EAX/EDX, caller cleans up
254 * the stack.
255 */
256
257___seh_handler_win32:
258
259 pushl %ebp
260 movl %esp, %ebp
261
262 /*
263 * 8(%ebp) - pRec
264 * 12(%ebp) - pFrame
265 * 16(%ebp) - pContext
266 * 20(%ebp) - pVoid
267 */
268
269 /* preserve used registers */
270 pushl %ebx
271 pushl %edi
272 pushl %esi
273
274 movl %fs, %eax
275 andl $0x0000FFFF, %eax
276 cmpl $Dos32TIB, %eax /* Running along the OS/2 chain? */
277 jne __seh_handler_win32_Win32 /* No, assume the Win32 chain */
278
279 /* Note: Unwinding is disabled here since a) it is more correct to do
280 * centralized unwinding from OS2ExceptionHandler2ndLevel() (i.e. not only
281 * for SEH frames) and b) it crashes under SMP kernel due to stack being
282 * corrupt by the time when unwinding happens. See comments in
283 * OS2ExceptionHandler2ndLevel() for more details. */
284
285#if 0
286
287 movl 8(%ebp), %eax
288 movl 4(%eax), %eax /* fHandlerFlags */
289 testl $0x02, %eax /* EH_UNWINDING? */
290 jne __seh_handler_win32_OS2_Unwind
291
292 /* restore the OS/2 chain in our frame */
293 movl 12(%ebp), %eax
294 movl 44(%eax), %ecx /* pPrevFrameOS2 */
295 movl %ecx, 0(%eax) /* pPrev */
296
297 xorl %eax, %eax /* return XCPT_CONTINUE_SEARCH (0) */
298 jmp __seh_handler_win32_Return
299
300__seh_handler_win32_OS2_Unwind:
301
302 /* unwind the Win32 chain including our frame as someone's definitely
303 * jumping outside it if we're being unwound by OS/2 */
304 movl 12(%ebp), %eax
305 cmpl $0, 64(%eax) /* Win32FS == 0? */
306 je __seh_handler_win32_OS2_Unwind_End /* Yes, we already unwound this frame */
307
308 /* restore the Win32 chain in our frame */
309 movl 60(%eax), %ebx /* pPrevFrameWin32 */
310 movl %ebx, 0(%eax) /* pPrev */
311
312 pushl %fs
313
314 pushl 64(%eax) /* Win32FS */
315 popl %fs
316
317 pushl $0 /* DWORD (unused) */
318 pushl $0 /* PEXCEPTION_RECORD */
319 pushl $0 /* LPVOID (unused) */
320 pushl %ebx /* PEXCEPTION_FRAME */
321 call _RtlUnwind@16 /* _stdcall, rtl, callee cleans stack */
322
323 popl %fs
324
325 /* restore the OS/2 chain in our frame */
326 movl 12(%ebp), %eax
327 movl 44(%eax), %ecx /* pPrevFrameOS2 */
328 movl %ecx, 0(%eax) /* pPrev */
329
330__seh_handler_win32_OS2_Unwind_End:
331
332 xor %eax, %eax /* return code is irrelevant for EH_UNWINDING */
333 jmp __seh_handler_win32_Return
334
335#else
336
337 /* restore the OS/2 chain in our frame */
338 movl 12(%ebp), %eax
339 movl 44(%eax), %ecx /* pPrevFrameOS2 */
340 movl %ecx, 0(%eax) /* pPrev */
341
342 xorl %eax, %eax /* return XCPT_CONTINUE_SEARCH (0) */
343 jmp __seh_handler_win32_Return
344
345#endif
346
347__seh_handler_win32_Win32:
348
349 /* restore the Win32 chain in our frame */
350 movl 12(%ebp), %eax
351 movl 60(%eax), %ecx /* pPrevFrameWin32 */
352 movl %ecx, 0(%eax) /* pPrev */
353
354 /* skip EH_UNWINDING calls (for compatibility with MSVC) */
355 movl 8(%ebp), %ebx
356 movl 4(%ebx), %eax /* pRec->ExceptionFlags */
357 testl $0x2, %eax /* EH_UNWINDING? */
358 je ___seh_handler_win32_Win32_NotUnwinding /* No, continue normally */
359
360 /* See the comment above */
361#if 0
362 /* clear out the Win32FS field so that we will not attempt to unwind twice
363 * (first, as a result of forced unwind from ExitProcess/ExitThread/etc and
364 * then from the OS/2 EH_UNWINDING call of our handler) */
365 movl 12(%ebp), %eax
366 movl $0, 64(%eax) /* Win32FS */
367#endif
368
369 movl $1, %eax /* ExceptionContinueSearch */
370 jmp __seh_handler_win32_Return
371
372___seh_handler_win32_Win32_NotUnwinding:
373
374 /* save handler's context */
375 pushl %ebp
376 pushl $0 /* reserve space for length, must be saved right before ESP! */
377 pushl %esp /* ESP must be saved last! */
378
379 movl 12(%ebp), %ebx
380 movl $0f, 12(%ebx) /* pFrame->pHandlerCallback */
381
382 /* get the size of the handler's stack */
383 movl 40(%ebx), %ecx /* pFrame->pTryRegs[4] is ESP */
384 subl %esp, %ecx
385 jle __seh_handler_win32_Error /* Invalid stack! */
386 movl %ecx, 4(%esp) /* save length */
387
388 /* check that EXCEPTION_RECORD and CONTEXT are on our stack
389 * and save their offsets in pFrame */
390 movl 8(%ebp), %eax
391 subl %esp, %eax
392 jl __seh_handler_win32_Error /* Invalid stack! */
393 cmpl %ecx, %eax
394 jg __seh_handler_win32_Error /* Invalid stack! */
395 movl %eax, 48(%ebx) /* pFrame->Pointers.ExceptionRecord */
396
397 movl 16(%ebp), %eax
398 subl %esp, %eax
399 jl __seh_handler_win32_Error /* Invalid stack! */
400 cmpl %ecx, %eax
401 jg __seh_handler_win32_Error /* Invalid stack! */
402 movl %eax, 52(%ebx) /* pFrame->Pointers.ContextRecord */
403
404 /* save the handler's stack on heap */
405 movl %ecx, %eax /* size_t */
406 subl $4, %esp
407 movl %eax, 0(%esp)
408 call _malloc /* __cdecl (and _Optlink compatible -> EAX/EDX/ECX-in) */
409 addl $4, %esp
410 testl %eax, %eax
411 je __seh_handler_win32_Error /* No memory! */
412 movl 4(%esp), %ecx
413 movl %eax, %edi
414 movl %edi, 16(%ebx) /* pFrame->pHandlerContext */
415 movl %esp, %esi
416 rep movsb
417
418 /* correct Pointers offsets to point to the saved stack on heap */
419 movl 16(%ebx), %eax /* pFrame->pHandlerContext */
420 addl %eax, 48(%ebx) /* pFrame->Pointers.ExceptionRecord */
421 addl %eax, 52(%ebx) /* pFrame->Pointers.ContextRecord */
422
423 /* restore __try/__catch context */
424 movl 12(%ebp), %eax
425 movl 24(%eax), %ebx /* pFrame->pTryRegs */
426 movl 28(%eax), %esi
427 movl 32(%eax), %edi
428 movl 36(%eax), %ebp
429 movl 40(%eax), %esp
430
431 /* jump to the filter callback */
432 movl $1, 56(%eax) /* pFrame->state */
433 jmp *8(%eax) /* pFrame->pFilterCallback */
434
4350:
436 /* restore handler's context (we assume that the callback puts the address
437 * of pFrame back to EBX!) */
438 movl 16(%ebx), %esi /* pFrame->pHandlerContext */
439 movl 0(%esi), %esp /* restore saved ESP */
440 movl 4(%esi), %ecx /* saved stack length */
441 subl $4, %esp /* correct ESP to compensate for PUSH ESP logic */
442 movl %esp, %edi
443 rep movsb
444
445 popl %esp
446 addl $4, %esp
447 popl %ebp
448
449 /* free heap block */
450 movl 16(%ebx), %eax /* pFrame->pHandlerContext */
451 subl $4, %esp
452 movl %eax, 0(%esp)
453 call _free /* __cdecl (and _Optlink compatible -> EAX/EDX/ECX-in) */
454 addl $4, %esp
455
456 /* analyze filter result */
457 movl 20(%ebx), %eax /* pFrame->filterResult */
458 cmpl $1, %eax /* EXCEPTION_EXECUTE_HANDLER? */
459 je __seh_handler_win32_Unwind
460 cmpl $-1, %eax /* EXCEPTION_CONTINUE_EXECUTION? */
461 jne 1f
462 movl $0, 56(%ebx) /* pFrame->state */
463 movl $0, %eax /* ExceptionContinueExecution */
464 jmp __seh_handler_win32_Return
4651:
466 /* assume EXCEPTION_CONTINUE_SEARCH (0) */
467 movl $1, %eax /* ExceptionContinueSearch */
468 jmp __seh_handler_win32_Return
469
470__seh_handler_win32_Unwind:
471
472 /* unwind Win32 exception chain up to ours */
473 pushl $0 /* DWORD (unused) */
474 pushl 8(%ebp) /* PEXCEPTION_RECORD */
475 pushl $0 /* LPVOID (unused) */
476 pushl 12(%ebp) /* PEXCEPTION_FRAME */
477 call _RtlUnwind@16 /* _stdcall, rtl, callee cleans stack */
478
479 /* restore the OS/2 chain in our frame */
480 movl 12(%ebp), %eax
481 movl 44(%eax), %ecx /* pPrevFrameOS2 */
482 movl %ecx, 0(%eax) /* pPrev */
483
484 /* unwind OS/2 exception chain up to ours */
485 movl %fs, %ebx
486 pushl $Dos32TIB
487 popl %fs
488 pushl $0 /* PEXCEPTIONREPORTRECORD */
489 pushl $1f /* PVOID pTargetIP */
490 pushl 12(%ebp) /* PEXCEPTIONREGISTRATIONRECORD */
491 call _DosUnwindException /* _syscall, rtl, but stack is not restored! */
4921:
493 movl %ebx, %fs
494
495 /* restore the Win32 chain in our frame */
496 movl 12(%ebp), %eax
497 movl 60(%eax), %ecx /* pPrevFrameWin32 */
498 movl %ecx, 0(%eax) /* pPrev */
499
500 /* restore __try/__except context */
501 movl 12(%ebp), %eax
502 movl 24(%eax), %ebx
503 movl 28(%eax), %esi
504 movl 32(%eax), %edi
505 movl 36(%eax), %ebp
506 movl 40(%eax), %esp
507
508 /* jump to __except */
509 movl $2, 56(%eax) /* pFrame->state */
510 jmp *8(%eax) /* pFrame->pFilterCallback */
511
512__seh_handler_win32_Error:
513
514 addl $8, %esp
515 popl %ebp
516
517 movl $1, %eax /* ExceptionContinueSearch */
518
519__seh_handler_win32_Return:
520
521 popl %esi
522 popl %edi
523 popl %ebx
524
525 popl %ebp
526 ret
527
528/*
529 * extern "C"
530 * EXCEPTION_FRAME *__seh_get_prev_frame_win32(EXCEPTION_FRAME *pFrame)
531 *
532 * Returns the previous Win32 exception frame given an existing frame.
533 *
534 * If SEH is used, the pFrame->Prev expression will not always return a correct
535 * result: the SEH handler is installed on both the Win32 and OS/2 exception
536 * chains and it dynamically substitutes the Prev field in its frame with the
537 * Win32 or OS/2 tail depending on the source of the exception. This function
538 * takes this into account and always returns the correct pointer to the
539 * previous Win32 frame.
540 *
541 * NOTE: This is a heavily platform specific stuff. The code depends on the
542 * struct ___seh_EXCEPTION_FRAME layout so be very careful and keep both
543 * in sync!
544 *
545 * __cdecl: EAX/ECX/EDX are not preserved, result in EAX/EDX, caller cleans up
546 * the stack.
547 */
548
549___seh_get_prev_frame_win32:
550
551 /*
552 * 4(%esp) - pFrame
553 */
554
555 movl 4(%esp), %eax
556 /*leal ___seh_handler, %ecx*/
557 movl 4(%eax), %ecx /* pFrame->Handler */
558 cmpl $___seh_handler_win32, %ecx
559 jne ___seh_get_prev_frame_win32_Normal
560
561 movl 60(%eax), %eax /* pPrevFrameWin32 */
562 ret
563
564___seh_get_prev_frame_win32_Normal:
565
566 movl 0(%eax), %eax /* pFrame->Prev */
567 ret
568
Note: See TracBrowser for help on using the repository browser.