Implemented stripped down SEH functionality
[reactos.git] / ntoskrnl / rtl / i386 / seh.s
1 /* $Id$
2  *
3  * COPYRIGHT:         See COPYING in the top level directory
4  * PROJECT:           ReactOS kernel
5  * PURPOSE:           Runtime library exception support for IA-32
6  * FILE:              ntoskrnl/rtl/i386/seh.s
7  * PROGRAMER:         Casper S. Hornstrup (chorns@users.sourceforge.net)
8  * NOTES:             This file is shared with lib/msvcrt/except/seh.s.
9  *                    Please keep them in sync.
10  */
11
12 #define ExceptionContinueExecution      0
13 #define ExceptionContinueSearch         1
14 #define ExceptionNestedException        2
15 #define ExceptionCollidedUnwind         3
16
17 #define EXCEPTION_NONCONTINUABLE        0x01
18 #define EXCEPTION_UNWINDING                     0x02
19 #define EXCEPTION_EXIT_UNWIND           0x04
20 #define EXCEPTION_STACK_INVALID         0x08
21 #define EXCEPTION_NESTED_CALL           0x10
22 #define EXCEPTION_TARGET_UNWIND         0x20
23 #define EXCEPTION_COLLIDED_UNWIND       0x40
24
25 #define EXCEPTION_UNWIND_MODE \
26 (  EXCEPTION_UNWINDING \
27  | EXCEPTION_EXIT_UNWIND \
28  | EXCEPTION_TARGET_UNWIND \
29  | EXCEPTION_COLLIDED_UNWIND)
30
31 #define EREC_CODE               0x00
32 #define EREC_FLAGS              0x04
33 #define EREC_RECORD             0x08
34 #define EREC_ADDRESS    0x0C
35 #define EREC_NUMPARAMS  0x10
36 #define EREC_INFO               0x14
37
38 #define TRYLEVEL_NONE    -1
39 #define TRYLEVEL_INVALID -2
40
41 #define ER_STANDARDESP  -0x08
42 #define ER_EPOINTERS    -0x04
43 #define ER_PREVFRAME    0x00
44 #define ER_HANDLER              0x04
45 #define ER_SCOPETABLE   0x08
46 #define ER_TRYLEVEL             0x0C
47 #define ER_EBP                  0x10
48
49 #define ST_TRYLEVEL             0x00
50 #define ST_FILTER               0x04
51 #define ST_HANDLER              0x08
52
53 #define CONTEXT_EDI             0x9C
54 #define CONTEXT_EBX             0xA4
55 #define CONTEXT_EIP             0xB8
56
57 #define __local_unwind2 _local_unwind2  /* LIBCAPTIVE */
58 #define __except_handler3 _except_handler3      /* LIBCAPTIVE */
59 #define __global_unwind2 _global_unwind2        /* LIBCAPTIVE */
60
61 #if 0   /* LIBCAPTIVE */
62 .globl __local_unwind2
63 #endif /* LIBCAPTIVE */
64 .globl __except_handler3
65
66 #if 0   /* LIBCAPTIVE */
67
68 // EAX = value to print
69 _do_debug:
70         pushal
71         pushl   %eax
72         call    _MsvcrtDebug@4
73         popal
74         ret
75
76 #endif /* LIBCAPTIVE */
77
78 #define LU2_TRYLEVEL    0x08
79 #define LU2_REGFRAME    0x04
80
81 //
82 // void
83 // _local_unwind2(PEXCEPTION_REGISTRATION RegistrationFrame,LONG TryLevel)
84 //
85 // Parameters:
86 //   [EDX+08h] - LONG TryLevel
87 //   [EDX+04h] - PEXCEPTION_REGISTRATION RegistrationFrame
88 // Registers:
89 //   EBP - EBP of call frame we are unwinding
90 // Returns:
91 //   Nothing
92 // Notes:
93 //   Run all termination handlers for a call frame from the current
94 //   try-level up to (but not including) the given stop try-level.
95 #if 0
96 __local_unwind2:
97     // Setup our call frame so we can access parameters using EDX
98     //pushl    %ebp
99     movl     %esp, %edx
100
101     // FIXME: Setup an EXCEPTION_REGISTRATION entry to protect the
102     // unwinding in case something goes wrong
103
104 .lu2_next_scope:
105
106     // Keep a pointer to the exception registration in EBX
107     movl     LU2_REGFRAME(%edx), %ebx
108
109     // If we have reached the end of the chain or we're asked to stop here
110     // by the caller then exit
111     movl     ER_TRYLEVEL(%ebx), %eax
112
113     cmpl     $-1, %eax
114     je       .lu2_done
115
116     cmpl     LU2_TRYLEVEL(%edx), %eax
117     je       .lu2_done
118
119     // Keep a pointer to the scopetable in ESI
120     movl     ER_SCOPETABLE(%ebx), %esi
121
122     // Compute the offset of the entry in the scopetable that describes
123     // the scope that is to be unwound. Put the offset in EDI.
124     movl        ST_TRYLEVEL(%esi), %edi
125     lea     (%edi, %edi, 2), %edi
126     shll    $2, %edi
127     addl    %esi, %edi
128
129     // If this is not a termination handler then skip it
130     cmpl     $0, ST_FILTER(%edi)
131     jne      .lu2_next_scope
132
133     // Save the previous try-level in the exception registration structure
134     movl     ST_TRYLEVEL(%edi), %eax
135     movl     %eax, ER_TRYLEVEL(%ebx)
136
137     // Fetch the address of the termination handler
138     movl     ST_HANDLER(%edi), %eax
139
140     // Termination handlers may trash all registers so save the
141     // important ones and then call the handler
142     pushl    %edx
143     call         *%eax
144
145         // Get our base pointer back
146     popl     %edx
147
148     jmp      .lu2_next_scope
149
150 .lu2_done:
151
152     // FIXME: Tear down the EXCEPTION_REGISTRATION entry setup to protect
153     // the unwinding
154
155         //movl  %esi, %esp
156     //popl      %ebp
157     ret
158 #endif
159
160 #define EH3_DISPCONTEXT 0x14
161 #define EH3_CONTEXT             0x10
162 #define EH3_REGFRAME    0x0C
163 #define EH3_ERECORD             0x08
164
165 // Parameters:
166 //   [ESP+14h] - PVOID DispatcherContext
167 //   [ESP+10h] - PCONTEXT Context
168 //   [ESP+0Ch] - PEXCEPTION_REGISTRATION RegistrationFrame
169 //   [ESP+08h] - PEXCEPTION_RECORD ExceptionRecord
170 // Registers:
171 //   Unknown
172 // Returns:
173 //   EXCEPTION_DISPOSITION - How this handler handled the exception
174 // Notes:
175 //   Try to find an exception handler that will handle the exception.
176 //   Traverse the entries in the scopetable that is associated with the
177 //   exception registration passed as a parameter to this function.
178 //   If an exception handler that will handle the exception is found, it
179 //   is called and this function never returns
180 __except_handler3:
181     // Setup our call frame so we can access parameters using EBP
182     pushl    %ebp                               // Standard ESP in frame (considered part of EXCEPTION_REGISTRATION)
183     movl     %esp, %ebp
184     pushl    %ebx       /* LIBCAPTIVE */
185     pushl    %esi       /* LIBCAPTIVE */
186     pushl    %edi       /* LIBCAPTIVE */
187
188     // Don't trust the direction flag to be cleared
189     cld
190
191     // Keep a pointer to the exception registration in EBX
192     movl     EH3_REGFRAME(%ebp), %ebx
193
194     // Either we're called to handle an exception or we're called to unwind    
195     movl        EH3_ERECORD(%ebp), %eax
196     testl       $EXCEPTION_UNWIND_MODE, EREC_FLAGS(%eax)
197     jnz         .eh3_unwind
198
199     // Build an EXCEPTION_POINTERS structure on the stack and store it's
200     // address in the EXCEPTION_REGISTRATION structure
201     movl     EH3_CONTEXT(%ebp), %eax
202     pushl    %ebx                                               // Registration frame
203     pushl    %eax                                               // Context
204     movl     %esp, ER_EPOINTERS(%ebx)   // Pointer to EXCEPTION_REGISTRATION on the stack
205
206     // Keep current try-level in EDI
207     movl     ER_TRYLEVEL(%ebx), %edi
208
209     // Keep a pointer to the scopetable in ESI
210     movl     ER_SCOPETABLE(%ebx), %esi
211
212 .eh3_next_scope:
213
214     // If we have reached the end of the chain then exit
215     cmpl     $-1, %edi
216     je       .eh3_search
217
218     // Compute the offset of the entry in the scopetable and store
219     // the absolute address in EAX
220     lea     (%edi, %edi, 2), %eax
221     shll    $2, %eax
222     addl    %esi, %eax
223
224     // Fetch the address of the filter routine
225     movl     ST_FILTER(%eax), %eax
226
227     // If this is a termination handler then skip it
228     cmpl     $0, %eax
229     je       .eh3_continue
230
231     // Filter routines may trash all registers so save the important
232     // ones before restoring the call frame ebp and calling the handler
233     pushl       %ebp
234     pushl       %edi                            // Stop try-level
235     lea         ER_EBP(%ebx), %ebp
236     call        *%eax
237     popl        %edi                            // Stop try-level
238     popl        %ebp
239
240     // Reload EBX with registration frame address
241     movl        EH3_REGFRAME(%ebp), %ebx
242
243     // Be more flexible here by checking if the return value is less than
244     // zero, equal to zero, or larger than zero instead of the defined
245     // values:
246     //   -1 (EXCEPTION_CONTINUE_EXECUTION)
247     //    0 (EXCEPTION_CONTINUE_SEARCH)
248     //   +1 (EXCEPTION_EXECUTE_HANDLER)
249     orl      %eax, %eax
250     jz       .eh3_continue
251     js       .eh3_dismiss
252
253     // Filter returned: EXCEPTION_EXECUTE_HANDLER
254
255     // Ask the OS to perform global unwinding.
256     pushl       %edi            // Save stop try-level
257     pushl       %ebx            // Save registration frame address
258     pushl       %ebx            // Registration frame address
259     call        __global_unwind2
260     popl        %eax            // Remove parameter to __global_unwind2
261     popl        %ebx            // Restore registration frame address
262     popl        %edi            // Restore stop try-level
263
264     // Change the context structure so _except_finish is called in the
265     // correct context since we return ExceptionContinueExecution.
266 #if 0   /* LIBCAPTIVE */
267     movl     EH3_CONTEXT(%ebp), %eax
268     
269     movl     %edi, CONTEXT_EDI(%eax)            // Stop try-level
270     movl     %ebx, CONTEXT_EBX(%eax)            // Registration frame address
271     movl     $_except_finish, CONTEXT_EIP(%eax)
272
273     movl     $ExceptionContinueExecution, %eax
274     jmp      .eh3_return
275 #else /* !LIBCAPTIVE */
276     // pass %edi
277     // pass %ebx
278     jmp      _except_finish
279 #endif /* LIBCAPTIVE */
280
281     // Filter returned: EXCEPTION_CONTINUE_SEARCH
282 .eh3_continue:
283
284     // Reload ESI because the filter routine may have trashed it
285     movl     ER_SCOPETABLE(%ebx), %esi
286
287     // Go one try-level closer to the top
288     lea      (%edi, %edi, 2), %edi
289     shll     $2, %edi
290     addl     %esi, %edi
291     movl     ST_TRYLEVEL(%edi), %edi
292
293     jmp      .eh3_next_scope
294
295     // Filter returned: EXCEPTION_CONTINUE_EXECUTION
296     // Continue execution like nothing happened
297 .eh3_dismiss:
298     movl     $ExceptionContinueExecution, %eax
299     jmp      .eh3_return
300
301     // Tell the OS to search for another handler that will handle the exception
302 .eh3_search:
303
304     movl     $ExceptionContinueSearch, %eax
305     jmp      .eh3_return
306
307     // Perform local unwinding
308 .eh3_unwind:
309
310     testl    $EXCEPTION_TARGET_UNWIND, EREC_FLAGS(%eax)
311     movl     $ExceptionContinueSearch, %eax
312     jnz      .eh3_return
313
314         // Save some important registers
315     pushl       %ebp
316
317     lea          ER_EBP(%ebx), %ebp
318     pushl    $-1
319     pushl    %ebx
320
321 #if 0   /* LIBCAPTIVE */
322     call     __local_unwind2
323 #else /* !LIBCAPTIVE */
324     .extern  _local_unwind2_addr
325     movl     _local_unwind2_addr,%eax
326                 call     *%eax
327 #endif /* LIBCAPTIVE */
328
329     addl     $8, %esp
330
331         // Restore some important registers
332     popl     %ebp
333
334     movl     $ExceptionContinueSearch, %eax
335
336     // Get me out of here
337 .eh3_return:
338
339     lea     -3*4(%ebp),%esp     /* LIBCAPTIVE */
340     popl    %edi        /* LIBCAPTIVE */
341     popl    %esi        /* LIBCAPTIVE */
342     popl    %ebx        /* LIBCAPTIVE */
343 #if 0   /* LIBCAPTIVE */
344     movl        %ebp, %esp
345 #endif /* LIBCAPTIVE */
346     popl        %ebp
347     ret
348
349 // Parameters:
350 //   None
351 // Registers:
352 //   EBX - Pointer to exception registration structure
353 //   EDI - Stop try-level
354 // Returns:
355 //   -
356 // Notes:
357 //   -
358 _except_finish:
359
360     // Setup EBP for the exception handler. By doing this the exception
361     // handler can access local variables as normal
362     lea         ER_EBP(%ebx), %ebp
363
364         // Save some important registers
365     pushl       %ebp
366     pushl       %ebx
367     pushl       %edi
368
369     // Stop try-level
370     pushl       %edi
371
372     // Pointer to exception registration structure
373     pushl    %ebx
374
375 #if 0   /* LIBCAPTIVE */
376     call     __local_unwind2
377 #else /* !LIBCAPTIVE */
378     .extern  _local_unwind2_addr
379     movl     _local_unwind2_addr,%eax
380                 call     *%eax
381 #endif /* LIBCAPTIVE */
382
383     addl     $8, %esp
384
385         // Restore some important registers
386     popl     %edi
387     popl     %ebx
388     popl     %ebp
389
390     // Keep a pointer to the scopetable in ESI
391     movl     ER_SCOPETABLE(%ebx), %esi
392
393     // Compute the offset of the entry in the scopetable and store
394     // the absolute address in EDI
395     lea     (%edi, %edi, 2), %edi
396     shll    $2, %edi
397     addl    %esi, %edi
398
399     // Set the current try-level to the previous try-level and call
400     // the exception handler
401     movl     ST_TRYLEVEL(%edi), %eax
402     movl     %eax, ER_TRYLEVEL(%ebx)
403     movl     ST_HANDLER(%edi), %eax
404
405     call    *%eax
406
407     // We should never get here
408                 hlt
409     ret