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