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