Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenunwind.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS system libraries 00004 * PURPOSE: Unwinding related functions 00005 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org) 00006 */ 00007 00008 /* INCLUDES *****************************************************************/ 00009 00010 #include <rtl.h> 00011 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 #define UNWIND_HISTORY_TABLE_NONE 0 00016 #define UNWIND_HISTORY_TABLE_GLOBAL 1 00017 #define UNWIND_HISTORY_TABLE_LOCAL 2 00018 00019 #define UWOP_PUSH_NONVOL 0 00020 #define UWOP_ALLOC_LARGE 1 00021 #define UWOP_ALLOC_SMALL 2 00022 #define UWOP_SET_FPREG 3 00023 #define UWOP_SAVE_NONVOL 4 00024 #define UWOP_SAVE_NONVOL_FAR 5 00025 #define UWOP_SAVE_XMM 6 00026 #define UWOP_SAVE_XMM_FAR 7 00027 #define UWOP_SAVE_XMM128 8 00028 #define UWOP_SAVE_XMM128_FAR 9 00029 #define UWOP_PUSH_MACHFRAME 10 00030 00031 #define UNW_FLAG_NHANDLER 0 00032 #define UNW_FLAG_EHANDLER 1 00033 #define UNW_FLAG_UHANDLER 2 00034 #define UNW_FLAG_CHAININFO 4 00035 00036 typedef unsigned char UBYTE; 00037 00038 typedef union _UNWIND_CODE 00039 { 00040 struct 00041 { 00042 UBYTE CodeOffset; 00043 UBYTE UnwindOp:4; 00044 UBYTE OpInfo:4; 00045 }; 00046 USHORT FrameOffset; 00047 } UNWIND_CODE, *PUNWIND_CODE; 00048 00049 typedef struct _UNWIND_INFO 00050 { 00051 UBYTE Version:3; 00052 UBYTE Flags:5; 00053 UBYTE SizeOfProlog; 00054 UBYTE CountOfCodes; 00055 UBYTE FrameRegister:4; 00056 UBYTE FrameOffset:4; 00057 UNWIND_CODE UnwindCode[1]; 00058 /* union { 00059 OPTIONAL ULONG ExceptionHandler; 00060 OPTIONAL ULONG FunctionEntry; 00061 }; 00062 OPTIONAL ULONG ExceptionData[]; 00063 */ 00064 } UNWIND_INFO, *PUNWIND_INFO; 00065 00066 /* FUNCTIONS *****************************************************************/ 00067 00079 PRUNTIME_FUNCTION 00080 NTAPI 00081 RtlLookupFunctionTable( 00082 IN DWORD64 ControlPc, 00083 OUT PDWORD64 ImageBase, 00084 OUT PULONG Length) 00085 { 00086 PVOID Table; 00087 ULONG Size; 00088 00089 /* Find corresponding file header from code address */ 00090 if (!RtlPcToFileHeader((PVOID)ControlPc, (PVOID*)ImageBase)) 00091 { 00092 /* Nothing found */ 00093 return NULL; 00094 } 00095 00096 /* Locate the exception directory */ 00097 Table = RtlImageDirectoryEntryToData((PVOID)*ImageBase, 00098 TRUE, 00099 IMAGE_DIRECTORY_ENTRY_EXCEPTION, 00100 &Size); 00101 00102 /* Return the number of entries */ 00103 *Length = Size / sizeof(RUNTIME_FUNCTION); 00104 00105 /* Return the address of the table */ 00106 return Table; 00107 } 00108 00114 PRUNTIME_FUNCTION 00115 NTAPI 00116 RtlLookupFunctionEntry( 00117 IN DWORD64 ControlPc, 00118 OUT PDWORD64 ImageBase, 00119 OUT PUNWIND_HISTORY_TABLE HistoryTable) 00120 { 00121 PRUNTIME_FUNCTION FunctionTable, FunctionEntry; 00122 ULONG TableLength; 00123 ULONG IndexLo, IndexHi, IndexMid; 00124 00125 /* Find the corresponding table */ 00126 FunctionTable = RtlLookupFunctionTable(ControlPc, ImageBase, &TableLength); 00127 00128 /* Fail, if no table is found */ 00129 if (!FunctionTable) 00130 { 00131 return NULL; 00132 } 00133 00134 /* Use relative virtual address */ 00135 ControlPc -= *ImageBase; 00136 00137 /* Do a binary search */ 00138 IndexLo = 0; 00139 IndexHi = TableLength; 00140 while (IndexHi > IndexLo) 00141 { 00142 IndexMid = (IndexLo + IndexHi) / 2; 00143 FunctionEntry = &FunctionTable[IndexMid]; 00144 00145 if (ControlPc < FunctionEntry->BeginAddress) 00146 { 00147 /* Continue search in lower half */ 00148 IndexHi = IndexMid; 00149 } 00150 else if (ControlPc >= FunctionEntry->EndAddress) 00151 { 00152 /* Continue search in upper half */ 00153 IndexLo = IndexMid + 1; 00154 } 00155 else 00156 { 00157 /* ControlPc is within limits, return entry */ 00158 return FunctionEntry; 00159 } 00160 } 00161 00162 /* Nothing found, return NULL */ 00163 return NULL; 00164 } 00165 00166 BOOLEAN 00167 NTAPI 00168 RtlAddFunctionTable( 00169 IN PRUNTIME_FUNCTION FunctionTable, 00170 IN DWORD EntryCount, 00171 IN DWORD64 BaseAddress) 00172 { 00173 UNIMPLEMENTED; 00174 return FALSE; 00175 } 00176 00177 BOOLEAN 00178 NTAPI 00179 RtlDeleteFunctionTable( 00180 IN PRUNTIME_FUNCTION FunctionTable) 00181 { 00182 UNIMPLEMENTED; 00183 return FALSE; 00184 } 00185 00186 BOOLEAN 00187 NTAPI 00188 RtlInstallFunctionTableCallback( 00189 IN DWORD64 TableIdentifier, 00190 IN DWORD64 BaseAddress, 00191 IN DWORD Length, 00192 IN PGET_RUNTIME_FUNCTION_CALLBACK Callback, 00193 IN PVOID Context, 00194 IN PCWSTR OutOfProcessCallbackDll) 00195 { 00196 UNIMPLEMENTED; 00197 return FALSE; 00198 } 00199 00200 void 00201 FORCEINLINE 00202 SetReg(PCONTEXT Context, BYTE Reg, DWORD64 Value) 00203 { 00204 ((DWORD64*)(&Context->Rax))[Reg] = Value; 00205 } 00206 00207 DWORD64 00208 FORCEINLINE 00209 GetReg(PCONTEXT Context, BYTE Reg) 00210 { 00211 return ((DWORD64*)(&Context->Rax))[Reg]; 00212 } 00213 00214 void 00215 FORCEINLINE 00216 PopReg(PCONTEXT Context, BYTE Reg) 00217 { 00218 DWORD64 Value = *(DWORD64*)Context->Rsp; 00219 Context->Rsp += 8; 00220 SetReg(Context, Reg, Value); 00221 } 00222 00233 BOOLEAN 00234 static 00235 __inline 00236 RtlpTryToUnwindEpilog( 00237 PCONTEXT Context, 00238 ULONG64 ImageBase, 00239 PRUNTIME_FUNCTION FunctionEntry) 00240 { 00241 CONTEXT LocalContext; 00242 BYTE *InstrPtr; 00243 DWORD Instr; 00244 BYTE Reg, Mod; 00245 ULONG64 EndAddress; 00246 00247 /* Make a local copy of the context */ 00248 LocalContext = *Context; 00249 00250 InstrPtr = (BYTE*)LocalContext.Rip; 00251 00252 /* Check if first instruction of epilog is "add rsp, x" */ 00253 Instr = *(DWORD*)InstrPtr; 00254 if ( (Instr & 0x00fffdff) == 0x00c48148 ) 00255 { 00256 if ( (Instr & 0x0000ff00) == 0x8300 ) 00257 { 00258 /* This is "add rsp, 0x??" */ 00259 LocalContext.Rsp += Instr >> 24; 00260 InstrPtr += 4; 00261 } 00262 else 00263 { 00264 /* This is "add rsp, 0x???????? */ 00265 LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); 00266 InstrPtr += 7; 00267 } 00268 } 00269 /* Check if first instruction of epilog is "lea rsp, ..." */ 00270 else if ( (Instr & 0x38fffe) == 0x208d48 ) 00271 { 00272 /* Get the register */ 00273 Reg = ((Instr << 8) | (Instr >> 16)) & 0x7; 00274 00275 LocalContext.Rsp = GetReg(&LocalContext, Reg); 00276 00277 /* Get adressing mode */ 00278 Mod = (Instr >> 22) & 0x3; 00279 if (Mod == 0) 00280 { 00281 /* No displacement */ 00282 InstrPtr += 3; 00283 } 00284 else if (Mod == 1) 00285 { 00286 /* 1 byte displacement */ 00287 LocalContext.Rsp += Instr >> 24; 00288 InstrPtr += 4; 00289 } 00290 else if (Mod == 2) 00291 { 00292 /* 4 bytes displacement */ 00293 LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); 00294 InstrPtr += 7; 00295 } 00296 } 00297 00298 /* Loop the following instructions */ 00299 EndAddress = FunctionEntry->EndAddress + ImageBase; 00300 while((DWORD64)InstrPtr < EndAddress) 00301 { 00302 Instr = *(DWORD*)InstrPtr; 00303 00304 /* Check for a simple pop */ 00305 if ( (Instr & 0xf8) == 0x58 ) 00306 { 00307 /* Opcode pops a basic register from stack */ 00308 Reg = Instr & 0x7; 00309 PopReg(&LocalContext, Reg); 00310 InstrPtr++; 00311 continue; 00312 } 00313 00314 /* Check for REX + pop */ 00315 if ( (Instr & 0xf8fb) == 0x5841 ) 00316 { 00317 /* Opcode is pop r8 .. r15 */ 00318 Reg = (Instr & 0x7) + 8; 00319 PopReg(&LocalContext, Reg); 00320 InstrPtr += 2; 00321 continue; 00322 } 00323 00324 /* Check for retn / retf */ 00325 if ( (Instr & 0xf7) == 0xc3 ) 00326 { 00327 /* We are finished */ 00328 break; 00329 } 00330 00331 /* Opcode not allowed for Epilog */ 00332 return FALSE; 00333 } 00334 00335 /* Unwind is finished, pop new Rip from Stack */ 00336 LocalContext.Rip = *(DWORD64*)LocalContext.Rsp; 00337 LocalContext.Rsp += sizeof(DWORD64); 00338 00339 *Context = LocalContext; 00340 return TRUE; 00341 } 00342 00343 00344 PEXCEPTION_ROUTINE 00345 NTAPI 00346 RtlVirtualUnwind( 00347 IN ULONG HandlerType, 00348 IN ULONG64 ImageBase, 00349 IN ULONG64 ControlPc, 00350 IN PRUNTIME_FUNCTION FunctionEntry, 00351 IN OUT PCONTEXT Context, 00352 OUT PVOID *HandlerData, 00353 OUT PULONG64 EstablisherFrame, 00354 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers) 00355 { 00356 PUNWIND_INFO UnwindInfo; 00357 ULONG_PTR CodeOffset; 00358 ULONG i; 00359 UNWIND_CODE UnwindCode; 00360 BYTE Reg; 00361 00362 /* Use relative virtual address */ 00363 ControlPc -= ImageBase; 00364 00365 /* Sanity checks */ 00366 if ( (ControlPc < FunctionEntry->BeginAddress) || 00367 (ControlPc >= FunctionEntry->EndAddress) ) 00368 { 00369 return NULL; 00370 } 00371 00372 /* Get a pointer to the unwind info */ 00373 UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData); 00374 00375 /* Calculate relative offset to function start */ 00376 CodeOffset = ControlPc - FunctionEntry->BeginAddress; 00377 00378 /* Check if we are in the function epilog and try to finish it */ 00379 if (CodeOffset > UnwindInfo->SizeOfProlog) 00380 { 00381 if (RtlpTryToUnwindEpilog(Context, ImageBase, FunctionEntry)) 00382 { 00383 /* There's no exception routine */ 00384 return NULL; 00385 } 00386 } 00387 00388 /* Skip all Ops with an offset greater than the current Offset */ 00389 i = 0; 00390 while (i < UnwindInfo->CountOfCodes && 00391 CodeOffset < UnwindInfo->UnwindCode[i].CodeOffset) 00392 { 00393 UnwindCode = UnwindInfo->UnwindCode[i]; 00394 switch (UnwindCode.UnwindOp) 00395 { 00396 case UWOP_SAVE_NONVOL: 00397 case UWOP_SAVE_XMM: 00398 case UWOP_SAVE_XMM128: 00399 i += 2; 00400 break; 00401 00402 case UWOP_SAVE_NONVOL_FAR: 00403 case UWOP_SAVE_XMM_FAR: 00404 case UWOP_SAVE_XMM128_FAR: 00405 i += 3; 00406 break; 00407 00408 case UWOP_ALLOC_LARGE: 00409 i += UnwindCode.OpInfo ? 3 : 2; 00410 break; 00411 00412 default: 00413 i++; 00414 } 00415 } 00416 00417 /* Process the left Ops */ 00418 while (i < UnwindInfo->CountOfCodes) 00419 { 00420 UnwindCode = UnwindInfo->UnwindCode[i]; 00421 switch (UnwindCode.UnwindOp) 00422 { 00423 case UWOP_PUSH_NONVOL: 00424 Reg = UnwindCode.OpInfo; 00425 SetReg(Context, Reg, *(DWORD64*)Context->Rsp); 00426 Context->Rsp += sizeof(DWORD64); 00427 i++; 00428 break; 00429 00430 case UWOP_ALLOC_LARGE: 00431 if (UnwindCode.OpInfo) 00432 { 00433 ULONG Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i+1]); 00434 Context->Rsp += Offset; 00435 i += 3; 00436 } 00437 else 00438 { 00439 USHORT Offset = UnwindInfo->UnwindCode[i+1].FrameOffset; 00440 Context->Rsp += Offset * 8; 00441 i += 2; 00442 } 00443 break; 00444 00445 case UWOP_ALLOC_SMALL: 00446 Context->Rsp += (UnwindCode.OpInfo + 1) * 8; 00447 i++; 00448 break; 00449 00450 case UWOP_SET_FPREG: 00451 i++; 00452 break; 00453 00454 case UWOP_SAVE_NONVOL: 00455 i += 2; 00456 break; 00457 00458 case UWOP_SAVE_NONVOL_FAR: 00459 i += 3; 00460 break; 00461 00462 case UWOP_SAVE_XMM: 00463 i += 2; 00464 break; 00465 00466 case UWOP_SAVE_XMM_FAR: 00467 i += 3; 00468 break; 00469 00470 case UWOP_SAVE_XMM128: 00471 i += 2; 00472 break; 00473 00474 case UWOP_SAVE_XMM128_FAR: 00475 i += 3; 00476 break; 00477 00478 case UWOP_PUSH_MACHFRAME: 00479 i += 1; 00480 break; 00481 } 00482 } 00483 00484 /* Unwind is finished, pop new Rip from Stack */ 00485 Context->Rip = *(DWORD64*)Context->Rsp; 00486 Context->Rsp += sizeof(DWORD64); 00487 00488 return 0; 00489 } 00490 00491 VOID 00492 NTAPI 00493 RtlUnwindEx( 00494 IN ULONG64 TargetFrame, 00495 IN ULONG64 TargetIp, 00496 IN PEXCEPTION_RECORD ExceptionRecord, 00497 IN PVOID ReturnValue, 00498 OUT PCONTEXT OriginalContext, 00499 IN PUNWIND_HISTORY_TABLE HistoryTable) 00500 { 00501 UNIMPLEMENTED; 00502 return; 00503 } 00504 00505 VOID 00506 NTAPI 00507 RtlUnwind( 00508 IN PVOID TargetFrame, 00509 IN PVOID TargetIp, 00510 IN PEXCEPTION_RECORD ExceptionRecord, 00511 IN PVOID ReturnValue) 00512 { 00513 UNIMPLEMENTED; 00514 return; 00515 } 00516 00517 ULONG 00518 NTAPI 00519 RtlWalkFrameChain(OUT PVOID *Callers, 00520 IN ULONG Count, 00521 IN ULONG Flags) 00522 { 00523 CONTEXT Context; 00524 ULONG64 ControlPc, ImageBase, EstablisherFrame; 00525 ULONG64 StackLow, StackHigh; 00526 PVOID HandlerData; 00527 ULONG i; 00528 PRUNTIME_FUNCTION FunctionEntry; 00529 00530 DPRINT("Enter RtlWalkFrameChain\n"); 00531 00532 /* Capture the current Context */ 00533 RtlCaptureContext(&Context); 00534 ControlPc = Context.Rip; 00535 00536 /* Get the stack limits */ 00537 RtlpGetStackLimits(&StackLow, &StackHigh); 00538 00539 /* Check if we want the user-mode stack frame */ 00540 if (Flags == 1) 00541 { 00542 } 00543 00544 /* Loop the frames */ 00545 for (i = 0; i < Count; i++) 00546 { 00547 /* Lookup the FunctionEntry for the current ControlPc */ 00548 FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL); 00549 00550 /* Is this a leaf function? */ 00551 if (!FunctionEntry) 00552 { 00553 Context.Rip = *(DWORD64*)Context.Rsp; 00554 Context.Rsp += sizeof(DWORD64); 00555 DPRINT("leaf funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); 00556 } 00557 else 00558 { 00559 RtlVirtualUnwind(0, 00560 ImageBase, 00561 ControlPc, 00562 FunctionEntry, 00563 &Context, 00564 &HandlerData, 00565 &EstablisherFrame, 00566 NULL); 00567 DPRINT("normal funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); 00568 } 00569 00570 /* Check if new Rip is valid */ 00571 if (!Context.Rip) 00572 { 00573 break; 00574 } 00575 00576 /* Check, if we have left our stack */ 00577 if ((Context.Rsp < StackLow) || (Context.Rsp > StackHigh)) 00578 { 00579 break; 00580 } 00581 00582 /* Save this frame and continue with new Rip */ 00583 ControlPc = Context.Rip; 00584 Callers[i] = (PVOID)ControlPc; 00585 } 00586 00587 DPRINT("RtlWalkFrameChain returns %ld\n", i); 00588 return i; 00589 } 00590 00594 #undef RtlGetCallersAddress 00595 VOID 00596 NTAPI 00597 RtlGetCallersAddress( 00598 OUT PVOID *CallersAddress, 00599 OUT PVOID *CallersCaller ) 00600 { 00601 PVOID Callers[4]; 00602 ULONG Number; 00603 00604 /* Get callers: 00605 * RtlWalkFrameChain -> RtlGetCallersAddress -> x -> y */ 00606 Number = RtlWalkFrameChain(Callers, 4, 0); 00607 00608 if (CallersAddress) 00609 { 00610 *CallersAddress = (Number >= 3) ? Callers[2] : NULL; 00611 } 00612 if (CallersCaller) 00613 { 00614 *CallersCaller = (Number == 4) ? Callers[3] : NULL; 00615 } 00616 00617 return; 00618 } 00619 00620 // FIXME: move to different file 00621 VOID 00622 NTAPI 00623 RtlRaiseException(IN PEXCEPTION_RECORD ExceptionRecord) 00624 { 00625 CONTEXT Context; 00626 NTSTATUS Status = STATUS_INVALID_DISPOSITION; 00627 ULONG64 ImageBase; 00628 PRUNTIME_FUNCTION FunctionEntry; 00629 PVOID HandlerData; 00630 ULONG64 EstablisherFrame; 00631 00632 /* Capture the context */ 00633 RtlCaptureContext(&Context); 00634 00635 /* Get the function entry for this function */ 00636 FunctionEntry = RtlLookupFunctionEntry(Context.Rip, 00637 &ImageBase, 00638 NULL); 00639 00640 /* Check if we found it */ 00641 if (FunctionEntry) 00642 { 00643 /* Unwind to the caller of this function */ 00644 RtlVirtualUnwind(UNW_FLAG_NHANDLER, 00645 ImageBase, 00646 Context.Rip, 00647 FunctionEntry, 00648 &Context, 00649 &HandlerData, 00650 &EstablisherFrame, 00651 NULL); 00652 00653 /* Save the exception address */ 00654 ExceptionRecord->ExceptionAddress = (PVOID)Context.Rip; 00655 00656 /* Write the context flag */ 00657 Context.ContextFlags = CONTEXT_FULL; 00658 00659 /* Check if user mode debugger is active */ 00660 if (RtlpCheckForActiveDebugger()) 00661 { 00662 /* Raise an exception immediately */ 00663 Status = ZwRaiseException(ExceptionRecord, &Context, TRUE); 00664 } 00665 else 00666 { 00667 /* Dispatch the exception and check if we should continue */ 00668 if (!RtlDispatchException(ExceptionRecord, &Context)) 00669 { 00670 /* Raise the exception */ 00671 Status = ZwRaiseException(ExceptionRecord, &Context, FALSE); 00672 } 00673 else 00674 { 00675 /* Continue, go back to previous context */ 00676 Status = ZwContinue(&Context, FALSE); 00677 } 00678 } 00679 } 00680 00681 /* If we returned, raise a status */ 00682 RtlRaiseStatus(Status); 00683 } 00684 Generated on Sat May 26 2012 04:35:20 for ReactOS by
1.7.6.1
|