[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 (a dummy in kernel mode) */
78 if (RtlCallVectoredExceptionHandlers(ExceptionRecord, Context))
79 {
80 /* Exception handled, continue execution */
81 return TRUE;
82 }
83
84 /* Get the current stack limits and registration frame */
85 RtlpGetStackLimits(&StackLow, &StackHigh);
86 RegistrationFrame = RtlpGetExceptionList();
87
88 /* Now loop every frame */
89 while (RegistrationFrame != EXCEPTION_CHAIN_END)
90 {
91 /* Registration chain entries are never NULL */
92 ASSERT(RegistrationFrame != NULL);
93
94 /* Find out where it ends */
95 RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
96 sizeof(EXCEPTION_REGISTRATION_RECORD);
97
98 /* Make sure the registration frame is located within the stack */
99 if ((RegistrationFrameEnd > StackHigh) ||
100 ((ULONG_PTR)RegistrationFrame < StackLow) ||
101 ((ULONG_PTR)RegistrationFrame & 0x3))
102 {
103 /* Check if this happened in the DPC Stack */
104 if (RtlpHandleDpcStackException(RegistrationFrame,
105 RegistrationFrameEnd,
106 &StackLow,
107 &StackHigh))
108 {
109 /* Use DPC Stack Limits and restart */
110 continue;
111 }
112
113 /* Set invalid stack and return false */
114 ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
115 return FALSE;
116 }
117
118 /* Check if logging is enabled */
119 RtlpCheckLogException(ExceptionRecord,
120 Context,
121 RegistrationFrame,
122 sizeof(*RegistrationFrame));
123
124 /* Call the handler */
125 Disposition = RtlpExecuteHandlerForException(ExceptionRecord,
126 RegistrationFrame,
127 Context,
128 &DispatcherContext,
129 RegistrationFrame->Handler);
130
131 /* Check if this is a nested frame */
132 if (RegistrationFrame == NestedFrame)
133 {
134 /* Mask out the flag and the nested frame */
135 ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NESTED_CALL;
136 NestedFrame = NULL;
137 }
138
139 /* Handle the dispositions */
140 switch (Disposition)
141 {
142 /* Continue searching */
143 case ExceptionContinueExecution:
144
145 /* Check if it was non-continuable */
146 if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
147 {
148 /* Set up the exception record */
149 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
150 ExceptionRecord2.ExceptionCode =
151 STATUS_NONCONTINUABLE_EXCEPTION;
152 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
153 ExceptionRecord2.NumberParameters = 0;
154
155 /* Raise the exception */
156 RtlRaiseException(&ExceptionRecord2);
157 }
158 else
159 {
160 /* Return to caller */
161 return TRUE;
162 }
163
164 /* Continue searching */
165 case ExceptionContinueSearch:
166 break;
167
168 /* Nested exception */
169 case ExceptionNestedException:
170
171 /* Turn the nested flag on */
172 ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
173
174 /* Update the current nested frame */
175 if (DispatcherContext.RegistrationPointer > NestedFrame)
176 {
177 /* Get the frame from the dispatcher context */
178 NestedFrame = DispatcherContext.RegistrationPointer;
179 }
180 break;
181
182 /* Anything else */
183 default:
184
185 /* Set up the exception record */
186 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
187 ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
188 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
189 ExceptionRecord2.NumberParameters = 0;
190
191 /* Raise the exception */
192 RtlRaiseException(&ExceptionRecord2);
193 break;
194 }
195
196 /* Go to the next frame */
197 RegistrationFrame = RegistrationFrame->Next;
198 }
199
200 /* Unhandled, return false */
201 return FALSE;
202 }
203
204 /*
205 * @implemented
206 */
207 VOID
208 NTAPI
209 RtlUnwind(IN PVOID TargetFrame OPTIONAL,
210 IN PVOID TargetIp OPTIONAL,
211 IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
212 IN PVOID ReturnValue)
213 {
214 PEXCEPTION_REGISTRATION_RECORD RegistrationFrame, OldFrame;
215 DISPATCHER_CONTEXT DispatcherContext;
216 EXCEPTION_RECORD ExceptionRecord2, ExceptionRecord3;
217 EXCEPTION_DISPOSITION Disposition;
218 ULONG_PTR StackLow, StackHigh;
219 ULONG_PTR RegistrationFrameEnd;
220 CONTEXT LocalContext;
221 PCONTEXT Context;
222
223 /* Get the current stack limits */
224 RtlpGetStackLimits(&StackLow, &StackHigh);
225
226 /* Check if we don't have an exception record */
227 if (!ExceptionRecord)
228 {
229 /* Overwrite the argument */
230 ExceptionRecord = &ExceptionRecord3;
231
232 /* Setup a local one */
233 ExceptionRecord3.ExceptionFlags = 0;
234 ExceptionRecord3.ExceptionCode = STATUS_UNWIND;
235 ExceptionRecord3.ExceptionRecord = NULL;
236 ExceptionRecord3.ExceptionAddress = _ReturnAddress();
237 ExceptionRecord3.NumberParameters = 0;
238 }
239
240 /* Check if we have a frame */
241 if (TargetFrame)
242 {
243 /* Set it as unwinding */
244 ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING;
245 }
246 else
247 {
248 /* Set the Exit Unwind flag as well */
249 ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING |
250 EXCEPTION_EXIT_UNWIND);
251 }
252
253 /* Now capture the context */
254 Context = &LocalContext;
255 LocalContext.ContextFlags = CONTEXT_INTEGER |
256 CONTEXT_CONTROL |
257 CONTEXT_SEGMENTS;
258 RtlpCaptureContext(Context);
259
260 /* Pop the current arguments off */
261 Context->Esp += sizeof(TargetFrame) +
262 sizeof(TargetIp) +
263 sizeof(ExceptionRecord) +
264 sizeof(ReturnValue);
265
266 /* Set the new value for EAX */
267 Context->Eax = (ULONG)ReturnValue;
268
269 /* Get the current frame */
270 RegistrationFrame = RtlpGetExceptionList();
271
272 /* Now loop every frame */
273 while (RegistrationFrame != EXCEPTION_CHAIN_END)
274 {
275 /* Registration chain entries are never NULL */
276 ASSERT(RegistrationFrame != NULL);
277
278 /* If this is the target */
279 if (RegistrationFrame == TargetFrame) ZwContinue(Context, FALSE);
280
281 /* Check if the frame is too low */
282 if ((TargetFrame) &&
283 ((ULONG_PTR)TargetFrame < (ULONG_PTR)RegistrationFrame))
284 {
285 /* Create an invalid unwind exception */
286 ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
287 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
288 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
289 ExceptionRecord2.NumberParameters = 0;
290
291 /* Raise the exception */
292 RtlRaiseException(&ExceptionRecord2);
293 }
294
295 /* Find out where it ends */
296 RegistrationFrameEnd = (ULONG_PTR)RegistrationFrame +
297 sizeof(EXCEPTION_REGISTRATION_RECORD);
298
299 /* Make sure the registration frame is located within the stack */
300 if ((RegistrationFrameEnd > StackHigh) ||
301 ((ULONG_PTR)RegistrationFrame < StackLow) ||
302 ((ULONG_PTR)RegistrationFrame & 0x3))
303 {
304 /* Check if this happened in the DPC Stack */
305 if (RtlpHandleDpcStackException(RegistrationFrame,
306 RegistrationFrameEnd,
307 &StackLow,
308 &StackHigh))
309 {
310 /* Use DPC Stack Limits and restart */
311 continue;
312 }
313
314 /* Create an invalid stack exception */
315 ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK;
316 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
317 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
318 ExceptionRecord2.NumberParameters = 0;
319
320 /* Raise the exception */
321 RtlRaiseException(&ExceptionRecord2);
322 }
323 else
324 {
325 /* Call the handler */
326 Disposition = RtlpExecuteHandlerForUnwind(ExceptionRecord,
327 RegistrationFrame,
328 Context,
329 &DispatcherContext,
330 RegistrationFrame->Handler);
331 switch(Disposition)
332 {
333 /* Continue searching */
334 case ExceptionContinueSearch:
335 break;
336
337 /* Collission */
338 case ExceptionCollidedUnwind :
339
340 /* Get the original frame */
341 RegistrationFrame = DispatcherContext.RegistrationPointer;
342 break;
343
344 /* Anything else */
345 default:
346
347 /* Set up the exception record */
348 ExceptionRecord2.ExceptionRecord = ExceptionRecord;
349 ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
350 ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
351 ExceptionRecord2.NumberParameters = 0;
352
353 /* Raise the exception */
354 RtlRaiseException(&ExceptionRecord2);
355 break;
356 }
357
358 /* Go to the next frame */
359 OldFrame = RegistrationFrame;
360 RegistrationFrame = RegistrationFrame->Next;
361
362 /* Remove this handler */
363 RtlpSetExceptionList(OldFrame);
364 }
365 }
366
367 /* Check if we reached the end */
368 if (TargetFrame == EXCEPTION_CHAIN_END)
369 {
370 /* Unwind completed, so we don't exit */
371 ZwContinue(Context, FALSE);
372 }
373 else
374 {
375 /* This is an exit_unwind or the frame wasn't present in the list */
376 ZwRaiseException(ExceptionRecord, Context, FALSE);
377 }
378 }
379
380 /* EOF */