xref: /reactos/sdk/lib/pseh/i386/seh.s (revision 1734f297)
1/*
2 * COPYRIGHT:       See COPYING in the top level directory
3 * PROJECT:         ReactOS CRT
4 * FILE:            lib/crt/misc/i386/seh.S
5 * PURPOSE:         SEH Support for the CRT
6 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9/* INCLUDES ******************************************************************/
10
11#include <asm.inc>
12#include <ks386.inc>
13
14#define DISPOSITION_DISMISS         0
15#define DISPOSITION_CONTINUE_SEARCH 1
16#define DISPOSITION_COLLIDED_UNWIND 3
17
18#define EXCEPTION_EXIT_UNWIND 4
19#define EXCEPTION_UNWINDING 2
20
21/* See seh_prolog.s */
22SEH_FRAME_NewEsp         =  0 /* 0x00 */
23SEH_FRAME_unused         =  4 /* 0x04 */
24SEH_FRAME_PreviousRecord =  8 /* 0x08 */
25SEH_FRAME_Handler        = 12 /* 0x0c */
26SEH_FRAME_SEHTable       = 16 /* 0x10 */
27SEH_FRAME_Disable        = 20 /* 0x14 */
28SEH_FRAME_OriginalEbp    = 24 /* 0x18 */
29SEH_FRAME_Size           = 28 /* 0x1c */
30
31
32EXTERN _RtlUnwind@16:PROC
33
34/* GLOBALS *******************************************************************/
35
36PUBLIC __global_unwind2
37PUBLIC __local_unwind2
38PUBLIC __abnormal_termination
39PUBLIC __except_handler2
40PUBLIC __except_handler3
41
42/* FUNCTIONS *****************************************************************/
43
44.code
45_unwind_handler:
46
47    /* Check if we were unwinding and continue search if not */
48    mov ecx, [esp+4]
49    test dword ptr [ecx+4], EXCEPTION_EXIT_UNWIND + EXCEPTION_UNWINDING
50    mov eax, DISPOSITION_CONTINUE_SEARCH
51    jz unwind_handler_return
52
53    /* We have a collision, do a local unwind */
54    mov eax, [esp+20]
55    push ebp
56    mov ebp, [eax+16]
57    mov edx, [eax+40]
58    push edx
59    mov edx, [eax+36]
60    push edx
61    call __local_unwind2
62    add esp, 8
63    pop ebp
64
65    /* Set new try level */
66    mov eax, [esp+8]
67    mov edx, [esp+16]
68    mov [edx], eax
69
70    /* Return collided unwind */
71    mov eax, DISPOSITION_COLLIDED_UNWIND
72
73unwind_handler_return:
74    ret
75
76
77__global_unwind2:
78
79    /* Create stack and save all registers */
80    push ebp
81    mov ebp, esp
82    push ebx
83    push esi
84    push edi
85    push ebp
86
87    /* Call unwind */
88    push 0
89    push 0
90    push glu_return
91    push [ebp+8]
92    call _RtlUnwind@16
93
94glu_return:
95    /* Restore registers and return */
96    pop ebp
97    pop edi
98    pop esi
99    pop ebx
100    mov esp, ebp
101    pop ebp
102    ret
103
104
105__abnormal_termination:
106
107    /* Assume false */
108    xor eax, eax
109
110    /* Check if the handler is the unwind handler */
111    mov ecx, fs:0
112    cmp dword ptr [ecx+4], offset _unwind_handler
113    jne short ab_return
114
115    /* Get the try level */
116    mov edx, [ecx+12]
117    mov edx, [edx+12]
118
119    /* Compare it */
120    cmp [ecx+8], edx
121    jne ab_return
122
123    /* Return true */
124    mov eax, 1
125
126    /* Return */
127ab_return:
128    ret
129
130
131__local_unwind2:
132
133    /* Save volatiles */
134    push ebx
135    push esi
136    push edi
137
138    /* Get the exception registration */
139    mov eax, [esp+16]
140
141    /* Setup SEH to protect the unwind */
142    push ebp
143    push eax
144    push -2
145    push offset _unwind_handler
146    push fs:0
147    mov fs:0, esp
148
149unwind_loop:
150    /* Get the exception registration and try level */
151    mov eax, [esp+36]
152    mov ebx, [eax+8]
153    mov esi, [eax+12]
154
155    /* Validate the unwind */
156    cmp esi, -1
157    je unwind_return
158    cmp dword ptr [esp+40], -1
159    je unwind_ok
160    cmp esi, [esp+40]
161    jbe unwind_return
162
163unwind_ok:
164    /* Get the new enclosing level and save it */
165    lea esi, [esi+esi*2]
166    mov ecx, [ebx+esi*4]
167    mov [esp+8], ecx
168    mov [eax+12], ecx
169
170    /* Check the filter type */
171    cmp dword ptr [ebx+esi*4+4], 0
172    jnz __NLG_Return2
173
174    /* FIXME: NLG Notification */
175
176    /* Call the handler */
177    call dword ptr [ebx+esi*4+8]
178
179__NLG_Return2:
180    /* Unwind again */
181    jmp unwind_loop
182
183unwind_return:
184    /* Cleanup SEH */
185    pop fs:0
186    add esp, 16
187    pop edi
188    pop esi
189    pop ebx
190    ret
191
192
193__except_handler2:
194
195    /* Setup stack and save volatiles */
196    push ebp
197    mov ebp, esp
198    sub esp, 8
199    push ebx
200    push esi
201    push edi
202    push ebp
203
204    /* Clear direction flag */
205    cld
206
207    /* Get exception registration and record */
208    mov ebx, [ebp+12]
209    mov eax, [ebp+8]
210
211    /* Check if this is an unwind */
212    test dword ptr [eax+4], EXCEPTION_EXIT_UNWIND + EXCEPTION_UNWINDING
213    jnz except_unwind2
214
215    /* Save exception pointers structure */
216    mov [ebp-8], eax
217    mov eax, [ebp+16]
218    mov [ebp-4], eax
219    lea eax, [ebp-8]
220    mov [ebx+20], eax
221
222    /* Get the try level and scope table */
223    mov esi, [ebx+12]
224    mov edi, [ebx+8]
225
226except_loop2:
227    /* Validate try level */
228    cmp esi, -1
229    je except_search2
230
231    /* Check if this is the termination handler */
232    lea ecx, [esi+esi*2]
233    cmp dword ptr [edi+ecx*4+4], 0
234    jz except_continue2
235
236    /* Save registers and call filter, then restore them */
237    push esi
238    push ebp
239    mov ebp, [ebx+16]
240    call dword ptr [edi+ecx*4+4]
241    pop ebp
242    pop esi
243
244    /* Restore ebx and check the result */
245    mov ebx, [ebp+12]
246    or eax, eax
247    jz except_continue2
248    js except_dismiss2
249
250    /* So this is an accept, call the termination handlers */
251    mov edi, [ebx+8]
252    push ebx
253    call __global_unwind2
254    add esp, 4
255
256    /* Restore ebp */
257    mov ebp, [ebx+16]
258
259    /* Do local unwind */
260    push esi
261    push ebx
262    call __local_unwind2
263    add esp, 8
264
265    /* Set new try level */
266    lea ecx, [esi+esi*2]
267    mov eax, [edi+ecx*4]
268    mov [ebx+12], eax
269
270    /* Call except handler */
271    call dword ptr [edi+ecx*4+8]
272
273except_continue2:
274    /* Reload try level and except again */
275    mov edi, [ebx+8]
276    lea ecx, [esi+esi*2]
277    mov esi, [edi+ecx*4]
278    jmp except_loop2
279
280except_dismiss2:
281    /* Dismiss it */
282    mov eax, DISPOSITION_DISMISS
283    jmp except_return2
284
285except_search2:
286    /* Continue searching */
287    mov eax, DISPOSITION_CONTINUE_SEARCH
288    jmp except_return2
289
290    /* Do local unwind */
291except_unwind2:
292    push ebp
293    mov ebp, [ebx+16]
294    push -1
295    push ebx
296    call __local_unwind2
297    add esp, 8
298
299    /* Retore EBP and set return disposition */
300    pop ebp
301    mov eax, DISPOSITION_CONTINUE_SEARCH
302
303except_return2:
304    /* Restore registers and stack */
305    pop ebp
306    pop edi
307    pop esi
308    pop ebx
309    mov esp, ebp
310    pop ebp
311    ret
312
313
314__except_handler3:
315PARAM_ExceptionRecord = 8
316PARAM_RegistrationFrame = 12
317PARAM_Context = 16
318    /* Setup stack and save volatiles */
319    push ebp
320    mov ebp, esp
321    sub esp, 8
322    push ebx
323    push esi
324    push edi
325    push ebp
326
327    /* Clear direction flag */
328    cld
329
330    /* Get exception registration and record */
331    mov ebx, [ebp+PARAM_RegistrationFrame]
332    mov eax, [ebp+PARAM_ExceptionRecord]
333
334    /* Check if this is an unwind */
335    test dword ptr [eax+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_EXIT_UNWIND + EXCEPTION_UNWINDING
336    jnz except_unwind3
337
338    /* Save exception pointers structure */
339    mov [ebp-8+EXCEPTION_POINTERS_EXCEPTION_RECORD], eax
340    mov eax, [ebp+PARAM_Context]
341    mov [ebp-8+EXCEPTION_POINTERS_CONTEXT_RECORD], eax
342    lea eax, [ebp-8]
343    mov [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_unused], eax
344
345    /* Get the try level and scope table */
346    mov esi, [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_Disable]
347    mov edi, [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_SEHTable]
348
349    /* FIXME: Validate the SEH exception */
350
351except_loop3:
352    /* Validate try level */
353    cmp esi, -1
354    je except_search3
355
356    /* Check if this is the termination handler */
357    lea ecx, [esi+esi*2]
358    mov eax, [edi+ecx*4+4]
359    test eax, eax
360    jz except_continue3
361
362    /* Save registers clear them all */
363    push esi
364    push ebp
365    lea ebp, [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_OriginalEbp]
366    xor ebx, ebx
367    xor ecx, ecx
368    xor edx, edx
369    xor esi, esi
370    xor edi, edi
371
372    /* Call the filter and restore our registers */
373    call eax
374    pop ebp
375    pop esi
376
377    /* Restore ebx and check the result */
378    mov ebx, [ebp+PARAM_RegistrationFrame]
379    test eax, eax
380    jz except_continue3
381    js except_dismiss3
382
383    /* So this is an accept, call the termination handlers */
384    mov edi, [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_SEHTable]
385    push ebx
386    call __global_unwind2
387    add esp, 4
388
389    /* Restore ebp */
390    lea ebp, [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_OriginalEbp]
391
392    /* Do local unwind */
393    push esi
394    push ebx
395    call __local_unwind2
396    add esp, 8
397
398    /* FIXME: Do NLG Notification */
399
400    /* Set new try level */
401    lea ecx, [esi+esi*2]
402    mov eax, [edi+ecx*4]
403    mov [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_Disable], eax
404
405    /* Clear registers and call except handler */
406    mov eax, [edi+ecx*4+8]
407    xor ebx, ebx
408    xor ecx, ecx
409    xor edx, edx
410    xor esi, esi
411    xor edi, edi
412    call eax
413
414except_continue3:
415    /* Reload try level and except again */
416    mov edi, [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_SEHTable]
417    lea ecx, [esi+esi*2]
418    mov esi, [edi+ecx*4]
419    jmp except_loop3
420
421except_dismiss3:
422    /* Dismiss it */
423    mov eax, DISPOSITION_DISMISS
424    jmp except_return3
425
426    /* Do local unwind */
427except_unwind3:
428    push ebp
429    lea ebp, [ebx-SEH_FRAME_PreviousRecord+SEH_FRAME_OriginalEbp]
430    push -1
431    push ebx
432    call __local_unwind2
433    add esp, 8
434
435    /* Restore EBP */
436    pop ebp
437
438except_search3:
439    /* Continue searching */
440    mov eax, DISPOSITION_CONTINUE_SEARCH
441
442except_return3:
443    /* Restore registers and stack */
444    pop ebp
445    pop edi
446    pop esi
447    pop ebx
448    mov esp, ebp
449    pop ebp
450    ret
451
452END
453