xref: /reactos/sdk/lib/rtl/exception.c (revision 1734f297)
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 
21 PRTLP_UNHANDLED_EXCEPTION_FILTER RtlpUnhandledExceptionFilter;
22 
23 /* FUNCTIONS ***************************************************************/
24 
25 #if !defined(_M_IX86) && !defined(_M_AMD64)
26 
27 /*
28  * @implemented
29  */
30 VOID
31 NTAPI
32 RtlRaiseException(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  */
83 VOID
84 NTAPI
85 RtlRaiseStatus(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  */
131 USHORT
132 NTAPI
133 RtlCaptureStackBackTrace(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 */
177 static 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 
202 static 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 
236 static VOID
237     PrintStackTrace(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  */
311 LONG
312 NTAPI
313 RtlUnhandledExceptionFilter(IN struct _EXCEPTION_POINTERS* ExceptionInfo)
314 {
315     /* This is used by the security cookie checks, and also called externally */
316     UNIMPLEMENTED;
317     PrintStackTrace(ExceptionInfo);
318     return ERROR_CALL_NOT_IMPLEMENTED;
319 }
320 
321 /*
322  * @unimplemented
323  */
324 LONG
325 NTAPI
326 RtlUnhandledExceptionFilter2(
327     _In_ PEXCEPTION_POINTERS ExceptionInfo,
328     _In_ ULONG Flags)
329 {
330     /* This is used by the security cookie checks, and also called externally */
331     UNIMPLEMENTED;
332     PrintStackTrace(ExceptionInfo);
333     return ERROR_CALL_NOT_IMPLEMENTED;
334 }
335 
336 /*
337  * @implemented
338  */
339 VOID
340 NTAPI
341 RtlSetUnhandledExceptionFilter(IN PRTLP_UNHANDLED_EXCEPTION_FILTER TopLevelExceptionFilter)
342 {
343     /* Set the filter which is used by the CriticalSection package */
344     RtlpUnhandledExceptionFilter = RtlEncodePointer(TopLevelExceptionFilter);
345 }
346