[PSEH]
[reactos.git] / reactos / sdk / lib / pseh / i386 / seh.s
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 */
22 SEH_FRAME_NewEsp = 0 /* 0x00 */
23 SEH_FRAME_unused = 4 /* 0x04 */
24 SEH_FRAME_PreviousRecord = 8 /* 0x08 */
25 SEH_FRAME_Handler = 12 /* 0x0c */
26 SEH_FRAME_SEHTable = 16 /* 0x10 */
27 SEH_FRAME_Disable = 20 /* 0x14 */
28 SEH_FRAME_OriginalEbp = 24 /* 0x18 */
29 SEH_FRAME_Size = 28 /* 0x1c */
30
31
32 EXTERN _RtlUnwind@16:PROC
33
34 /* GLOBALS *******************************************************************/
35
36 PUBLIC __global_unwind2
37 PUBLIC __local_unwind2
38 PUBLIC __abnormal_termination
39 PUBLIC __except_handler2
40 PUBLIC __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
73 unwind_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
94 glu_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 */
127 ab_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
149 unwind_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
163 unwind_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
183 unwind_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
226 except_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
273 except_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
280 except_dismiss2:
281 /* Dismiss it */
282 mov eax, DISPOSITION_DISMISS
283 jmp except_return2
284
285 except_search2:
286 /* Continue searching */
287 mov eax, DISPOSITION_CONTINUE_SEARCH
288 jmp except_return2
289
290 /* Do local unwind */
291 except_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
303 except_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:
315 PARAM_ExceptionRecord = 8
316 PARAM_RegistrationFrame = 12
317 PARAM_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
351 except_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
414 except_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
421 except_dismiss3:
422 /* Dismiss it */
423 mov eax, DISPOSITION_DISMISS
424 jmp except_return3
425
426 /* Do local unwind */
427 except_unwind3:
428 push ebp
429 mov 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
438 except_search3:
439 /* Continue searching */
440 mov eax, DISPOSITION_CONTINUE_SEARCH
441
442 except_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
452 END