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