[PSEH/RTL/CRT]
[reactos.git] / reactos / lib / rtl / exception.c
1 /* COPYRIGHT: See COPYING in the top level directory
2 * PROJECT: ReactOS Runtime Library
3 * PURPOSE: User-Mode Exception Support
4 * FILE: lib/rtl/exception.c
5 * PROGRAMERS: Alex Ionescu (alex@relsoft.net)
6 * David Welch <welch@cwcom.net>
7 * Skywing <skywing@valhallalegends.com>
8 * KJK::Hyperion <noog@libero.it>
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <rtl.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS *****************************************************************/
19
20 PRTLP_UNHANDLED_EXCEPTION_FILTER RtlpUnhandledExceptionFilter;
21
22 /* FUNCTIONS ***************************************************************/
23
24 #if !defined(_M_IX86) && !defined(_M_AMD64)
25
26 /*
27 * @implemented
28 */
29 VOID
30 NTAPI
31 RtlRaiseException(IN PEXCEPTION_RECORD ExceptionRecord)
32 {
33 CONTEXT Context;
34 NTSTATUS Status;
35
36 /* Capture the context */
37 RtlCaptureContext(&Context);
38
39 /* Save the exception address */
40 ExceptionRecord->ExceptionAddress = _ReturnAddress();
41
42 /* Write the context flag */
43 Context.ContextFlags = CONTEXT_FULL;
44
45 /* Check if user mode debugger is active */
46 if (RtlpCheckForActiveDebugger())
47 {
48 /* Raise an exception immediately */
49 Status = ZwRaiseException(ExceptionRecord, &Context, TRUE);
50 }
51 else
52 {
53 /* Dispatch the exception and check if we should continue */
54 if (!RtlDispatchException(ExceptionRecord, &Context))
55 {
56 /* Raise the exception */
57 Status = ZwRaiseException(ExceptionRecord, &Context, FALSE);
58 }
59 else
60 {
61 /* Continue, go back to previous context */
62 Status = ZwContinue(&Context, FALSE);
63 }
64 }
65
66 /* If we returned, raise a status */
67 RtlRaiseStatus(Status);
68 }
69
70 #endif
71
72 #if !defined(_M_IX86)
73
74 #ifdef _MSC_VER
75 #pragma warning(push)
76 #pragma warning(disable:4717) // RtlRaiseStatus is recursive by design
77 #endif
78
79 /*
80 * @implemented
81 */
82 VOID
83 NTAPI
84 RtlRaiseStatus(IN NTSTATUS Status)
85 {
86 EXCEPTION_RECORD ExceptionRecord;
87 CONTEXT Context;
88
89 /* Capture the context */
90 RtlCaptureContext(&Context);
91
92 /* Create an exception record */
93 ExceptionRecord.ExceptionAddress = _ReturnAddress();
94 ExceptionRecord.ExceptionCode = Status;
95 ExceptionRecord.ExceptionRecord = NULL;
96 ExceptionRecord.NumberParameters = 0;
97 ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
98
99 /* Write the context flag */
100 Context.ContextFlags = CONTEXT_FULL;
101
102 /* Check if user mode debugger is active */
103 if (RtlpCheckForActiveDebugger())
104 {
105 /* Raise an exception immediately */
106 ZwRaiseException(&ExceptionRecord, &Context, TRUE);
107 }
108 else
109 {
110 /* Dispatch the exception */
111 RtlDispatchException(&ExceptionRecord, &Context);
112
113 /* Raise exception if we got here */
114 Status = ZwRaiseException(&ExceptionRecord, &Context, FALSE);
115 }
116
117 /* If we returned, raise a status */
118 RtlRaiseStatus(Status);
119 }
120
121 #ifdef _MSC_VER
122 #pragma warning(pop)
123 #endif
124
125 #endif
126
127 /*
128 * @implemented
129 */
130 USHORT
131 NTAPI
132 RtlCaptureStackBackTrace(IN ULONG FramesToSkip,
133 IN ULONG FramesToCapture,
134 OUT PVOID *BackTrace,
135 OUT PULONG BackTraceHash OPTIONAL)
136 {
137 PVOID Frames[2 * 64];
138 ULONG FrameCount;
139 ULONG Hash = 0, i;
140
141 /* Skip a frame for the caller */
142 FramesToSkip++;
143
144 /* Don't go past the limit */
145 if ((FramesToCapture + FramesToSkip) >= 128) return 0;
146
147 /* Do the back trace */
148 FrameCount = RtlWalkFrameChain(Frames, FramesToCapture + FramesToSkip, 0);
149
150 /* Make sure we're not skipping all of them */
151 if (FrameCount <= FramesToSkip) return 0;
152
153 /* Loop all the frames */
154 for (i = 0; i < FramesToCapture; i++)
155 {
156 /* Don't go past the limit */
157 if ((FramesToSkip + i) >= FrameCount) break;
158
159 /* Save this entry and hash it */
160 BackTrace[i] = Frames[FramesToSkip + i];
161 Hash += PtrToUlong(BackTrace[i]);
162 }
163
164 /* Write the hash */
165 if (BackTraceHash) *BackTraceHash = Hash;
166
167 /* Clear the other entries and return count */
168 RtlFillMemoryUlong(Frames, 128, 0);
169 return (USHORT)i;
170 }
171
172 /*
173 * Private helper function to lookup the module name from a given address.
174 * The address can point to anywhere within the module.
175 */
176 static const char*
177 _module_name_from_addr(const void* addr, void **module_start_addr,
178 char* psz, size_t nChars)
179 {
180 #if 0
181 MEMORY_BASIC_INFORMATION mbi;
182 if (VirtualQuery(addr, &mbi, sizeof(mbi)) != sizeof(mbi) ||
183 !GetModuleFileNameA((HMODULE) mbi.AllocationBase, psz, nChars))
184 {
185 psz[0] = '\0';
186 *module_start_addr = 0;
187 }
188 else
189 {
190 *module_start_addr = (void *) mbi.AllocationBase;
191 }
192 return psz;
193 #else
194 psz[0] = '\0';
195 *module_start_addr = 0;
196 return psz;
197 #endif
198 }
199
200
201 static VOID
202 _dump_context(PCONTEXT pc)
203 {
204 #ifdef _M_IX86
205 /*
206 * Print out the CPU registers
207 */
208 DbgPrint("CS:EIP %x:%x\n", pc->SegCs & 0xffff, pc->Eip);
209 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs & 0xffff, pc->SegEs & 0xffff,
210 pc->SegFs & 0xffff, pc->SegGs & 0xfff);
211 DbgPrint("EAX: %.8x EBX: %.8x ECX: %.8x\n", pc->Eax, pc->Ebx, pc->Ecx);
212 DbgPrint("EDX: %.8x EBP: %.8x ESI: %.8x ESP: %.8x\n", pc->Edx,
213 pc->Ebp, pc->Esi, pc->Esp);
214 DbgPrint("EDI: %.8x EFLAGS: %.8x\n", pc->Edi, pc->EFlags);
215 #elif defined(_M_AMD64)
216 DbgPrint("CS:RIP %x:%I64x\n", pc->SegCs & 0xffff, pc->Rip);
217 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs & 0xffff, pc->SegEs & 0xffff,
218 pc->SegFs & 0xffff, pc->SegGs & 0xfff);
219 DbgPrint("RAX: %I64x RBX: %I64x RCX: %I64x RDI: %I64x\n", pc->Rax, pc->Rbx, pc->Rcx, pc->Rdi);
220 DbgPrint("RDX: %I64x RBP: %I64x RSI: %I64x RSP: %I64x\n", pc->Rdx, pc->Rbp, pc->Rsi, pc->Rsp);
221 DbgPrint("R8: %I64x R9: %I64x R10: %I64x R11: %I64x\n", pc->R8, pc->R9, pc->R10, pc->R11);
222 DbgPrint("R12: %I64x R13: %I64x R14: %I64x R15: %I64x\n", pc->R12, pc->R13, pc->R14, pc->R15);
223 DbgPrint("EFLAGS: %.8x\n", pc->EFlags);
224 #elif defined(_M_ARM)
225 DbgPrint("Pc: %lx Lr: %lx Sp: %lx Cpsr: %lx\n", pc->Pc, pc->Lr, pc->Sp, pc->Cpsr);
226 DbgPrint("R0: %lx R1: %lx R2: %lx R3: %lx\n", pc->R0, pc->R1, pc->R2, pc->R3);
227 DbgPrint("R4: %lx R5: %lx R6: %lx R7: %lx\n", pc->R4, pc->R5, pc->R6, pc->R7);
228 DbgPrint("R8: %lx R9: %lx R10: %lx R11: %lx\n", pc->R8, pc->R9, pc->R10, pc->R11);
229 DbgPrint("R12: %lx \n", pc->R12);
230 #else
231 #pragma message ("Unknown architecture")
232 #endif
233 }
234
235 static VOID
236 PrintStackTrace(struct _EXCEPTION_POINTERS *ExceptionInfo)
237 {
238 PVOID StartAddr;
239 CHAR szMod[128] = "";
240 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
241 PCONTEXT ContextRecord = ExceptionInfo->ContextRecord;
242
243 /* Print a stack trace. */
244 DbgPrint("Unhandled exception\n");
245 DbgPrint("ExceptionCode: %8x\n", ExceptionRecord->ExceptionCode);
246
247 if ((NTSTATUS) ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
248 ExceptionRecord->NumberParameters == 2)
249 {
250 DbgPrint("Faulting Address: %8x\n", ExceptionRecord->ExceptionInformation[1]);
251 }
252
253 _dump_context(ContextRecord);
254 _module_name_from_addr(ExceptionRecord->ExceptionAddress, &StartAddr, szMod, sizeof(szMod));
255 DbgPrint("Address:\n %8x+%-8x %s\n",
256 (PVOID) StartAddr,
257 (ULONG_PTR) ExceptionRecord->ExceptionAddress - (ULONG_PTR) StartAddr,
258 szMod);
259 #ifdef _M_IX86
260 DbgPrint("Frames:\n");
261
262 _SEH2_TRY
263 {
264 UINT i;
265 PULONG Frame = (PULONG) ContextRecord->Ebp;
266
267 for (i = 0; Frame[1] != 0 && Frame[1] != 0xdeadbeef && i < 128; i++)
268 {
269 //if (IsBadReadPtr((PVOID) Frame[1], 4))
270 if (Frame[1] == 0)
271 {
272 DbgPrint(" %8x%9s %s\n", Frame[1], "<invalid address>", " ");
273 }
274 else
275 {
276 _module_name_from_addr((const void*) Frame[1], &StartAddr,
277 szMod, sizeof(szMod));
278 DbgPrint(" %8x+%-8x %s\n",
279 (PVOID) StartAddr,
280 (ULONG_PTR) Frame[1] - (ULONG_PTR) StartAddr,
281 szMod);
282 }
283
284 if (Frame[0] == 0) break;
285 //if (IsBadReadPtr((PVOID) Frame[0], sizeof(*Frame) * 2))
286 //break;
287
288 Frame = (PULONG) Frame[0];
289 }
290 }
291 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
292 {
293 DbgPrint("<error dumping stack trace: 0x%x>\n", _SEH2_GetExceptionCode());
294 }
295 _SEH2_END;
296 #endif
297 }
298
299
300 /*
301 * @unimplemented
302 */
303 LONG
304 NTAPI
305 RtlUnhandledExceptionFilter(IN struct _EXCEPTION_POINTERS* ExceptionInfo)
306 {
307 /* This is used by the security cookie checks, and calso called externally */
308 UNIMPLEMENTED;
309 PrintStackTrace(ExceptionInfo);
310 return ERROR_CALL_NOT_IMPLEMENTED;
311 }
312
313 /*
314 * @implemented
315 */
316 VOID
317 NTAPI
318 RtlSetUnhandledExceptionFilter(IN PRTLP_UNHANDLED_EXCEPTION_FILTER TopLevelExceptionFilter)
319 {
320 /* Set the filter which is used by the CriticalSection package */
321 RtlpUnhandledExceptionFilter = RtlEncodePointer(TopLevelExceptionFilter);
322 }