xref: /reactos/sdk/lib/rtl/i386/except_asm.s (revision c2c66aff)
1/*
2 * COPYRIGHT:       See COPYING in the top level directory
3 * PROJECT:         ReactOS Runtime Library (RTL)
4 * FILE:            lib/rtl/i386/except_asm.s
5 * PURPOSE:         User-mode exception support for IA-32
6 * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
7 *                  Stefan Ginsberg (stefan.ginsberg@reactos.org)
8 */
9
10/* INCLUDES ******************************************************************/
11
12#include <asm.inc>
13#include <ks386.inc>
14
15EXTERN _RtlpCheckForActiveDebugger@0:PROC
16EXTERN _RtlDispatchException@8:PROC
17EXTERN _ZwContinue@8:PROC
18EXTERN _ZwRaiseException@12:PROC
19
20#define ExceptionContinueSearch     1
21#define ExceptionNestedException    2
22#define ExceptionCollidedUnwind     3
23
24/* FUNCTIONS *****************************************************************/
25
26.code
27
28PUBLIC _RtlpGetExceptionList@0
29_RtlpGetExceptionList@0:
30
31    /* Return the exception list */
32    mov eax, fs:[TEB_EXCEPTION_LIST]
33    ret
34
35
36PUBLIC _RtlpSetExceptionList@4
37_RtlpSetExceptionList@4:
38
39    /* Get the new list */
40    mov ecx, [esp+4]
41    mov ecx, [ecx]
42
43    /* Write it */
44    mov fs:[TEB_EXCEPTION_LIST], ecx
45
46    /* Return */
47    ret 4
48
49
50PUBLIC _RtlCaptureContext@4
51_RtlCaptureContext@4:
52
53    /* Preserve EBX and put the context in it */
54    push ebx
55    mov ebx, [esp+8]
56
57    /* Save the basic register context */
58    mov [ebx+CONTEXT_EAX], eax
59    mov [ebx+CONTEXT_ECX], ecx
60    mov [ebx+CONTEXT_EDX], edx
61    mov eax, [esp]
62    mov [ebx+CONTEXT_EBX], eax
63    mov [ebx+CONTEXT_ESI], esi
64    mov [ebx+CONTEXT_EDI], edi
65
66    /* Capture the other regs */
67    jmp CaptureRest
68
69
70PUBLIC _RtlpCaptureContext@4
71_RtlpCaptureContext@4:
72
73    /* Preserve EBX and put the context in it */
74    push ebx
75    mov ebx, [esp+8]
76
77    /* Clear the basic register context */
78    mov dword ptr [ebx+CONTEXT_EAX], 0
79    mov dword ptr [ebx+CONTEXT_ECX], 0
80    mov dword ptr [ebx+CONTEXT_EDX], 0
81    mov dword ptr [ebx+CONTEXT_EBX], 0
82    mov dword ptr [ebx+CONTEXT_ESI], 0
83    mov dword ptr [ebx+CONTEXT_EDI], 0
84
85CaptureRest:
86    /* Capture the segment registers */
87    mov [ebx+CONTEXT_SEGCS], cs
88    mov [ebx+CONTEXT_SEGDS], ds
89    mov [ebx+CONTEXT_SEGES], es
90    mov [ebx+CONTEXT_SEGFS], fs
91    mov [ebx+CONTEXT_SEGGS], gs
92    mov [ebx+CONTEXT_SEGSS], ss
93
94    /* Capture flags */
95    pushfd
96    pop [ebx+CONTEXT_EFLAGS]
97
98    /* The return address should be in [ebp+4] */
99    mov eax, [ebp+4]
100    mov [ebx+CONTEXT_EIP], eax
101
102    /* Get EBP */
103    mov eax, [ebp+0]
104    mov [ebx+CONTEXT_EBP], eax
105
106    /* And get ESP */
107    lea eax, [ebp+8]
108    mov [ebx+CONTEXT_ESP], eax
109
110    /* Return to the caller */
111    pop ebx
112    ret 4
113
114
115PUBLIC _RtlpExecuteHandlerForException@20
116_RtlpExecuteHandlerForException@20:
117
118    /* Copy the routine in EDX */
119    mov edx, offset _RtlpExceptionProtector
120
121    /* Jump to common routine */
122    jmp _RtlpExecuteHandler@20
123
124
125PUBLIC _RtlpExecuteHandlerForUnwind@20
126_RtlpExecuteHandlerForUnwind@20:
127    /* Copy the routine in EDX */
128    mov edx, offset _RtlpUnwindProtector
129
130
131_RtlpExecuteHandler@20:
132
133    /* Save non-volatile */
134    push ebx
135    push esi
136    push edi
137
138    /* Clear registers */
139    xor eax, eax
140    xor ebx, ebx
141    xor esi, esi
142    xor edi, edi
143
144    /* Call the 2nd-stage executer */
145    push [esp+32]
146    push [esp+32]
147    push [esp+32]
148    push [esp+32]
149    push [esp+32]
150    call _RtlpExecuteHandler2@20
151
152    /* Restore non-volatile */
153    pop edi
154    pop esi
155    pop ebx
156    ret 20
157
158
159PUBLIC _RtlpExecuteHandler2@20
160_RtlpExecuteHandler2@20:
161
162    /* Set up stack frame */
163    push ebp
164    mov ebp, esp
165
166    /* Save the Frame */
167    push [ebp+12]
168
169    /* Push handler address */
170    push edx
171
172    /* Push the exception list */
173    push [fs:TEB_EXCEPTION_LIST]
174
175    /* Link us to it */
176    mov [fs:TEB_EXCEPTION_LIST], esp
177
178    /* Call the handler */
179    push [ebp+20]
180    push [ebp+16]
181    push [ebp+12]
182    push [ebp+8]
183    mov ecx, [ebp+24]
184    call ecx
185
186    /* Unlink us */
187    mov esp, [fs:TEB_EXCEPTION_LIST]
188
189    /* Restore it */
190    pop [fs:TEB_EXCEPTION_LIST]
191
192    /* Undo stack frame and return */
193    mov esp, ebp
194    pop ebp
195    ret 20
196
197
198_RtlpExceptionProtector:
199
200    /* Assume we'll continue */
201    mov eax, ExceptionContinueSearch
202
203    /* Put the exception record in ECX and check the Flags */
204    mov ecx, [esp+4]
205    test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND
206    jnz return
207
208    /* Save the frame in ECX and Context in EDX */
209    mov ecx, [esp+8]
210    mov edx, [esp+16]
211
212    /* Get the nested frame */
213    mov eax, [ecx+8]
214
215    /* Set it as the dispatcher context */
216    mov [edx], eax
217
218    /* Return nested exception */
219    mov eax, ExceptionNestedException
220
221return:
222    ret 16
223
224
225_RtlpUnwindProtector:
226
227    /* Assume we'll continue */
228    mov eax, ExceptionContinueSearch
229
230    /* Put the exception record in ECX and check the Flags */
231    mov ecx, [esp+4]
232    test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND
233    jz .return
234
235    /* Save the frame in ECX and Context in EDX */
236    mov ecx, [esp+8]
237    mov edx, [esp+16]
238
239    /* Get the nested frame */
240    mov eax, [ecx+8]
241
242    /* Set it as the dispatcher context */
243    mov [edx], eax
244
245    /* Return collided unwind */
246    mov eax, ExceptionCollidedUnwind
247
248.return:
249    ret 16
250
251
252PUBLIC _RtlRaiseException@4
253_RtlRaiseException@4:
254
255    /* Set up stack frame */
256    push ebp
257    mov ebp, esp
258
259    /*
260     * Save the context while preserving everything but ESP and EBP.
261     * This is vital because the caller will be restored with this context
262     * in case the execution is continued, which means we must not clobber
263     * the non-volatiles. We preserve the volatiles too because the context
264     * could get passed to a debugger.
265     */
266    lea esp, [esp-CONTEXT_FRAME_LENGTH]
267    push esp
268    call _RtlCaptureContext@4
269
270    /* Adjust ESP to account for the argument that was passed */
271    add dword ptr [esp+CONTEXT_ESP], 4
272
273    /* Save the exception address */
274    mov edx, [ebp+4]
275    mov eax, [ebp+8]
276    mov [eax+EXCEPTION_RECORD_EXCEPTION_ADDRESS], edx
277
278    /* Write the context flag */
279    mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL
280
281    /* Check if user mode debugger is active */
282    call _RtlpCheckForActiveDebugger@0
283    test al, al
284    jnz DebuggerActive1
285
286    /* Dispatch the exception */
287    push esp
288    push [ebp+8]
289    call _RtlDispatchException@8
290    test al, al
291    jz RaiseException
292
293    /* Continue, go back to previous context */
294    mov ecx, esp
295    push 0
296    push ecx
297    call  _ZwContinue@8
298    jmp RaiseStatus1
299
300DebuggerActive1:
301
302    /* Raise an exception immediately */
303    mov ecx, esp
304    push 1
305    push ecx
306    push [ebp+8]
307    call _ZwRaiseException@12
308    jmp RaiseStatus1
309
310RaiseException:
311
312    /* Raise the exception */
313    mov ecx, esp
314    push 0
315    push ecx
316    push [ebp+8]
317    call _ZwRaiseException@12
318
319RaiseStatus1:
320
321    /* If we returned, raise a status */
322    push eax
323    call _RtlRaiseStatus@4
324
325
326PUBLIC _RtlRaiseStatus@4
327_RtlRaiseStatus@4:
328
329    /* Set up stack frame */
330    push ebp
331    mov ebp, esp
332
333    /*
334     * Save the context while preserving everything but ESP and EBP.
335     * This is vital because the caller will be restored with this context
336     * in case the execution is continued, which means we must not clobber
337     * the non-volatiles. We preserve the volatiles too because the context
338     * could get passed to a debugger.
339     */
340    lea esp, [esp-CONTEXT_FRAME_LENGTH-EXCEPTION_RECORD_LENGTH]
341    push esp
342    call _RtlCaptureContext@4
343
344    /* Adjust ESP to account for the argument that was passed */
345    add dword ptr [esp+CONTEXT_ESP], 4
346
347    /* Set up the exception record */
348    lea ecx, [esp+CONTEXT_FRAME_LENGTH]
349    mov eax, [ebp+8]
350    mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_CODE], eax
351    mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_NONCONTINUABLE
352    and dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_RECORD], 0
353    mov eax, [ebp+4]
354    mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_ADDRESS], eax
355    and dword ptr [ecx+EXCEPTION_RECORD_NUMBER_PARAMETERS], 0
356
357    /* Write the context flag */
358    mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL
359
360    /* Check if user mode debugger is active */
361    call _RtlpCheckForActiveDebugger@0
362
363    /* Restore ECX and jump if debugger is active */
364    lea ecx, [esp+CONTEXT_FRAME_LENGTH]
365    test al, al
366    jnz DebuggerActive2
367
368    /* Dispatch the exception */
369    push esp
370    push ecx
371    call _RtlDispatchException@8
372
373    /* Raise exception if we got here */
374    lea ecx, [esp+CONTEXT_FRAME_LENGTH]
375    mov edx, esp
376    push 0
377    push edx
378    push ecx
379    call _ZwRaiseException@12
380    jmp RaiseStatus2
381
382DebuggerActive2:
383
384    /* Raise an exception immediately */
385    mov edx, esp
386    push 1
387    push edx
388    push ecx
389    call _ZwRaiseException@12
390
391RaiseStatus2:
392
393    /* If we returned, raise a status */
394    push eax
395    call _RtlRaiseStatus@4
396
397END
398