xref: /reactos/sdk/lib/rtl/i386/except_asm.s (revision 5c7ce447)
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 
15 EXTERN _RtlpCheckForActiveDebugger@0:PROC
16 EXTERN _RtlDispatchException@8:PROC
17 EXTERN _ZwContinue@8:PROC
18 EXTERN _ZwRaiseException@12:PROC
19 
20 #define ExceptionContinueSearch     1
21 #define ExceptionNestedException    2
22 #define ExceptionCollidedUnwind     3
23 
24 /* FUNCTIONS *****************************************************************/
25 
26 .code
27 
28 PUBLIC _RtlpGetExceptionList@0
29 _RtlpGetExceptionList@0:
30 
31     /* Return the exception list */
32     mov eax, fs:[TEB_EXCEPTION_LIST]
33     ret
34 
35 
36 PUBLIC _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 
50 PUBLIC _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 
70 PUBLIC _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 
85 CaptureRest:
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 
115 PUBLIC _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 
125 PUBLIC _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 
161 PUBLIC _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 
223 return:
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 
254 PUBLIC _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 
302 DebuggerActive1:
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 
312 RaiseException:
313 
314     /* Raise the exception */
315     mov ecx, esp
316     push 0
317     push ecx
318     push [ebp+8]
319     call _ZwRaiseException@12
320 
321 RaiseStatus1:
322 
323     /* If we returned, raise a status */
324     push eax
325     call _RtlRaiseStatus@4
326 
327 
328 PUBLIC _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 
384 DebuggerActive2:
385 
386     /* Raise an exception immediately */
387     mov edx, esp
388     push 1
389     push edx
390     push ecx
391     call _ZwRaiseException@12
392 
393 RaiseStatus2:
394 
395     /* If we returned, raise a status */
396     push eax
397     call _RtlRaiseStatus@4
398 
399 END
400