xref: /reactos/sdk/lib/rtl/amd64/unwind.c (revision 1d58e847)
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