[ASM]
[reactos.git] / reactos / lib / rtl / i386 / except_asm.s
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 _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
159 PUBLIC _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
221 return:
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
252 PUBLIC _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
300 DebuggerActive1:
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
310 RaiseException:
311
312 /* Raise the exception */
313 mov ecx, esp
314 push 0
315 push ecx
316 push [ebp+8]
317 call _ZwRaiseException@12
318
319 RaiseStatus1:
320
321 /* If we returned, raise a status */
322 push eax
323 call _RtlRaiseStatus@4
324
325
326 PUBLIC _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
382 DebuggerActive2:
383
384 /* Raise an exception immediately */
385 mov edx, esp
386 push 1
387 push edx
388 push ecx
389 call _ZwRaiseException@12
390
391 RaiseStatus2:
392
393 /* If we returned, raise a status */
394 push eax
395 call _RtlRaiseStatus@4
396
397 END