03ccd6b3ba68a265ebce5b74028288c2af8af1e4
[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 .globl __local_unwind2
58 .globl __except_handler3
59
60 // EAX = value to print
61 _do_debug:
62         pushal
63         pushl   %eax
64         call    _MsvcrtDebug@4
65         popal
66         ret
67
68 #define LU2_TRYLEVEL    0x08
69 #define LU2_REGFRAME    0x04
70
71 //
72 // void
73 // _local_unwind2(PEXCEPTION_REGISTRATION RegistrationFrame,
74 //                            LONG TryLevel)
75 //
76 // Parameters:
77 //   [EDX+08h] - PEXCEPTION_REGISTRATION RegistrationFrame
78 //   [EDX+04h] - LONG TryLevel
79 // Registers:
80 //   EBP - EBP of call frame we are unwinding
81 // Returns:
82 //   Nothing
83 // Notes:
84 //   Run all termination handlers for a call frame from the current
85 //   try-level up to (but not including) the given stop try-level.
86 __local_unwind2:
87     // Setup our call frame so we can access parameters using EDX
88     //pushl    %ebp
89     movl     %esp, %edx
90
91     // FIXME: Setup an EXCEPTION_REGISTRATION entry to protect the
92     // unwinding in case something goes wrong
93
94 .lu2_next_scope:
95
96     // Keep a pointer to the exception registration in EBX
97     movl     LU2_REGFRAME(%edx), %ebx
98
99     // If we have reached the end of the chain or we're asked to stop here
100     // by the caller then exit
101     movl     ER_TRYLEVEL(%ebx), %eax
102
103     cmpl     $-1, %eax
104     je       .lu2_done
105
106     cmpl     LU2_TRYLEVEL(%edx), %eax
107     je       .lu2_done
108
109     // Keep a pointer to the scopetable in ESI
110     movl     ER_SCOPETABLE(%ebx), %esi
111
112     // Compute the offset of the entry in the scopetable that describes
113     // the scope that is to be unwound. Put the offset in EDI.
114     movl        ST_TRYLEVEL(%esi), %edi
115     lea     (%edi, %edi, 2), %edi
116     shll    $2, %edi
117     addl    %esi, %edi
118
119     // If this is not a termination handler then skip it
120     cmpl     $0, ST_FILTER(%edi)
121     jne      .lu2_next_scope
122
123     // Save the previous try-level in the exception registration structure
124     movl     ST_TRYLEVEL(%edi), %eax
125     movl     %eax, ER_TRYLEVEL(%ebx)
126
127     // Fetch the address of the termination handler
128     movl     ST_HANDLER(%edi), %eax
129
130     // Termination handlers may trash all registers so save the
131     // important ones and then call the handler
132     pushl    %edx
133     call         *%eax
134
135         // Get our base pointer back
136     popl     %edx
137
138     jmp      .lu2_next_scope
139
140 .lu2_done:
141
142     // FIXME: Tear down the EXCEPTION_REGISTRATION entry setup to protect
143     // the unwinding
144
145         //movl  %esi, %esp
146     //popl      %ebp
147     ret
148
149 #define EH3_DISPCONTEXT 0x14
150 #define EH3_CONTEXT             0x10
151 #define EH3_REGFRAME    0x0C
152 #define EH3_ERECORD             0x08
153
154 // Parameters:
155 //   [ESP+14h] - PVOID DispatcherContext
156 //   [ESP+10h] - PCONTEXT Context
157 //   [ESP+0Ch] - PEXCEPTION_REGISTRATION RegistrationFrame
158 //   [ESP+08h] - PEXCEPTION_RECORD ExceptionRecord
159 // Registers:
160 //   Unknown
161 // Returns:
162 //   EXCEPTION_DISPOSITION - How this handler handled the exception
163 // Notes:
164 //   Try to find an exception handler that will handle the exception.
165 //   Traverse the entries in the scopetable that is associated with the
166 //   exception registration passed as a parameter to this function.
167 //   If an exception handler that will handle the exception is found, it
168 //   is called and this function never returns
169 __except_handler3:
170     // Setup our call frame so we can access parameters using EBP
171     pushl    %ebp                               // Standard ESP in frame (considered part of EXCEPTION_REGISTRATION)
172     movl     %esp, %ebp
173
174     // Don't trust the direction flag to be cleared
175     cld
176
177     // Either we're called to handle an exception or we're called to unwind    
178     movl        EH3_ERECORD(%ebp), %eax
179     testl       $EXCEPTION_UNWIND_MODE, EREC_FLAGS(%eax)
180     jnz         .eh3_unwind
181
182     // Keep a pointer to the exception registration in EBX
183     movl     EH3_REGFRAME(%ebp), %ebx
184
185     // Build an EXCEPTION_POINTERS structure on the stack and store it's
186     // address in the EXCEPTION_REGISTRATION structure
187     movl     EH3_CONTEXT(%esp), %eax
188     pushl    %ebx                                               // Registration frame
189     pushl    %eax                                               // Context
190     movl     %esp, ER_EPOINTERS(%ebx)   // Pointer to EXCEPTION_REGISTRATION on the stack
191
192     // Keep current try-level in EDI
193     movl     ER_TRYLEVEL(%ebx), %edi
194
195     // Keep a pointer to the scopetable in ESI
196     movl     ER_SCOPETABLE(%ebx), %esi
197
198 .eh3_next_scope:
199
200     // If we have reached the end of the chain then exit
201     cmpl     $-1, %edi
202     je       .eh3_search
203
204     // Compute the offset of the entry in the scopetable and store
205     // the absolute address in EAX
206     lea     (%edi, %edi, 2), %eax
207     shll    $2, %eax
208     addl    %esi, %eax
209
210     // Fetch the address of the filter routine
211     movl     ST_FILTER(%eax), %eax
212
213     // If this is a termination handler then skip it
214     cmpl     $0, %eax
215     je       .eh3_continue
216
217     // Filter routines may trash all registers so save the important
218     // ones before restoring the call frame ebp and calling the handler
219     pushl       %ebp
220     pushl       %edi                            // Stop try-level
221     lea         ER_EBP(%ebx), %ebp
222     call        *%eax
223     popl        %edi                            // Stop try-level
224     popl        %ebp
225
226     // Reload EBX with registration frame address
227     movl        EH3_REGFRAME(%ebp), %ebx
228
229     // Be more flexible here by checking if the return value is less than
230     // zero, equal to zero, or larger than zero instead of the defined
231     // values:
232     //   -1 (EXCEPTION_CONTINUE_EXECUTION)
233     //    0 (EXCEPTION_CONTINUE_SEARCH)
234     //   +1 (EXCEPTION_EXECUTE_HANDLER)
235     orl      %eax, %eax
236     jz       .eh3_continue
237     js       .eh3_dismiss
238
239     // Filter returned: EXCEPTION_EXECUTE_HANDLER
240
241     // Ask the OS to perform global unwinding.
242     pushl       %edi            // Save stop try-level
243     pushl       %ebx            // Save registration frame address
244     pushl       %ebx            // Registration frame address
245     call        __global_unwind2
246     popl        %eax            // Remove parameter to __global_unwind2
247     popl        %ebx            // Restore registration frame address
248     popl        %edi            // Restore stop try-level
249
250     // Change the context structure so _except_finish is called in the
251     // correct context since we return ExceptionContinueExecution.
252     movl     EH3_CONTEXT(%ebp), %eax
253     
254     movl     %edi, CONTEXT_EDI(%eax)            // Stop try-level
255     movl     %ebx, CONTEXT_EBX(%eax)            // Registration frame address
256     movl     $_except_finish, CONTEXT_EIP(%eax)
257
258     movl     $ExceptionContinueExecution, %eax
259     jmp      .eh3_return
260
261     // Filter returned: EXCEPTION_CONTINUE_SEARCH
262 .eh3_continue:
263
264     // Reload ESI because the filter routine may have trashed it
265     movl     ER_SCOPETABLE(%ebx), %esi
266
267     // Go one try-level closer to the top
268     lea      (%edi, %edi, 2), %edi
269     shll     $2, %edi
270     addl     %esi, %edi
271     movl     ST_TRYLEVEL(%edi), %edi
272
273     jmp      .eh3_next_scope
274
275     // Filter returned: EXCEPTION_CONTINUE_EXECUTION
276     // Continue execution like nothing happened
277 .eh3_dismiss:
278     movl     $ExceptionContinueExecution, %eax
279     jmp      .eh3_return
280
281     // Tell the OS to search for another handler that will handle the exception
282 .eh3_search:
283
284     movl     $ExceptionContinueSearch, %eax
285     jmp      .eh3_return
286
287     // Perform local unwinding
288 .eh3_unwind:
289
290     movl     $ExceptionContinueSearch, %eax
291     testl    $EXCEPTION_TARGET_UNWIND, EREC_FLAGS(%eax)
292     jnz      .eh3_return
293
294         // Save some important registers
295     pushl       %ebp
296
297     lea          ER_EBP(%ebx), %ebp
298     pushl    $-1
299     pushl    %ebx
300     call     __local_unwind2
301     addl     $8, %esp
302
303         // Restore some important registers
304     popl     %ebp
305
306     movl     $ExceptionContinueSearch, %eax
307
308     // Get me out of here
309 .eh3_return:
310
311         movl    %ebp, %esp
312     popl        %ebp
313     ret
314
315 // Parameters:
316 //   None
317 // Registers:
318 //   EBX - Pointer to exception registration structure
319 //   EDI - Stop try-level
320 // Returns:
321 //   -
322 // Notes:
323 //   -
324 _except_finish:
325
326     // Setup EBP for the exception handler. By doing this the exception
327     // handler can access local variables as normal
328     lea         ER_EBP(%ebx), %ebp
329
330         // Save some important registers
331     pushl       %ebp
332     pushl       %ebx
333     pushl       %edi
334
335     // Stop try-level
336     pushl       %edi
337
338     // Pointer to exception registration structure
339     pushl    %ebx
340     call     __local_unwind2
341     addl     $8, %esp
342
343         // Restore some important registers
344     popl     %edi
345     popl     %ebx
346     popl     %ebp
347
348     // Keep a pointer to the scopetable in ESI
349     movl     ER_SCOPETABLE(%ebx), %esi
350
351     // Compute the offset of the entry in the scopetable and store
352     // the absolute address in EDI
353     lea     (%edi, %edi, 2), %edi
354     shll    $2, %edi
355     addl    %esi, %edi
356
357     // Set the current try-level to the previous try-level and call
358     // the exception handler
359     movl     ST_TRYLEVEL(%edi), %eax
360     movl     %eax, ER_TRYLEVEL(%ebx)
361     movl     ST_HANDLER(%edi), %eax
362
363     call    *%eax
364
365     // We should never get here
366     ret