1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * PURPOSE: Unwinding related functions 5 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org) 6 */ 7 8 /* INCLUDES *****************************************************************/ 9 10 #include <rtl.h> 11 12 #define NDEBUG 13 #include <debug.h> 14 15 #define UNWIND_HISTORY_TABLE_NONE 0 16 #define UNWIND_HISTORY_TABLE_GLOBAL 1 17 #define UNWIND_HISTORY_TABLE_LOCAL 2 18 19 #define UWOP_PUSH_NONVOL 0 20 #define UWOP_ALLOC_LARGE 1 21 #define UWOP_ALLOC_SMALL 2 22 #define UWOP_SET_FPREG 3 23 #define UWOP_SAVE_NONVOL 4 24 #define UWOP_SAVE_NONVOL_FAR 5 25 #if 0 // These are deprecated / not for x64 26 #define UWOP_SAVE_XMM 6 27 #define UWOP_SAVE_XMM_FAR 7 28 #else 29 #define UWOP_EPILOG 6 30 #define UWOP_SPARE_CODE 7 31 #endif 32 #define UWOP_SAVE_XMM128 8 33 #define UWOP_SAVE_XMM128_FAR 9 34 #define UWOP_PUSH_MACHFRAME 10 35 36 37 typedef unsigned char UBYTE; 38 39 typedef union _UNWIND_CODE 40 { 41 struct 42 { 43 UBYTE CodeOffset; 44 UBYTE UnwindOp:4; 45 UBYTE OpInfo:4; 46 }; 47 USHORT FrameOffset; 48 } UNWIND_CODE, *PUNWIND_CODE; 49 50 typedef struct _UNWIND_INFO 51 { 52 UBYTE Version:3; 53 UBYTE Flags:5; 54 UBYTE SizeOfProlog; 55 UBYTE CountOfCodes; 56 UBYTE FrameRegister:4; 57 UBYTE FrameOffset:4; 58 UNWIND_CODE UnwindCode[1]; 59 /* union { 60 OPTIONAL ULONG ExceptionHandler; 61 OPTIONAL ULONG FunctionEntry; 62 }; 63 OPTIONAL ULONG ExceptionData[]; 64 */ 65 } UNWIND_INFO, *PUNWIND_INFO; 66 67 /* FUNCTIONS *****************************************************************/ 68 69 /*! RtlLookupFunctionTable 70 * \brief Locates the table of RUNTIME_FUNCTION entries for a code address. 71 * \param ControlPc 72 * Address of the code, for which the table should be searched. 73 * \param ImageBase 74 * Pointer to a DWORD64 that receives the base address of the 75 * corresponding executable image. 76 * \param Length 77 * Pointer to an ULONG that receives the number of table entries 78 * present in the table. 79 */ 80 PRUNTIME_FUNCTION 81 NTAPI 82 RtlLookupFunctionTable( 83 IN DWORD64 ControlPc, 84 OUT PDWORD64 ImageBase, 85 OUT PULONG Length) 86 { 87 PVOID Table; 88 ULONG Size; 89 90 /* Find corresponding file header from code address */ 91 if (!RtlPcToFileHeader((PVOID)ControlPc, (PVOID*)ImageBase)) 92 { 93 /* Nothing found */ 94 return NULL; 95 } 96 97 /* Locate the exception directory */ 98 Table = RtlImageDirectoryEntryToData((PVOID)*ImageBase, 99 TRUE, 100 IMAGE_DIRECTORY_ENTRY_EXCEPTION, 101 &Size); 102 103 /* Return the number of entries */ 104 *Length = Size / sizeof(RUNTIME_FUNCTION); 105 106 /* Return the address of the table */ 107 return Table; 108 } 109 110 /*! RtlLookupFunctionEntry 111 * \brief Locates the RUNTIME_FUNCTION entry corresponding to a code address. 112 * \ref http://msdn.microsoft.com/en-us/library/ms680597(VS.85).aspx 113 * \todo Implement HistoryTable 114 */ 115 PRUNTIME_FUNCTION 116 NTAPI 117 RtlLookupFunctionEntry( 118 IN DWORD64 ControlPc, 119 OUT PDWORD64 ImageBase, 120 OUT PUNWIND_HISTORY_TABLE HistoryTable) 121 { 122 PRUNTIME_FUNCTION FunctionTable, FunctionEntry; 123 ULONG TableLength; 124 ULONG IndexLo, IndexHi, IndexMid; 125 126 /* Find the corresponding table */ 127 FunctionTable = RtlLookupFunctionTable(ControlPc, ImageBase, &TableLength); 128 129 /* Fail, if no table is found */ 130 if (!FunctionTable) 131 { 132 return NULL; 133 } 134 135 /* Use relative virtual address */ 136 ControlPc -= *ImageBase; 137 138 /* Do a binary search */ 139 IndexLo = 0; 140 IndexHi = TableLength; 141 while (IndexHi > IndexLo) 142 { 143 IndexMid = (IndexLo + IndexHi) / 2; 144 FunctionEntry = &FunctionTable[IndexMid]; 145 146 if (ControlPc < FunctionEntry->BeginAddress) 147 { 148 /* Continue search in lower half */ 149 IndexHi = IndexMid; 150 } 151 else if (ControlPc >= FunctionEntry->EndAddress) 152 { 153 /* Continue search in upper half */ 154 IndexLo = IndexMid + 1; 155 } 156 else 157 { 158 /* ControlPc is within limits, return entry */ 159 return FunctionEntry; 160 } 161 } 162 163 /* Nothing found, return NULL */ 164 return NULL; 165 } 166 167 BOOLEAN 168 NTAPI 169 RtlAddFunctionTable( 170 IN PRUNTIME_FUNCTION FunctionTable, 171 IN DWORD EntryCount, 172 IN DWORD64 BaseAddress) 173 { 174 UNIMPLEMENTED; 175 return FALSE; 176 } 177 178 BOOLEAN 179 NTAPI 180 RtlDeleteFunctionTable( 181 IN PRUNTIME_FUNCTION FunctionTable) 182 { 183 UNIMPLEMENTED; 184 return FALSE; 185 } 186 187 BOOLEAN 188 NTAPI 189 RtlInstallFunctionTableCallback( 190 IN DWORD64 TableIdentifier, 191 IN DWORD64 BaseAddress, 192 IN DWORD Length, 193 IN PGET_RUNTIME_FUNCTION_CALLBACK Callback, 194 IN PVOID Context, 195 IN PCWSTR OutOfProcessCallbackDll) 196 { 197 UNIMPLEMENTED; 198 return FALSE; 199 } 200 201 static 202 __inline 203 ULONG 204 UnwindOpSlots( 205 _In_ UNWIND_CODE UnwindCode) 206 { 207 static const UCHAR UnwindOpExtraSlotTable[] = 208 { 209 0, // UWOP_PUSH_NONVOL 210 1, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code) 211 0, // UWOP_ALLOC_SMALL 212 0, // UWOP_SET_FPREG 213 1, // UWOP_SAVE_NONVOL 214 2, // UWOP_SAVE_NONVOL_FAR 215 1, // UWOP_EPILOG // previously UWOP_SAVE_XMM 216 2, // UWOP_SPARE_CODE // previously UWOP_SAVE_XMM_FAR 217 1, // UWOP_SAVE_XMM128 218 2, // UWOP_SAVE_XMM128_FAR 219 0, // UWOP_PUSH_MACHFRAME 220 2, // UWOP_SET_FPREG_LARGE 221 }; 222 223 if ((UnwindCode.UnwindOp == UWOP_ALLOC_LARGE) && 224 (UnwindCode.OpInfo != 0)) 225 { 226 return 3; 227 } 228 else 229 { 230 return UnwindOpExtraSlotTable[UnwindCode.UnwindOp] + 1; 231 } 232 } 233 234 static 235 __inline 236 void 237 SetReg( 238 _Inout_ PCONTEXT Context, 239 _In_ BYTE Reg, 240 _In_ DWORD64 Value) 241 { 242 ((DWORD64*)(&Context->Rax))[Reg] = Value; 243 } 244 245 static 246 __inline 247 void 248 SetRegFromStackValue( 249 _Inout_ PCONTEXT Context, 250 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 251 _In_ BYTE Reg, 252 _In_ PDWORD64 ValuePointer) 253 { 254 SetReg(Context, Reg, *ValuePointer); 255 if (ContextPointers != NULL) 256 { 257 ContextPointers->IntegerContext[Reg] = ValuePointer; 258 } 259 } 260 261 static 262 __inline 263 DWORD64 264 GetReg( 265 _In_ PCONTEXT Context, 266 _In_ BYTE Reg) 267 { 268 return ((DWORD64*)(&Context->Rax))[Reg]; 269 } 270 271 static 272 __inline 273 void 274 PopReg( 275 _Inout_ PCONTEXT Context, 276 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 277 _In_ BYTE Reg) 278 { 279 SetRegFromStackValue(Context, ContextPointers, Reg, (PDWORD64)Context->Rsp); 280 Context->Rsp += sizeof(DWORD64); 281 } 282 283 static 284 __inline 285 void 286 SetXmmReg( 287 _Inout_ PCONTEXT Context, 288 _In_ BYTE Reg, 289 _In_ M128A Value) 290 { 291 ((M128A*)(&Context->Xmm0))[Reg] = Value; 292 } 293 294 static 295 __inline 296 void 297 SetXmmRegFromStackValue( 298 _Out_ PCONTEXT Context, 299 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 300 _In_ BYTE Reg, 301 _In_ M128A *ValuePointer) 302 { 303 SetXmmReg(Context, Reg, *ValuePointer); 304 if (ContextPointers != NULL) 305 { 306 ContextPointers->FloatingContext[Reg] = ValuePointer; 307 } 308 } 309 310 static 311 __inline 312 M128A 313 GetXmmReg(PCONTEXT Context, BYTE Reg) 314 { 315 return ((M128A*)(&Context->Xmm0))[Reg]; 316 } 317 318 /*! RtlpTryToUnwindEpilog 319 * \brief Helper function that tries to unwind epilog instructions. 320 * \return TRUE if we have been in an epilog and it could be unwound. 321 * FALSE if the instructions were not allowed for an epilog. 322 * \ref 323 * https://docs.microsoft.com/en-us/cpp/build/unwind-procedure 324 * https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog 325 * \todo 326 * - Test and compare with Windows behaviour 327 */ 328 static 329 __inline 330 BOOLEAN 331 RtlpTryToUnwindEpilog( 332 _Inout_ PCONTEXT Context, 333 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 334 _In_ ULONG64 ImageBase, 335 _In_ PRUNTIME_FUNCTION FunctionEntry) 336 { 337 CONTEXT LocalContext; 338 BYTE *InstrPtr; 339 DWORD Instr; 340 BYTE Reg, Mod; 341 ULONG64 EndAddress; 342 343 /* Make a local copy of the context */ 344 LocalContext = *Context; 345 346 InstrPtr = (BYTE*)LocalContext.Rip; 347 348 /* Check if first instruction of epilog is "add rsp, x" */ 349 Instr = *(DWORD*)InstrPtr; 350 if ( (Instr & 0x00fffdff) == 0x00c48148 ) 351 { 352 if ( (Instr & 0x0000ff00) == 0x8300 ) 353 { 354 /* This is "add rsp, 0x??" */ 355 LocalContext.Rsp += Instr >> 24; 356 InstrPtr += 4; 357 } 358 else 359 { 360 /* This is "add rsp, 0x???????? */ 361 LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); 362 InstrPtr += 7; 363 } 364 } 365 /* Check if first instruction of epilog is "lea rsp, ..." */ 366 else if ( (Instr & 0x38fffe) == 0x208d48 ) 367 { 368 /* Get the register */ 369 Reg = ((Instr << 8) | (Instr >> 16)) & 0x7; 370 371 LocalContext.Rsp = GetReg(&LocalContext, Reg); 372 373 /* Get adressing mode */ 374 Mod = (Instr >> 22) & 0x3; 375 if (Mod == 0) 376 { 377 /* No displacement */ 378 InstrPtr += 3; 379 } 380 else if (Mod == 1) 381 { 382 /* 1 byte displacement */ 383 LocalContext.Rsp += Instr >> 24; 384 InstrPtr += 4; 385 } 386 else if (Mod == 2) 387 { 388 /* 4 bytes displacement */ 389 LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); 390 InstrPtr += 7; 391 } 392 } 393 394 /* Loop the following instructions before the ret */ 395 EndAddress = FunctionEntry->EndAddress + ImageBase - 1; 396 while ((DWORD64)InstrPtr < EndAddress) 397 { 398 Instr = *(DWORD*)InstrPtr; 399 400 /* Check for a simple pop */ 401 if ( (Instr & 0xf8) == 0x58 ) 402 { 403 /* Opcode pops a basic register from stack */ 404 Reg = Instr & 0x7; 405 PopReg(&LocalContext, ContextPointers, Reg); 406 InstrPtr++; 407 continue; 408 } 409 410 /* Check for REX + pop */ 411 if ( (Instr & 0xf8fb) == 0x5841 ) 412 { 413 /* Opcode is pop r8 .. r15 */ 414 Reg = ((Instr >> 8) & 0x7) + 8; 415 PopReg(&LocalContext, ContextPointers, Reg); 416 InstrPtr += 2; 417 continue; 418 } 419 420 /* Opcode not allowed for Epilog */ 421 return FALSE; 422 } 423 424 // check for popfq 425 426 // also allow end with jmp imm, jmp [target], iretq 427 428 /* Check if we are at the ret instruction */ 429 if ((DWORD64)InstrPtr != EndAddress) 430 { 431 /* If we went past the end of the function, something is broken! */ 432 ASSERT((DWORD64)InstrPtr <= EndAddress); 433 return FALSE; 434 } 435 436 /* Make sure this is really a ret instruction */ 437 if (*InstrPtr != 0xc3) 438 { 439 ASSERT(FALSE); 440 return FALSE; 441 } 442 443 /* Unwind is finished, pop new Rip from Stack */ 444 LocalContext.Rip = *(DWORD64*)LocalContext.Rsp; 445 LocalContext.Rsp += sizeof(DWORD64); 446 447 *Context = LocalContext; 448 return TRUE; 449 } 450 451 /*! 452 453 \ref https://docs.microsoft.com/en-us/cpp/build/unwind-data-definitions-in-c 454 */ 455 static 456 ULONG64 457 GetEstablisherFrame( 458 _In_ PCONTEXT Context, 459 _In_ PUNWIND_INFO UnwindInfo, 460 _In_ ULONG_PTR CodeOffset) 461 { 462 ULONG i; 463 464 /* Check if we have a frame register */ 465 if (UnwindInfo->FrameRegister == 0) 466 { 467 /* No frame register means we use Rsp */ 468 return Context->Rsp; 469 } 470 471 if ((CodeOffset >= UnwindInfo->SizeOfProlog) || 472 ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)) 473 { 474 return GetReg(Context, UnwindInfo->FrameRegister) - 475 UnwindInfo->FrameOffset * 16; 476 } 477 478 /* Loop all unwind ops */ 479 for (i = 0; 480 i < UnwindInfo->CountOfCodes; 481 i += UnwindOpSlots(UnwindInfo->UnwindCode[i])) 482 { 483 /* Check for SET_FPREG */ 484 if (UnwindInfo->UnwindCode[i].UnwindOp == UWOP_SET_FPREG) 485 { 486 return GetReg(Context, UnwindInfo->FrameRegister) - 487 UnwindInfo->FrameOffset * 16; 488 } 489 } 490 491 return Context->Rsp; 492 } 493 494 PEXCEPTION_ROUTINE 495 NTAPI 496 RtlVirtualUnwind( 497 _In_ ULONG HandlerType, 498 _In_ ULONG64 ImageBase, 499 _In_ ULONG64 ControlPc, 500 _In_ PRUNTIME_FUNCTION FunctionEntry, 501 _Inout_ PCONTEXT Context, 502 _Outptr_ PVOID *HandlerData, 503 _Out_ PULONG64 EstablisherFrame, 504 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers) 505 { 506 PUNWIND_INFO UnwindInfo; 507 ULONG_PTR CodeOffset; 508 ULONG i, Offset; 509 UNWIND_CODE UnwindCode; 510 BYTE Reg; 511 PULONG LanguageHandler; 512 513 /* Use relative virtual address */ 514 ControlPc -= ImageBase; 515 516 /* Sanity checks */ 517 if ( (ControlPc < FunctionEntry->BeginAddress) || 518 (ControlPc >= FunctionEntry->EndAddress) ) 519 { 520 return NULL; 521 } 522 523 /* Get a pointer to the unwind info */ 524 UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData); 525 526 /* Check for chained info */ 527 if (UnwindInfo->Flags & UNW_FLAG_CHAININFO) 528 { 529 UNIMPLEMENTED_DBGBREAK(); 530 531 /* See https://docs.microsoft.com/en-us/cpp/build/chained-unwind-info-structures */ 532 FunctionEntry = (PRUNTIME_FUNCTION)&(UnwindInfo->UnwindCode[(UnwindInfo->CountOfCodes + 1) & ~1]); 533 UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData); 534 } 535 536 /* The language specific handler data follows the unwind info */ 537 LanguageHandler = ALIGN_UP_POINTER_BY(&UnwindInfo->UnwindCode[UnwindInfo->CountOfCodes], sizeof(ULONG)); 538 *HandlerData = (LanguageHandler + 1); 539 540 /* Calculate relative offset to function start */ 541 CodeOffset = ControlPc - FunctionEntry->BeginAddress; 542 543 *EstablisherFrame = GetEstablisherFrame(Context, UnwindInfo, CodeOffset); 544 545 /* Check if we are in the function epilog and try to finish it */ 546 if (CodeOffset > UnwindInfo->SizeOfProlog) 547 { 548 if (RtlpTryToUnwindEpilog(Context, ContextPointers, ImageBase, FunctionEntry)) 549 { 550 /* There's no exception routine */ 551 return NULL; 552 } 553 } 554 555 /* Skip all Ops with an offset greater than the current Offset */ 556 i = 0; 557 while ((i < UnwindInfo->CountOfCodes) && 558 (UnwindInfo->UnwindCode[i].CodeOffset > CodeOffset)) 559 { 560 i += UnwindOpSlots(UnwindInfo->UnwindCode[i]); 561 } 562 563 /* Process the remaining unwind ops */ 564 while (i < UnwindInfo->CountOfCodes) 565 { 566 UnwindCode = UnwindInfo->UnwindCode[i]; 567 switch (UnwindCode.UnwindOp) 568 { 569 case UWOP_PUSH_NONVOL: 570 Reg = UnwindCode.OpInfo; 571 PopReg(Context, ContextPointers, Reg); 572 i++; 573 break; 574 575 case UWOP_ALLOC_LARGE: 576 if (UnwindCode.OpInfo) 577 { 578 Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i+1]); 579 Context->Rsp += Offset; 580 i += 3; 581 } 582 else 583 { 584 Offset = UnwindInfo->UnwindCode[i+1].FrameOffset; 585 Context->Rsp += Offset * 8; 586 i += 2; 587 } 588 break; 589 590 case UWOP_ALLOC_SMALL: 591 Context->Rsp += (UnwindCode.OpInfo + 1) * 8; 592 i++; 593 break; 594 595 case UWOP_SET_FPREG: 596 Reg = UnwindInfo->FrameRegister; 597 Context->Rsp = GetReg(Context, Reg) - UnwindInfo->FrameOffset * 16; 598 i++; 599 break; 600 601 case UWOP_SAVE_NONVOL: 602 Reg = UnwindCode.OpInfo; 603 Offset = *(USHORT*)(&UnwindInfo->UnwindCode[i + 1]); 604 SetRegFromStackValue(Context, ContextPointers, Reg, (DWORD64*)Context->Rsp + Offset); 605 i += 2; 606 break; 607 608 case UWOP_SAVE_NONVOL_FAR: 609 Reg = UnwindCode.OpInfo; 610 Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i + 1]); 611 SetRegFromStackValue(Context, ContextPointers, Reg, (DWORD64*)Context->Rsp + Offset); 612 i += 3; 613 break; 614 615 case UWOP_EPILOG: 616 i += 1; 617 break; 618 619 case UWOP_SPARE_CODE: 620 ASSERT(FALSE); 621 i += 2; 622 break; 623 624 case UWOP_SAVE_XMM128: 625 Reg = UnwindCode.OpInfo; 626 Offset = *(USHORT*)(&UnwindInfo->UnwindCode[i + 1]); 627 SetXmmRegFromStackValue(Context, ContextPointers, Reg, (M128A*)(Context->Rsp + Offset)); 628 i += 2; 629 break; 630 631 case UWOP_SAVE_XMM128_FAR: 632 Reg = UnwindCode.OpInfo; 633 Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i + 1]); 634 SetXmmRegFromStackValue(Context, ContextPointers, Reg, (M128A*)(Context->Rsp + Offset)); 635 i += 3; 636 break; 637 638 case UWOP_PUSH_MACHFRAME: 639 /* OpInfo is 1, when an error code was pushed, otherwise 0. */ 640 Context->Rsp += UnwindCode.OpInfo * sizeof(DWORD64); 641 642 /* Now pop the MACHINE_FRAME (Yes, "magic numbers", deal with it) */ 643 Context->Rip = *(PDWORD64)(Context->Rsp + 0x00); 644 Context->SegCs = *(PDWORD64)(Context->Rsp + 0x08); 645 Context->EFlags = *(PDWORD64)(Context->Rsp + 0x10); 646 Context->SegSs = *(PDWORD64)(Context->Rsp + 0x20); 647 Context->Rsp = *(PDWORD64)(Context->Rsp + 0x18); 648 ASSERT((i + 1) == UnwindInfo->CountOfCodes); 649 goto Exit; 650 } 651 } 652 653 /* Unwind is finished, pop new Rip from Stack */ 654 if (Context->Rsp != 0) 655 { 656 Context->Rip = *(DWORD64*)Context->Rsp; 657 Context->Rsp += sizeof(DWORD64); 658 } 659 660 Exit: 661 662 /* Check if we have a handler and return it */ 663 if (UnwindInfo->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) 664 { 665 return RVA(ImageBase, *LanguageHandler); 666 } 667 668 return NULL; 669 } 670 671 VOID 672 NTAPI 673 RtlUnwindEx( 674 _In_opt_ PVOID TargetFrame, 675 _In_opt_ PVOID TargetIp, 676 _In_opt_ PEXCEPTION_RECORD ExceptionRecord, 677 _In_ PVOID ReturnValue, 678 _In_ PCONTEXT ContextRecord, 679 _In_opt_ struct _UNWIND_HISTORY_TABLE *HistoryTable) 680 { 681 __debugbreak(); 682 return; 683 } 684 685 VOID 686 NTAPI 687 RtlUnwind( 688 IN PVOID TargetFrame, 689 IN PVOID TargetIp, 690 IN PEXCEPTION_RECORD ExceptionRecord, 691 IN PVOID ReturnValue) 692 { 693 UNIMPLEMENTED; 694 return; 695 } 696 697 ULONG 698 NTAPI 699 RtlWalkFrameChain(OUT PVOID *Callers, 700 IN ULONG Count, 701 IN ULONG Flags) 702 { 703 CONTEXT Context; 704 ULONG64 ControlPc, ImageBase, EstablisherFrame; 705 ULONG64 StackLow, StackHigh; 706 PVOID HandlerData; 707 ULONG i, FramesToSkip; 708 PRUNTIME_FUNCTION FunctionEntry; 709 710 DPRINT("Enter RtlWalkFrameChain\n"); 711 712 /* The upper bits in Flags define how many frames to skip */ 713 FramesToSkip = Flags >> 8; 714 715 /* Capture the current Context */ 716 RtlCaptureContext(&Context); 717 ControlPc = Context.Rip; 718 719 /* Get the stack limits */ 720 RtlpGetStackLimits(&StackLow, &StackHigh); 721 722 /* Check if we want the user-mode stack frame */ 723 if (Flags & 1) 724 { 725 } 726 727 /* Loop the frames */ 728 for (i = 0; i < FramesToSkip + Count; i++) 729 { 730 /* Lookup the FunctionEntry for the current ControlPc */ 731 FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL); 732 733 /* Is this a leaf function? */ 734 if (!FunctionEntry) 735 { 736 Context.Rip = *(DWORD64*)Context.Rsp; 737 Context.Rsp += sizeof(DWORD64); 738 DPRINT("leaf funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); 739 } 740 else 741 { 742 RtlVirtualUnwind(0, 743 ImageBase, 744 ControlPc, 745 FunctionEntry, 746 &Context, 747 &HandlerData, 748 &EstablisherFrame, 749 NULL); 750 DPRINT("normal funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); 751 } 752 753 /* Check if new Rip is valid */ 754 if (!Context.Rip) 755 { 756 break; 757 } 758 759 /* Check, if we have left our stack */ 760 if ((Context.Rsp < StackLow) || (Context.Rsp > StackHigh)) 761 { 762 break; 763 } 764 765 /* Continue with new Rip */ 766 ControlPc = Context.Rip; 767 768 /* Save value, if we are past the frames to skip */ 769 if (i >= FramesToSkip) 770 { 771 Callers[i - FramesToSkip] = (PVOID)ControlPc; 772 } 773 } 774 775 DPRINT("RtlWalkFrameChain returns %ld\n", i); 776 return i; 777 } 778 779 /*! RtlGetCallersAddress 780 * \ref http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Debug/RtlGetCallersAddress.html 781 */ 782 #undef RtlGetCallersAddress 783 VOID 784 NTAPI 785 RtlGetCallersAddress( 786 OUT PVOID *CallersAddress, 787 OUT PVOID *CallersCaller ) 788 { 789 PVOID Callers[4]; 790 ULONG Number; 791 792 /* Get callers: 793 * RtlWalkFrameChain -> RtlGetCallersAddress -> x -> y */ 794 Number = RtlWalkFrameChain(Callers, 4, 0); 795 796 *CallersAddress = (Number >= 3) ? Callers[2] : NULL; 797 *CallersCaller = (Number == 4) ? Callers[3] : NULL; 798 799 return; 800 } 801 802 static 803 VOID 804 RtlpCaptureNonVolatileContextPointers( 805 _Out_ PKNONVOLATILE_CONTEXT_POINTERS NonvolatileContextPointers, 806 _In_ ULONG64 TargetFrame) 807 { 808 CONTEXT Context; 809 PRUNTIME_FUNCTION FunctionEntry; 810 ULONG64 ImageBase; 811 PVOID HandlerData; 812 ULONG64 EstablisherFrame; 813 814 /* Zero out the nonvolatile context pointers */ 815 RtlZeroMemory(NonvolatileContextPointers, sizeof(*NonvolatileContextPointers)); 816 817 /* Capture the current context */ 818 RtlCaptureContext(&Context); 819 820 do 821 { 822 /* Look up the function entry */ 823 FunctionEntry = RtlLookupFunctionEntry(Context.Rip, &ImageBase, NULL); 824 ASSERT(FunctionEntry != NULL); 825 826 /* Do a virtual unwind to the caller and capture saved non-volatiles */ 827 RtlVirtualUnwind(UNW_FLAG_EHANDLER, 828 ImageBase, 829 Context.Rip, 830 FunctionEntry, 831 &Context, 832 &HandlerData, 833 &EstablisherFrame, 834 NonvolatileContextPointers); 835 836 /* Make sure nothing fishy is going on. Currently this is for kernel mode only. */ 837 ASSERT(EstablisherFrame != 0); 838 ASSERT((LONG64)Context.Rip < 0); 839 840 /* Continue until we reached the target frame or user mode */ 841 } while (EstablisherFrame < TargetFrame); 842 843 /* If the caller did the right thing, we should get exactly the target frame */ 844 ASSERT(EstablisherFrame == TargetFrame); 845 } 846 847 VOID 848 RtlSetUnwindContext( 849 _In_ PCONTEXT Context, 850 _In_ DWORD64 TargetFrame) 851 { 852 KNONVOLATILE_CONTEXT_POINTERS ContextPointers; 853 854 /* Capture pointers to the non-volatiles up to the target frame */ 855 RtlpCaptureNonVolatileContextPointers(&ContextPointers, TargetFrame); 856 857 /* Copy the nonvolatiles to the captured locations */ 858 *ContextPointers.R12 = Context->R12; 859 *ContextPointers.R13 = Context->R13; 860 *ContextPointers.R14 = Context->R14; 861 *ContextPointers.R15 = Context->R15; 862 *ContextPointers.Xmm6 = Context->Xmm6; 863 *ContextPointers.Xmm7 = Context->Xmm7; 864 *ContextPointers.Xmm8 = Context->Xmm8; 865 *ContextPointers.Xmm9 = Context->Xmm9; 866 *ContextPointers.Xmm10 = Context->Xmm10; 867 *ContextPointers.Xmm11 = Context->Xmm11; 868 *ContextPointers.Xmm12 = Context->Xmm12; 869 *ContextPointers.Xmm13 = Context->Xmm13; 870 *ContextPointers.Xmm14 = Context->Xmm14; 871 *ContextPointers.Xmm15 = Context->Xmm15; 872 } 873