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 PRUNTIME_FUNCTION 111 NTAPI 112 RtlpLookupDynamicFunctionEntry( 113 _In_ DWORD64 ControlPc, 114 _Out_ PDWORD64 ImageBase, 115 _In_ PUNWIND_HISTORY_TABLE HistoryTable); 116 117 /*! RtlLookupFunctionEntry 118 * \brief Locates the RUNTIME_FUNCTION entry corresponding to a code address. 119 * \ref http://msdn.microsoft.com/en-us/library/ms680597(VS.85).aspx 120 * \todo Implement HistoryTable 121 */ 122 PRUNTIME_FUNCTION 123 NTAPI 124 RtlLookupFunctionEntry( 125 IN DWORD64 ControlPc, 126 OUT PDWORD64 ImageBase, 127 OUT PUNWIND_HISTORY_TABLE HistoryTable) 128 { 129 PRUNTIME_FUNCTION FunctionTable, FunctionEntry; 130 ULONG TableLength; 131 ULONG IndexLo, IndexHi, IndexMid; 132 133 /* Find the corresponding table */ 134 FunctionTable = RtlLookupFunctionTable(ControlPc, ImageBase, &TableLength); 135 136 /* If no table is found, try dynamic function tables */ 137 if (!FunctionTable) 138 { 139 return RtlpLookupDynamicFunctionEntry(ControlPc, ImageBase, HistoryTable); 140 } 141 142 /* Use relative virtual address */ 143 ControlPc -= *ImageBase; 144 145 /* Do a binary search */ 146 IndexLo = 0; 147 IndexHi = TableLength; 148 while (IndexHi > IndexLo) 149 { 150 IndexMid = (IndexLo + IndexHi) / 2; 151 FunctionEntry = &FunctionTable[IndexMid]; 152 153 if (ControlPc < FunctionEntry->BeginAddress) 154 { 155 /* Continue search in lower half */ 156 IndexHi = IndexMid; 157 } 158 else if (ControlPc >= FunctionEntry->EndAddress) 159 { 160 /* Continue search in upper half */ 161 IndexLo = IndexMid + 1; 162 } 163 else 164 { 165 /* ControlPc is within limits, return entry */ 166 return FunctionEntry; 167 } 168 } 169 170 /* Nothing found, return NULL */ 171 return NULL; 172 } 173 174 static 175 __inline 176 ULONG 177 UnwindOpSlots( 178 _In_ UNWIND_CODE UnwindCode) 179 { 180 static const UCHAR UnwindOpExtraSlotTable[] = 181 { 182 0, // UWOP_PUSH_NONVOL 183 1, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code) 184 0, // UWOP_ALLOC_SMALL 185 0, // UWOP_SET_FPREG 186 1, // UWOP_SAVE_NONVOL 187 2, // UWOP_SAVE_NONVOL_FAR 188 1, // UWOP_EPILOG // previously UWOP_SAVE_XMM 189 2, // UWOP_SPARE_CODE // previously UWOP_SAVE_XMM_FAR 190 1, // UWOP_SAVE_XMM128 191 2, // UWOP_SAVE_XMM128_FAR 192 0, // UWOP_PUSH_MACHFRAME 193 2, // UWOP_SET_FPREG_LARGE 194 }; 195 196 if ((UnwindCode.UnwindOp == UWOP_ALLOC_LARGE) && 197 (UnwindCode.OpInfo != 0)) 198 { 199 return 3; 200 } 201 else 202 { 203 return UnwindOpExtraSlotTable[UnwindCode.UnwindOp] + 1; 204 } 205 } 206 207 static 208 __inline 209 void 210 SetReg( 211 _Inout_ PCONTEXT Context, 212 _In_ BYTE Reg, 213 _In_ DWORD64 Value) 214 { 215 ((DWORD64*)(&Context->Rax))[Reg] = Value; 216 } 217 218 static 219 __inline 220 void 221 SetRegFromStackValue( 222 _Inout_ PCONTEXT Context, 223 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 224 _In_ BYTE Reg, 225 _In_ PDWORD64 ValuePointer) 226 { 227 SetReg(Context, Reg, *ValuePointer); 228 if (ContextPointers != NULL) 229 { 230 ContextPointers->IntegerContext[Reg] = ValuePointer; 231 } 232 } 233 234 static 235 __inline 236 DWORD64 237 GetReg( 238 _In_ PCONTEXT Context, 239 _In_ BYTE Reg) 240 { 241 return ((DWORD64*)(&Context->Rax))[Reg]; 242 } 243 244 static 245 __inline 246 void 247 PopReg( 248 _Inout_ PCONTEXT Context, 249 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 250 _In_ BYTE Reg) 251 { 252 SetRegFromStackValue(Context, ContextPointers, Reg, (PDWORD64)Context->Rsp); 253 Context->Rsp += sizeof(DWORD64); 254 } 255 256 static 257 __inline 258 void 259 SetXmmReg( 260 _Inout_ PCONTEXT Context, 261 _In_ BYTE Reg, 262 _In_ M128A Value) 263 { 264 ((M128A*)(&Context->Xmm0))[Reg] = Value; 265 } 266 267 static 268 __inline 269 void 270 SetXmmRegFromStackValue( 271 _Out_ PCONTEXT Context, 272 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 273 _In_ BYTE Reg, 274 _In_ M128A *ValuePointer) 275 { 276 SetXmmReg(Context, Reg, *ValuePointer); 277 if (ContextPointers != NULL) 278 { 279 ContextPointers->FloatingContext[Reg] = ValuePointer; 280 } 281 } 282 283 static 284 __inline 285 M128A 286 GetXmmReg(PCONTEXT Context, BYTE Reg) 287 { 288 return ((M128A*)(&Context->Xmm0))[Reg]; 289 } 290 291 /*! RtlpTryToUnwindEpilog 292 * \brief Helper function that tries to unwind epilog instructions. 293 * \return TRUE if we have been in an epilog and it could be unwound. 294 * FALSE if the instructions were not allowed for an epilog. 295 * \ref 296 * https://docs.microsoft.com/en-us/cpp/build/unwind-procedure 297 * https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog 298 * \todo 299 * - Test and compare with Windows behaviour 300 */ 301 static 302 __inline 303 BOOLEAN 304 RtlpTryToUnwindEpilog( 305 _Inout_ PCONTEXT Context, 306 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, 307 _In_ ULONG64 ImageBase, 308 _In_ PRUNTIME_FUNCTION FunctionEntry) 309 { 310 CONTEXT LocalContext; 311 BYTE *InstrPtr; 312 DWORD Instr; 313 BYTE Reg, Mod; 314 ULONG64 EndAddress; 315 316 /* Make a local copy of the context */ 317 LocalContext = *Context; 318 319 InstrPtr = (BYTE*)LocalContext.Rip; 320 321 /* Check if first instruction of epilog is "add rsp, x" */ 322 Instr = *(DWORD*)InstrPtr; 323 if ( (Instr & 0x00fffdff) == 0x00c48148 ) 324 { 325 if ( (Instr & 0x0000ff00) == 0x8300 ) 326 { 327 /* This is "add rsp, 0x??" */ 328 LocalContext.Rsp += Instr >> 24; 329 InstrPtr += 4; 330 } 331 else 332 { 333 /* This is "add rsp, 0x???????? */ 334 LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); 335 InstrPtr += 7; 336 } 337 } 338 /* Check if first instruction of epilog is "lea rsp, ..." */ 339 else if ( (Instr & 0x38fffe) == 0x208d48 ) 340 { 341 /* Get the register */ 342 Reg = ((Instr << 8) | (Instr >> 16)) & 0x7; 343 344 LocalContext.Rsp = GetReg(&LocalContext, Reg); 345 346 /* Get adressing mode */ 347 Mod = (Instr >> 22) & 0x3; 348 if (Mod == 0) 349 { 350 /* No displacement */ 351 InstrPtr += 3; 352 } 353 else if (Mod == 1) 354 { 355 /* 1 byte displacement */ 356 LocalContext.Rsp += Instr >> 24; 357 InstrPtr += 4; 358 } 359 else if (Mod == 2) 360 { 361 /* 4 bytes displacement */ 362 LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); 363 InstrPtr += 7; 364 } 365 } 366 367 /* Loop the following instructions before the ret */ 368 EndAddress = FunctionEntry->EndAddress + ImageBase - 1; 369 while ((DWORD64)InstrPtr < EndAddress) 370 { 371 Instr = *(DWORD*)InstrPtr; 372 373 /* Check for a simple pop */ 374 if ( (Instr & 0xf8) == 0x58 ) 375 { 376 /* Opcode pops a basic register from stack */ 377 Reg = Instr & 0x7; 378 PopReg(&LocalContext, ContextPointers, Reg); 379 InstrPtr++; 380 continue; 381 } 382 383 /* Check for REX + pop */ 384 if ( (Instr & 0xf8fb) == 0x5841 ) 385 { 386 /* Opcode is pop r8 .. r15 */ 387 Reg = ((Instr >> 8) & 0x7) + 8; 388 PopReg(&LocalContext, ContextPointers, Reg); 389 InstrPtr += 2; 390 continue; 391 } 392 393 /* Opcode not allowed for Epilog */ 394 return FALSE; 395 } 396 397 // check for popfq 398 399 // also allow end with jmp imm, jmp [target], iretq 400 401 /* Check if we are at the ret instruction */ 402 if ((DWORD64)InstrPtr != EndAddress) 403 { 404 /* If we went past the end of the function, something is broken! */ 405 ASSERT((DWORD64)InstrPtr <= EndAddress); 406 return FALSE; 407 } 408 409 /* Make sure this is really a ret instruction */ 410 if (*InstrPtr != 0xc3) 411 { 412 ASSERT(FALSE); 413 return FALSE; 414 } 415 416 /* Unwind is finished, pop new Rip from Stack */ 417 LocalContext.Rip = *(DWORD64*)LocalContext.Rsp; 418 LocalContext.Rsp += sizeof(DWORD64); 419 420 *Context = LocalContext; 421 return TRUE; 422 } 423 424 /*! 425 426 \ref https://docs.microsoft.com/en-us/cpp/build/unwind-data-definitions-in-c 427 */ 428 static 429 ULONG64 430 GetEstablisherFrame( 431 _In_ PCONTEXT Context, 432 _In_ PUNWIND_INFO UnwindInfo, 433 _In_ ULONG_PTR CodeOffset) 434 { 435 ULONG i; 436 437 /* Check if we have a frame register */ 438 if (UnwindInfo->FrameRegister == 0) 439 { 440 /* No frame register means we use Rsp */ 441 return Context->Rsp; 442 } 443 444 if ((CodeOffset >= UnwindInfo->SizeOfProlog) || 445 ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)) 446 { 447 return GetReg(Context, UnwindInfo->FrameRegister) - 448 UnwindInfo->FrameOffset * 16; 449 } 450 451 /* Loop all unwind ops */ 452 for (i = 0; 453 i < UnwindInfo->CountOfCodes; 454 i += UnwindOpSlots(UnwindInfo->UnwindCode[i])) 455 { 456 /* Check for SET_FPREG */ 457 if (UnwindInfo->UnwindCode[i].UnwindOp == UWOP_SET_FPREG) 458 { 459 return GetReg(Context, UnwindInfo->FrameRegister) - 460 UnwindInfo->FrameOffset * 16; 461 } 462 } 463 464 return Context->Rsp; 465 } 466 467 PEXCEPTION_ROUTINE 468 NTAPI 469 RtlVirtualUnwind( 470 _In_ ULONG HandlerType, 471 _In_ ULONG64 ImageBase, 472 _In_ ULONG64 ControlPc, 473 _In_ PRUNTIME_FUNCTION FunctionEntry, 474 _Inout_ PCONTEXT Context, 475 _Outptr_ PVOID *HandlerData, 476 _Out_ PULONG64 EstablisherFrame, 477 _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers) 478 { 479 PUNWIND_INFO UnwindInfo; 480 ULONG_PTR CodeOffset; 481 ULONG i, Offset; 482 UNWIND_CODE UnwindCode; 483 BYTE Reg; 484 PULONG LanguageHandler; 485 486 /* Use relative virtual address */ 487 ControlPc -= ImageBase; 488 489 /* Sanity checks */ 490 if ( (ControlPc < FunctionEntry->BeginAddress) || 491 (ControlPc >= FunctionEntry->EndAddress) ) 492 { 493 return NULL; 494 } 495 496 /* Get a pointer to the unwind info */ 497 UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData); 498 499 /* The language specific handler data follows the unwind info */ 500 LanguageHandler = ALIGN_UP_POINTER_BY(&UnwindInfo->UnwindCode[UnwindInfo->CountOfCodes], sizeof(ULONG)); 501 *HandlerData = (LanguageHandler + 1); 502 503 /* Calculate relative offset to function start */ 504 CodeOffset = ControlPc - FunctionEntry->BeginAddress; 505 506 *EstablisherFrame = GetEstablisherFrame(Context, UnwindInfo, CodeOffset); 507 508 /* Check if we are in the function epilog and try to finish it */ 509 if (CodeOffset > UnwindInfo->SizeOfProlog) 510 { 511 if (RtlpTryToUnwindEpilog(Context, ContextPointers, ImageBase, FunctionEntry)) 512 { 513 /* There's no exception routine */ 514 return NULL; 515 } 516 } 517 518 /* Skip all Ops with an offset greater than the current Offset */ 519 i = 0; 520 while ((i < UnwindInfo->CountOfCodes) && 521 (UnwindInfo->UnwindCode[i].CodeOffset > CodeOffset)) 522 { 523 i += UnwindOpSlots(UnwindInfo->UnwindCode[i]); 524 } 525 526 RepeatChainedInfo: 527 528 /* Process the remaining unwind ops */ 529 while (i < UnwindInfo->CountOfCodes) 530 { 531 UnwindCode = UnwindInfo->UnwindCode[i]; 532 switch (UnwindCode.UnwindOp) 533 { 534 case UWOP_PUSH_NONVOL: 535 Reg = UnwindCode.OpInfo; 536 PopReg(Context, ContextPointers, Reg); 537 i++; 538 break; 539 540 case UWOP_ALLOC_LARGE: 541 if (UnwindCode.OpInfo) 542 { 543 Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i+1]); 544 Context->Rsp += Offset; 545 i += 3; 546 } 547 else 548 { 549 Offset = UnwindInfo->UnwindCode[i+1].FrameOffset; 550 Context->Rsp += Offset * 8; 551 i += 2; 552 } 553 break; 554 555 case UWOP_ALLOC_SMALL: 556 Context->Rsp += (UnwindCode.OpInfo + 1) * 8; 557 i++; 558 break; 559 560 case UWOP_SET_FPREG: 561 Reg = UnwindInfo->FrameRegister; 562 Context->Rsp = GetReg(Context, Reg) - UnwindInfo->FrameOffset * 16; 563 i++; 564 break; 565 566 case UWOP_SAVE_NONVOL: 567 Reg = UnwindCode.OpInfo; 568 Offset = *(USHORT*)(&UnwindInfo->UnwindCode[i + 1]); 569 SetRegFromStackValue(Context, ContextPointers, Reg, (DWORD64*)Context->Rsp + Offset); 570 i += 2; 571 break; 572 573 case UWOP_SAVE_NONVOL_FAR: 574 Reg = UnwindCode.OpInfo; 575 Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i + 1]); 576 SetRegFromStackValue(Context, ContextPointers, Reg, (DWORD64*)Context->Rsp + Offset); 577 i += 3; 578 break; 579 580 case UWOP_EPILOG: 581 i += 1; 582 break; 583 584 case UWOP_SPARE_CODE: 585 ASSERT(FALSE); 586 i += 2; 587 break; 588 589 case UWOP_SAVE_XMM128: 590 Reg = UnwindCode.OpInfo; 591 Offset = *(USHORT*)(&UnwindInfo->UnwindCode[i + 1]); 592 SetXmmRegFromStackValue(Context, ContextPointers, Reg, (M128A*)(Context->Rsp + Offset)); 593 i += 2; 594 break; 595 596 case UWOP_SAVE_XMM128_FAR: 597 Reg = UnwindCode.OpInfo; 598 Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i + 1]); 599 SetXmmRegFromStackValue(Context, ContextPointers, Reg, (M128A*)(Context->Rsp + Offset)); 600 i += 3; 601 break; 602 603 case UWOP_PUSH_MACHFRAME: 604 /* OpInfo is 1, when an error code was pushed, otherwise 0. */ 605 Context->Rsp += UnwindCode.OpInfo * sizeof(DWORD64); 606 607 /* Now pop the MACHINE_FRAME (Yes, "magic numbers", deal with it) */ 608 Context->Rip = *(PDWORD64)(Context->Rsp + 0x00); 609 Context->SegCs = *(PDWORD64)(Context->Rsp + 0x08); 610 Context->EFlags = *(PDWORD64)(Context->Rsp + 0x10); 611 Context->SegSs = *(PDWORD64)(Context->Rsp + 0x20); 612 Context->Rsp = *(PDWORD64)(Context->Rsp + 0x18); 613 ASSERT((i + 1) == UnwindInfo->CountOfCodes); 614 goto Exit; 615 } 616 } 617 618 /* Check for chained info */ 619 if (UnwindInfo->Flags & UNW_FLAG_CHAININFO) 620 { 621 /* See https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=msvc-160#chained-unwind-info-structures */ 622 FunctionEntry = (PRUNTIME_FUNCTION)&(UnwindInfo->UnwindCode[(UnwindInfo->CountOfCodes + 1) & ~1]); 623 UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData); 624 i = 0; 625 goto RepeatChainedInfo; 626 } 627 628 /* Unwind is finished, pop new Rip from Stack */ 629 if (Context->Rsp != 0) 630 { 631 Context->Rip = *(DWORD64*)Context->Rsp; 632 Context->Rsp += sizeof(DWORD64); 633 } 634 635 Exit: 636 637 /* Check if we have a handler and return it */ 638 if (UnwindInfo->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) 639 { 640 return RVA(ImageBase, *LanguageHandler); 641 } 642 643 return NULL; 644 } 645 646 /*! 647 \remark The implementation is based on the description in this blog: http://www.nynaeve.net/?p=106 648 649 Differences to the desciption: 650 - Instead of using 2 pointers to the unwind context and previous context, 651 that are being swapped and the context copied, the unwind context is 652 kept in the local context and copied back into the context passed in 653 by the caller. 654 655 \see http://www.nynaeve.net/?p=106 656 */ 657 BOOLEAN 658 NTAPI 659 RtlpUnwindInternal( 660 _In_opt_ PVOID TargetFrame, 661 _In_opt_ PVOID TargetIp, 662 _In_ PEXCEPTION_RECORD ExceptionRecord, 663 _In_ PVOID ReturnValue, 664 _In_ PCONTEXT ContextRecord, 665 _In_opt_ struct _UNWIND_HISTORY_TABLE *HistoryTable, 666 _In_ ULONG HandlerType) 667 { 668 DISPATCHER_CONTEXT DispatcherContext; 669 PEXCEPTION_ROUTINE ExceptionRoutine; 670 EXCEPTION_DISPOSITION Disposition; 671 PRUNTIME_FUNCTION FunctionEntry; 672 ULONG_PTR StackLow, StackHigh; 673 ULONG64 ImageBase, EstablisherFrame; 674 CONTEXT UnwindContext; 675 676 /* Get the current stack limits and registration frame */ 677 RtlpGetStackLimits(&StackLow, &StackHigh); 678 679 /* If we have a target frame, then this is our high limit */ 680 if (TargetFrame != NULL) 681 { 682 StackHigh = (ULONG64)TargetFrame + 1; 683 } 684 685 /* Copy the context */ 686 UnwindContext = *ContextRecord; 687 688 /* Set up the constant fields of the dispatcher context */ 689 DispatcherContext.ContextRecord = ContextRecord; 690 DispatcherContext.HistoryTable = HistoryTable; 691 DispatcherContext.TargetIp = (ULONG64)TargetIp; 692 693 /* Start looping */ 694 while (TRUE) 695 { 696 /* Lookup the FunctionEntry for the current RIP */ 697 FunctionEntry = RtlLookupFunctionEntry(UnwindContext.Rip, &ImageBase, NULL); 698 if (FunctionEntry == NULL) 699 { 700 /* No function entry, so this must be a leaf function. Pop the return address from the stack. 701 Note: this can happen after the first frame as the result of an exception */ 702 UnwindContext.Rip = *(DWORD64*)UnwindContext.Rsp; 703 UnwindContext.Rsp += sizeof(DWORD64); 704 705 /* Copy the context back for the next iteration */ 706 *ContextRecord = UnwindContext; 707 continue; 708 } 709 710 /* Save Rip before the virtual unwind */ 711 DispatcherContext.ControlPc = UnwindContext.Rip; 712 713 /* Do a virtual unwind to get the next frame */ 714 ExceptionRoutine = RtlVirtualUnwind(HandlerType, 715 ImageBase, 716 UnwindContext.Rip, 717 FunctionEntry, 718 &UnwindContext, 719 &DispatcherContext.HandlerData, 720 &EstablisherFrame, 721 NULL); 722 723 /* Check, if we are still within the stack boundaries */ 724 if ((EstablisherFrame < StackLow) || 725 (EstablisherFrame >= StackHigh) || 726 (EstablisherFrame & 7)) 727 { 728 /// TODO: Handle DPC stack 729 730 /* If we are handling an exception, we are done here. */ 731 if (HandlerType == UNW_FLAG_EHANDLER) 732 { 733 ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID; 734 return FALSE; 735 } 736 737 __debugbreak(); 738 RtlRaiseStatus(STATUS_BAD_STACK); 739 } 740 741 /* Check if we have an exception routine */ 742 if (ExceptionRoutine != NULL) 743 { 744 /* Check if this is the target frame */ 745 if (EstablisherFrame == (ULONG64)TargetFrame) 746 { 747 /* Set flag to inform the language handler */ 748 ExceptionRecord->ExceptionFlags |= EXCEPTION_TARGET_UNWIND; 749 } 750 751 /* Log the exception if it's enabled */ 752 RtlpCheckLogException(ExceptionRecord, 753 ContextRecord, 754 &DispatcherContext, 755 sizeof(DispatcherContext)); 756 757 /* Set up the variable fields of the dispatcher context */ 758 DispatcherContext.ImageBase = ImageBase; 759 DispatcherContext.FunctionEntry = FunctionEntry; 760 DispatcherContext.LanguageHandler = ExceptionRoutine; 761 DispatcherContext.EstablisherFrame = EstablisherFrame; 762 DispatcherContext.ScopeIndex = 0; 763 764 /* Store the return value in the unwind context */ 765 UnwindContext.Rax = (ULONG64)ReturnValue; 766 767 /* Loop all nested handlers */ 768 do 769 { 770 /// TODO: call RtlpExecuteHandlerForUnwind instead 771 /* Call the language specific handler */ 772 Disposition = ExceptionRoutine(ExceptionRecord, 773 (PVOID)EstablisherFrame, 774 &UnwindContext, 775 &DispatcherContext); 776 777 /* Clear exception flags for the next iteration */ 778 ExceptionRecord->ExceptionFlags &= ~(EXCEPTION_TARGET_UNWIND | 779 EXCEPTION_COLLIDED_UNWIND); 780 781 /* Check if we do exception handling */ 782 if (HandlerType == UNW_FLAG_EHANDLER) 783 { 784 if (Disposition == ExceptionContinueExecution) 785 { 786 /* Check if it was non-continuable */ 787 if (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) 788 { 789 __debugbreak(); 790 RtlRaiseStatus(EXCEPTION_NONCONTINUABLE_EXCEPTION); 791 } 792 793 /* Execution continues */ 794 return TRUE; 795 } 796 else if (Disposition == ExceptionNestedException) 797 { 798 /// TODO 799 __debugbreak(); 800 } 801 } 802 803 if (Disposition == ExceptionCollidedUnwind) 804 { 805 /// TODO 806 __debugbreak(); 807 } 808 809 /* This must be ExceptionContinueSearch now */ 810 if (Disposition != ExceptionContinueSearch) 811 { 812 __debugbreak(); 813 RtlRaiseStatus(STATUS_INVALID_DISPOSITION); 814 } 815 } while (ExceptionRecord->ExceptionFlags & EXCEPTION_COLLIDED_UNWIND); 816 } 817 818 /* Check, if we have left our stack (8.) */ 819 if ((EstablisherFrame < StackLow) || 820 (EstablisherFrame > StackHigh) || 821 (EstablisherFrame & 7)) 822 { 823 /// TODO: Check for DPC stack 824 __debugbreak(); 825 826 if (UnwindContext.Rip == ContextRecord->Rip) 827 { 828 RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); 829 } 830 else 831 { 832 ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); 833 } 834 } 835 836 if (EstablisherFrame == (ULONG64)TargetFrame) 837 { 838 break; 839 } 840 841 /* We have successfully unwound a frame. Copy the unwind context back. */ 842 *ContextRecord = UnwindContext; 843 } 844 845 if (ExceptionRecord->ExceptionCode != STATUS_UNWIND_CONSOLIDATE) 846 { 847 ContextRecord->Rip = (ULONG64)TargetIp; 848 } 849 850 /* Set the return value */ 851 ContextRecord->Rax = (ULONG64)ReturnValue; 852 853 /* Restore the context */ 854 RtlRestoreContext(ContextRecord, ExceptionRecord); 855 856 /* Should never get here! */ 857 ASSERT(FALSE); 858 return FALSE; 859 } 860 861 VOID 862 NTAPI 863 RtlUnwindEx( 864 _In_opt_ PVOID TargetFrame, 865 _In_opt_ PVOID TargetIp, 866 _In_opt_ PEXCEPTION_RECORD ExceptionRecord, 867 _In_ PVOID ReturnValue, 868 _In_ PCONTEXT ContextRecord, 869 _In_opt_ struct _UNWIND_HISTORY_TABLE *HistoryTable) 870 { 871 EXCEPTION_RECORD LocalExceptionRecord; 872 873 /* Capture the current context */ 874 RtlCaptureContext(ContextRecord); 875 876 /* Check if we have an exception record */ 877 if (ExceptionRecord == NULL) 878 { 879 /* No exception record was passed, so set up a local one */ 880 LocalExceptionRecord.ExceptionCode = STATUS_UNWIND; 881 LocalExceptionRecord.ExceptionAddress = (PVOID)ContextRecord->Rip; 882 LocalExceptionRecord.ExceptionRecord = NULL; 883 LocalExceptionRecord.NumberParameters = 0; 884 ExceptionRecord = &LocalExceptionRecord; 885 } 886 887 /* Call the internal function */ 888 RtlpUnwindInternal(TargetFrame, 889 TargetIp, 890 ExceptionRecord, 891 ReturnValue, 892 ContextRecord, 893 HistoryTable, 894 UNW_FLAG_UHANDLER); 895 } 896 897 VOID 898 NTAPI 899 RtlUnwind( 900 IN PVOID TargetFrame, 901 IN PVOID TargetIp, 902 IN PEXCEPTION_RECORD ExceptionRecord, 903 IN PVOID ReturnValue) 904 { 905 UNIMPLEMENTED; 906 return; 907 } 908 909 ULONG 910 NTAPI 911 RtlWalkFrameChain(OUT PVOID *Callers, 912 IN ULONG Count, 913 IN ULONG Flags) 914 { 915 CONTEXT Context; 916 ULONG64 ControlPc, ImageBase, EstablisherFrame; 917 ULONG64 StackLow, StackHigh; 918 PVOID HandlerData; 919 ULONG i, FramesToSkip; 920 PRUNTIME_FUNCTION FunctionEntry; 921 922 DPRINT("Enter RtlWalkFrameChain\n"); 923 924 /* The upper bits in Flags define how many frames to skip */ 925 FramesToSkip = Flags >> 8; 926 927 /* Capture the current Context */ 928 RtlCaptureContext(&Context); 929 ControlPc = Context.Rip; 930 931 /* Get the stack limits */ 932 RtlpGetStackLimits(&StackLow, &StackHigh); 933 934 /* Check if we want the user-mode stack frame */ 935 if (Flags & 1) 936 { 937 } 938 939 _SEH2_TRY 940 { 941 /* Loop the frames */ 942 for (i = 0; i < FramesToSkip + Count; i++) 943 { 944 /* Lookup the FunctionEntry for the current ControlPc */ 945 FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL); 946 947 /* Is this a leaf function? */ 948 if (!FunctionEntry) 949 { 950 Context.Rip = *(DWORD64*)Context.Rsp; 951 Context.Rsp += sizeof(DWORD64); 952 DPRINT("leaf funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); 953 } 954 else 955 { 956 RtlVirtualUnwind(UNW_FLAG_NHANDLER, 957 ImageBase, 958 ControlPc, 959 FunctionEntry, 960 &Context, 961 &HandlerData, 962 &EstablisherFrame, 963 NULL); 964 DPRINT("normal funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); 965 } 966 967 /* Check if we are in kernel mode */ 968 if (RtlpGetMode() == KernelMode) 969 { 970 /* Check if we left the kernel range */ 971 if (!(Flags & 1) && (Context.Rip < 0xFFFF800000000000ULL)) 972 { 973 break; 974 } 975 } 976 else 977 { 978 /* Check if we left the user range */ 979 if ((Context.Rip < 0x10000) || 980 (Context.Rip > 0x000007FFFFFEFFFFULL)) 981 { 982 break; 983 } 984 } 985 986 /* Check, if we have left our stack */ 987 if ((Context.Rsp < StackLow) || (Context.Rsp > StackHigh)) 988 { 989 break; 990 } 991 992 /* Continue with new Rip */ 993 ControlPc = Context.Rip; 994 995 /* Save value, if we are past the frames to skip */ 996 if (i >= FramesToSkip) 997 { 998 Callers[i - FramesToSkip] = (PVOID)ControlPc; 999 } 1000 } 1001 } 1002 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1003 { 1004 DPRINT1("Exception while getting callers!\n"); 1005 i = 0; 1006 } 1007 _SEH2_END; 1008 1009 DPRINT("RtlWalkFrameChain returns %ld\n", i); 1010 return i; 1011 } 1012 1013 /*! RtlGetCallersAddress 1014 * \ref http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Debug/RtlGetCallersAddress.html 1015 */ 1016 #undef RtlGetCallersAddress 1017 VOID 1018 NTAPI 1019 RtlGetCallersAddress( 1020 OUT PVOID *CallersAddress, 1021 OUT PVOID *CallersCaller ) 1022 { 1023 PVOID Callers[4]; 1024 ULONG Number; 1025 1026 /* Get callers: 1027 * RtlWalkFrameChain -> RtlGetCallersAddress -> x -> y */ 1028 Number = RtlWalkFrameChain(Callers, 4, 0); 1029 1030 *CallersAddress = (Number >= 3) ? Callers[2] : NULL; 1031 *CallersCaller = (Number == 4) ? Callers[3] : NULL; 1032 1033 return; 1034 } 1035 1036 static 1037 VOID 1038 RtlpCaptureNonVolatileContextPointers( 1039 _Out_ PKNONVOLATILE_CONTEXT_POINTERS NonvolatileContextPointers, 1040 _In_ ULONG64 TargetFrame) 1041 { 1042 CONTEXT Context; 1043 PRUNTIME_FUNCTION FunctionEntry; 1044 ULONG64 ImageBase; 1045 PVOID HandlerData; 1046 ULONG64 EstablisherFrame; 1047 1048 /* Zero out the nonvolatile context pointers */ 1049 RtlZeroMemory(NonvolatileContextPointers, sizeof(*NonvolatileContextPointers)); 1050 1051 /* Capture the current context */ 1052 RtlCaptureContext(&Context); 1053 1054 do 1055 { 1056 /* Look up the function entry */ 1057 FunctionEntry = RtlLookupFunctionEntry(Context.Rip, &ImageBase, NULL); 1058 ASSERT(FunctionEntry != NULL); 1059 1060 /* Do a virtual unwind to the caller and capture saved non-volatiles */ 1061 RtlVirtualUnwind(UNW_FLAG_EHANDLER, 1062 ImageBase, 1063 Context.Rip, 1064 FunctionEntry, 1065 &Context, 1066 &HandlerData, 1067 &EstablisherFrame, 1068 NonvolatileContextPointers); 1069 1070 /* Make sure nothing fishy is going on. Currently this is for kernel mode only. */ 1071 ASSERT(EstablisherFrame != 0); 1072 ASSERT((LONG64)Context.Rip < 0); 1073 1074 /* Continue until we reached the target frame or user mode */ 1075 } while (EstablisherFrame < TargetFrame); 1076 1077 /* If the caller did the right thing, we should get exactly the target frame */ 1078 ASSERT(EstablisherFrame == TargetFrame); 1079 } 1080 1081 VOID 1082 RtlSetUnwindContext( 1083 _In_ PCONTEXT Context, 1084 _In_ DWORD64 TargetFrame) 1085 { 1086 KNONVOLATILE_CONTEXT_POINTERS ContextPointers; 1087 1088 /* Capture pointers to the non-volatiles up to the target frame */ 1089 RtlpCaptureNonVolatileContextPointers(&ContextPointers, TargetFrame); 1090 1091 /* Copy the nonvolatiles to the captured locations */ 1092 *ContextPointers.R12 = Context->R12; 1093 *ContextPointers.R13 = Context->R13; 1094 *ContextPointers.R14 = Context->R14; 1095 *ContextPointers.R15 = Context->R15; 1096 *ContextPointers.Xmm6 = Context->Xmm6; 1097 *ContextPointers.Xmm7 = Context->Xmm7; 1098 *ContextPointers.Xmm8 = Context->Xmm8; 1099 *ContextPointers.Xmm9 = Context->Xmm9; 1100 *ContextPointers.Xmm10 = Context->Xmm10; 1101 *ContextPointers.Xmm11 = Context->Xmm11; 1102 *ContextPointers.Xmm12 = Context->Xmm12; 1103 *ContextPointers.Xmm13 = Context->Xmm13; 1104 *ContextPointers.Xmm14 = Context->Xmm14; 1105 *ContextPointers.Xmm15 = Context->Xmm15; 1106 } 1107