[RTL]
[reactos.git] / reactos / lib / rtl / i386 / except.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Run-Time Library
4 * PURPOSE: User-mode exception support for IA-32
5 * FILE: lib/rtl/i386/exception.c
6 * PROGRAMERS: Alex Ionescu (alex@relsoft.net)
7 * Casper S. Hornstrup (chorns@users.sourceforge.net)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <rtl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PUBLIC FUNCTIONS **********************************************************/
17
18 /*
19 * @implemented
20 */
21 VOID
22 NTAPI
23 RtlGetCallersAddress(OUT PVOID *CallersAddress,
24 OUT PVOID *CallersCaller)
25 {
26 USHORT FrameCount;
27 PVOID BackTrace[2];
28 PULONG BackTraceHash = NULL;
29
30 /* Get the tow back trace address */
31 FrameCount = RtlCaptureStackBackTrace(2, 2, &BackTrace[0],BackTraceHash);
32
33 /* Only if user want it */
34 if (*CallersAddress != NULL)
35 {
36 /* only when first frames exist */
37 if (FrameCount >= 1)
38 {
39 *CallersAddress = BackTrace[0];
40 }
41 else
42 {
43 *CallersAddress = NULL;
44 }
45 }
46
47 /* Only if user want it */
48 if (*CallersCaller != NULL)
49 {
50 /* only when second frames exist */
51 if (FrameCount >= 2)
52 {
53 *CallersCaller = BackTrace[1];
54 }
55 else
56 {
57 *CallersCaller = NULL;
58 }
59 }
60 }
61
62 /*
63 * @implemented
64 */
65 BOOLEAN
66 NTAPI
67 RtlDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
68 IN PCONTEXT Context)
69 {
70 PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, NestedFrame = NULL;
71 DISPATCHER_CONTEXT DispatcherContext;
72 EXCEPTION_RECORD ExceptionRecord2;
73 EXCEPTION_DISPOSITION Disposition;
74 ULONG_PTR StackLow, StackHigh;
75 ULONG_PTR RegistrationFrameEnd;
76
77 /* Perform vectored exception handling if we are in user mode */
78 if (RtlpGetMode() != KernelMode)
79 {
80 /* Call any registered vectored handlers */
81 if (RtlCallVectoredExceptionHandlers(ExceptionRecord, Context))
82 {
83 /* Exception handled, continue execution */
84 return TRUE;
85 }
86 }
87
88 /* Get the current stack limits and registration frame */
89 RtlpGetStackLimits(&StackLow, &StackHigh);
90 RegistrationFrame = RtlpGetExceptionList();
91
92 /* Now loop every frame */
93 while (RegistrationFrame != EXCEPTION_CHAIN_END)
94 {
95 /* Registration chain entries are never NULL */
96 ASSERT(RegistrationFrame != NULL);
97
98 /* Find out where it ends */
99 RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
100 sizeof(EXCEPTION_REGISTRATION_RECORD);
101
102 /* Make sure the registration frame is located within the stack */
103 if ((RegistrationFrameEnd > StackHigh) ||
104 ((ULONG_PTR)RegistrationFrame < StackLow) ||
105 ((ULONG_PTR)RegistrationFrame & 0x3))
106 {
107 /* Check if this happened in the DPC Stack */
108 if (RtlpHandleDpcStackException(RegistrationFrame,
109 RegistrationFrameEnd,
110 &StackLow,
111 &StackHigh))
112 {
113 /* Use DPC Stack Limits and restart */
114 continue;
115 }
116
117 /* Set invalid stack and return false */
118 ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
119 return FALSE;
120 }
121
122 /* Check if logging is enabled */
123 RtlpCheckLogException(ExceptionRecord,
124 Context,
125 RegistrationFrame,
126 sizeof(*RegistrationFrame));
127
128 /* Call the handler */
129 Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
130 RegistrationFrame,
131 Context,
132 &DispatcherContext,
133 RegistrationFrame->Handler);
134
135 /* Check if this is a nested frame */
136 if (RegistrationFrame == NestedFrame)
137 {
138 /* Mask out the flag and the nested frame */
139 ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
140 NestedFrame = NULL;
141 }
142
143 /* Handle the dispositions */
144 switch (Disposition)
145 {
146 /* Continue searching */
147 case ExceptionContinueExecution:
148
149 /* Check if it was non-continuable */
150 if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
151 {
152 /* Set up the exception record */
153 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
154 ExceptionRecord2.ExceptionCode =
155 STATUS_NONCONTINUABLE_EXCEPTION;
156 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
157 ExceptionRecord2.NumberParameters = 0;
158
159 /* Raise the exception */
160 RtlRaiseException(&ExceptionRecord2);
161 }
162 else
163 {
164 /* Return to caller */
165 return TRUE;
166 }
167
168 /* Continue searching */
169 case ExceptionContinueSearch:
170 break;
171
172 /* Nested exception */
173 case ExceptionNestedException:
174
175 /* Turn the nested flag on */
176 ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
177
178 /* Update the current nested frame */
179 if (DispatcherContext.RegistrationPointer > NestedFrame)
180 {
181 /* Get the frame from the dispatcher context */
182 NestedFrame = DispatcherContext.RegistrationPointer;
183 }
184 break;
185
186 /* Anything else */
187 default:
188
189 /* Set up the exception record */
190 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
191 ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
192 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
193 ExceptionRecord2.NumberParameters = 0;
194
195 /* Raise the exception */
196 RtlRaiseException(&ExceptionRecord2);
197 break;
198 }
199
200 /* Go to the next frame */
201 RegistrationFrame = RegistrationFrame->Next;
202 }
203
204 /* Unhandled, return false */
205 return FALSE;
206 }
207
208 /*
209 * @implemented
210 */
211 VOID
212 NTAPI
213 RtlUnwind(IN PVOID TargetFrame OPTIONAL,
214 IN PVOID TargetIp OPTIONAL,
215 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
216 IN PVOID ReturnValue)
217 {
218 PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, OldFrame;
219 DISPATCHER_CONTEXT DispatcherContext;
220 EXCEPTION_RECORD ExceptionRecord2, ExceptionRecord3;
221 EXCEPTION_DISPOSITION Disposition;
222 ULONG_PTR StackLow, StackHigh;
223 ULONG_PTR RegistrationFrameEnd;
224 CONTEXT LocalContext;
225 PCONTEXT Context;
226
227 /* Get the current stack limits */
228 RtlpGetStackLimits(&StackLow, &StackHigh);
229
230 /* Check if we don't have an exception record */
231 if (!ExceptionRecord)
232 {
233 /* Overwrite the argument */
234 ExceptionRecord = &ExceptionRecord3;
235
236 /* Setup a local one */
237 ExceptionRecord3.ExceptionFlags = 0;
238 ExceptionRecord3.ExceptionCode = STATUS_UNWIND;
239 ExceptionRecord3.ExceptionRecord = NULL;
240 ExceptionRecord3.ExceptionAddress = _ReturnAddress();
241 ExceptionRecord3.NumberParameters = 0;
242 }
243
244 /* Check if we have a frame */
245 if (TargetFrame)
246 {
247 /* Set it as unwinding */
248 ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING;
249 }
250 else
251 {
252 /* Set the Exit Unwind flag as well */
253 ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING |
254 EXCEPTION_EXIT_UNWIND);
255 }
256
257 /* Now capture the context */
258 Context = &LocalContext;
259 LocalContext.ContextFlags = CONTEXT_INTEGER |
260 CONTEXT_CONTROL |
261 CONTEXT_SEGMENTS;
262 RtlpCaptureContext(Context);
263
264 /* Pop the current arguments off */
265 Context->Esp += sizeof(TargetFrame) +
266 sizeof(TargetIp) +
267 sizeof(ExceptionRecord) +
268 sizeof(ReturnValue);
269
270 /* Set the new value for EAX */
271 Context->Eax = (ULONG)ReturnValue;
272
273 /* Get the current frame */
274 RegistrationFrame = RtlpGetExceptionList();
275
276 /* Now loop every frame */
277 while (RegistrationFrame != EXCEPTION_CHAIN_END)
278 {
279 /* Registration chain entries are never NULL */
280 ASSERT(RegistrationFrame != NULL);
281
282 /* If this is the target */
283 if (RegistrationFrame == TargetFrame) ZwContinue(Context, FALSE);
284
285 /* Check if the frame is too low */
286 if ((TargetFrame) &&
287 ((ULONG_PTR)TargetFrame < (ULONG_PTR)RegistrationFrame))
288 {
289 /* Create an invalid unwind exception */
290 ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
291 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
292 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
293 ExceptionRecord2.NumberParameters = 0;
294
295 /* Raise the exception */
296 RtlRaiseException(&ExceptionRecord2);
297 }
298
299 /* Find out where it ends */
300 RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
301 sizeof(EXCEPTION_REGISTRATION_RECORD);
302
303 /* Make sure the registration frame is located within the stack */
304 if ((RegistrationFrameEnd > StackHigh) ||
305 ((ULONG_PTR)RegistrationFrame < StackLow) ||
306 ((ULONG_PTR)RegistrationFrame & 0x3))
307 {
308 /* Check if this happened in the DPC Stack */
309 if (RtlpHandleDpcStackException(RegistrationFrame,
310 RegistrationFrameEnd,
311 &StackLow,
312 &StackHigh))
313 {
314 /* Use DPC Stack Limits and restart */
315 continue;
316 }
317
318 /* Create an invalid stack exception */
319 ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK;
320 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
321 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
322 ExceptionRecord2.NumberParameters = 0;
323
324 /* Raise the exception */
325 RtlRaiseException(&ExceptionRecord2);
326 }
327 else
328 {
329 /* Call the handler */
330 Disposition = RtlpExecuteHandlerForUnwind(ExceptionRecord,
331 RegistrationFrame,
332 Context,
333 &DispatcherContext,
334 RegistrationFrame->Handler);
335 switch(Disposition)
336 {
337 /* Continue searching */
338 case ExceptionContinueSearch:
339 break;
340
341 /* Collission */
342 case ExceptionCollidedUnwind :
343
344 /* Get the original frame */
345 RegistrationFrame = DispatcherContext.RegistrationPointer;
346 break;
347
348 /* Anything else */
349 default:
350
351 /* Set up the exception record */
352 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
353 ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
354 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
355 ExceptionRecord2.NumberParameters = 0;
356
357 /* Raise the exception */
358 RtlRaiseException(&ExceptionRecord2);
359 break;
360 }
361
362 /* Go to the next frame */
363 OldFrame = RegistrationFrame;
364 RegistrationFrame = RegistrationFrame->Next;
365
366 /* Remove this handler */
367 RtlpSetExceptionList(OldFrame);
368 }
369 }
370
371 /* Check if we reached the end */
372 if (TargetFrame == EXCEPTION_CHAIN_END)
373 {
374 /* Unwind completed, so we don't exit */
375 ZwContinue(Context, FALSE);
376 }
377 else
378 {
379 /* This is an exit_unwind or the frame wasn't present in the list */
380 ZwRaiseException(ExceptionRecord, Context, FALSE);
381 }
382 }
383
384 /* EOF */