xref: /reactos/sdk/lib/rtl/i386/except_asm.s (revision 3051eb0e)
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.PROC _RtlpExecuteHandler@20
132    FPO 0, 0, 0, 0, 0, FRAME_FPO
133
134    /* Save non-volatile */
135    push ebx
136    push esi
137    push edi
138
139    /* Clear registers */
140    xor eax, eax
141    xor ebx, ebx
142    xor esi, esi
143    xor edi, edi
144
145    /* Call the 2nd-stage executer */
146    push [esp+32]
147    push [esp+32]
148    push [esp+32]
149    push [esp+32]
150    push [esp+32]
151    call _RtlpExecuteHandler2@20
152
153    /* Restore non-volatile */
154    pop edi
155    pop esi
156    pop ebx
157    ret 20
158
159.ENDP
160
161PUBLIC _RtlpExecuteHandler2@20
162_RtlpExecuteHandler2@20:
163
164    /* Set up stack frame */
165    push ebp
166    mov ebp, esp
167
168    /* Save the Frame */
169    push [ebp+12]
170
171    /* Push handler address */
172    push edx
173
174    /* Push the exception list */
175    push [fs:TEB_EXCEPTION_LIST]
176
177    /* Link us to it */
178    mov [fs:TEB_EXCEPTION_LIST], esp
179
180    /* Call the handler */
181    push [ebp+20]
182    push [ebp+16]
183    push [ebp+12]
184    push [ebp+8]
185    mov ecx, [ebp+24]
186    call ecx
187
188    /* Unlink us */
189    mov esp, [fs:TEB_EXCEPTION_LIST]
190
191    /* Restore it */
192    pop [fs:TEB_EXCEPTION_LIST]
193
194    /* Undo stack frame and return */
195    mov esp, ebp
196    pop ebp
197    ret 20
198
199
200_RtlpExceptionProtector:
201
202    /* Assume we'll continue */
203    mov eax, ExceptionContinueSearch
204
205    /* Put the exception record in ECX and check the Flags */
206    mov ecx, [esp+4]
207    test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND
208    jnz return
209
210    /* Save the frame in ECX and Context in EDX */
211    mov ecx, [esp+8]
212    mov edx, [esp+16]
213
214    /* Get the nested frame */
215    mov eax, [ecx+8]
216
217    /* Set it as the dispatcher context */
218    mov [edx], eax
219
220    /* Return nested exception */
221    mov eax, ExceptionNestedException
222
223return:
224    ret 16
225
226
227_RtlpUnwindProtector:
228
229    /* Assume we'll continue */
230    mov eax, ExceptionContinueSearch
231
232    /* Put the exception record in ECX and check the Flags */
233    mov ecx, [esp+4]
234    test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND
235    jz .return
236
237    /* Save the frame in ECX and Context in EDX */
238    mov ecx, [esp+8]
239    mov edx, [esp+16]
240
241    /* Get the nested frame */
242    mov eax, [ecx+8]
243
244    /* Set it as the dispatcher context */
245    mov [edx], eax
246
247    /* Return collided unwind */
248    mov eax, ExceptionCollidedUnwind
249
250.return:
251    ret 16
252
253
254PUBLIC _RtlRaiseException@4
255_RtlRaiseException@4:
256
257    /* Set up stack frame */
258    push ebp
259    mov ebp, esp
260
261    /*
262     * Save the context while preserving everything but ESP and EBP.
263     * This is vital because the caller will be restored with this context
264     * in case the execution is continued, which means we must not clobber
265     * the non-volatiles. We preserve the volatiles too because the context
266     * could get passed to a debugger.
267     */
268    lea esp, [esp-CONTEXT_FRAME_LENGTH]
269    push esp
270    call _RtlCaptureContext@4
271
272    /* Adjust ESP to account for the argument that was passed */
273    add dword ptr [esp+CONTEXT_ESP], 4
274
275    /* Save the exception address */
276    mov edx, [ebp+4]
277    mov eax, [ebp+8]
278    mov [eax+EXCEPTION_RECORD_EXCEPTION_ADDRESS], edx
279
280    /* Write the context flag */
281    mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL
282
283    /* Check if user mode debugger is active */
284    call _RtlpCheckForActiveDebugger@0
285    test al, al
286    jnz DebuggerActive1
287
288    /* Dispatch the exception */
289    push esp
290    push [ebp+8]
291    call _RtlDispatchException@8
292    test al, al
293    jz RaiseException
294
295    /* Continue, go back to previous context */
296    mov ecx, esp
297    push 0
298    push ecx
299    call  _ZwContinue@8
300    jmp RaiseStatus1
301
302DebuggerActive1:
303
304    /* Raise an exception immediately */
305    mov ecx, esp
306    push 1
307    push ecx
308    push [ebp+8]
309    call _ZwRaiseException@12
310    jmp RaiseStatus1
311
312RaiseException:
313
314    /* Raise the exception */
315    mov ecx, esp
316    push 0
317    push ecx
318    push [ebp+8]
319    call _ZwRaiseException@12
320
321RaiseStatus1:
322
323    /* If we returned, raise a status */
324    push eax
325    call _RtlRaiseStatus@4
326
327
328PUBLIC _RtlRaiseStatus@4
329_RtlRaiseStatus@4:
330
331    /* Set up stack frame */
332    push ebp
333    mov ebp, esp
334
335    /*
336     * Save the context while preserving everything but ESP and EBP.
337     * This is vital because the caller will be restored with this context
338     * in case the execution is continued, which means we must not clobber
339     * the non-volatiles. We preserve the volatiles too because the context
340     * could get passed to a debugger.
341     */
342    lea esp, [esp-CONTEXT_FRAME_LENGTH-EXCEPTION_RECORD_LENGTH]
343    push esp
344    call _RtlCaptureContext@4
345
346    /* Adjust ESP to account for the argument that was passed */
347    add dword ptr [esp+CONTEXT_ESP], 4
348
349    /* Set up the exception record */
350    lea ecx, [esp+CONTEXT_FRAME_LENGTH]
351    mov eax, [ebp+8]
352    mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_CODE], eax
353    mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS], EXCEPTION_NONCONTINUABLE
354    and dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_RECORD], 0
355    mov eax, [ebp+4]
356    mov dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_ADDRESS], eax
357    and dword ptr [ecx+EXCEPTION_RECORD_NUMBER_PARAMETERS], 0
358
359    /* Write the context flag */
360    mov dword ptr [esp+CONTEXT_FLAGS], CONTEXT_FULL
361
362    /* Check if user mode debugger is active */
363    call _RtlpCheckForActiveDebugger@0
364
365    /* Restore ECX and jump if debugger is active */
366    lea ecx, [esp+CONTEXT_FRAME_LENGTH]
367    test al, al
368    jnz DebuggerActive2
369
370    /* Dispatch the exception */
371    push esp
372    push ecx
373    call _RtlDispatchException@8
374
375    /* Raise exception if we got here */
376    lea ecx, [esp+CONTEXT_FRAME_LENGTH]
377    mov edx, esp
378    push 0
379    push edx
380    push ecx
381    call _ZwRaiseException@12
382    jmp RaiseStatus2
383
384DebuggerActive2:
385
386    /* Raise an exception immediately */
387    mov edx, esp
388    push 1
389    push edx
390    push ecx
391    call _ZwRaiseException@12
392
393RaiseStatus2:
394
395    /* If we returned, raise a status */
396    push eax
397    call _RtlRaiseStatus@4
398
399END
400