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