2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Exception related functions
5 * PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
8 /* INCLUDES *****************************************************************/
15 #define UNWIND_HISTORY_TABLE_NONE 0
16 #define UNWIND_HISTORY_TABLE_GLOBAL 1
17 #define UNWIND_HISTORY_TABLE_LOCAL 2
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 #define UWOP_SAVE_XMM 6
26 #define UWOP_SAVE_XMM_FAR 7
27 #define UWOP_SAVE_XMM128 8
28 #define UWOP_SAVE_XMM128_FAR 9
29 #define UWOP_PUSH_MACHFRAME 10
31 typedef unsigned char UBYTE
;
33 typedef union _UNWIND_CODE
42 } UNWIND_CODE
, *PUNWIND_CODE
;
44 typedef struct _UNWIND_INFO
50 UBYTE FrameRegister
:4;
52 UNWIND_CODE UnwindCode
[1];
54 OPTIONAL ULONG ExceptionHandler;
55 OPTIONAL ULONG FunctionEntry;
57 OPTIONAL ULONG ExceptionData[];
59 } UNWIND_INFO
, *PUNWIND_INFO
;
66 /* FUNCTIONS *****************************************************************/
70 RtlLookupFunctionTable(
72 OUT PDWORD64 ImageBase
,
75 PIMAGE_DOS_HEADER DosHeader
;
76 PIMAGE_NT_HEADERS NtHeader
;
77 PIMAGE_DATA_DIRECTORY Directory
;
80 DosHeader
= RtlpLookupModuleBase((PVOID
)ControlPc
);
86 /* Locate NT header and check number of directories */
87 NtHeader
= (PVOID
)((ULONG64
)DosHeader
+ DosHeader
->e_lfanew
);
88 if (NtHeader
->OptionalHeader
.NumberOfRvaAndSizes
89 < IMAGE_DIRECTORY_ENTRY_EXCEPTION
)
94 /* Locate the exception directory */
95 Directory
= &NtHeader
->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXCEPTION
];
96 *Length
= Directory
->Size
/ sizeof(RUNTIME_FUNCTION
);
97 *ImageBase
= (ULONG64
)DosHeader
;
98 if (!Directory
->VirtualAddress
)
103 return (PVOID
)((ULONG64
)DosHeader
+ Directory
->VirtualAddress
);
107 // http://msdn.microsoft.com/en-us/library/ms680597(VS.85).aspx
110 RtlLookupFunctionEntry(
111 IN DWORD64 ControlPc
,
112 OUT PDWORD64 ImageBase
,
113 OUT PUNWIND_HISTORY_TABLE HistoryTable
)
115 PRUNTIME_FUNCTION FunctionTable
, FunctionEntry
;
117 ULONG IndexLo
, IndexHi
, IndexMid
;
119 /* Find the corresponding table */
120 FunctionTable
= RtlLookupFunctionTable(ControlPc
, ImageBase
, &TableLength
);
122 /* Fail, if no table is found */
128 /* Use relative virtual address */
129 ControlPc
-= *ImageBase
;
131 /* Do a binary search */
133 IndexHi
= TableLength
;
134 while (IndexHi
> IndexLo
)
136 IndexMid
= (IndexLo
+ IndexHi
) / 2;
137 FunctionEntry
= &FunctionTable
[IndexMid
];
139 if ( (ControlPc
>= FunctionEntry
->BeginAddress
) &&
140 (ControlPc
< FunctionEntry
->EndAddress
) )
142 /* ControlPc is within limits, return entry */
143 return FunctionEntry
;
146 if (ControlPc
< FunctionEntry
->BeginAddress
)
148 /* Continue search in lower half */
153 /* Continue search in upper half */
154 IndexLo
= IndexMid
+ 1;
158 /* Nothing found, return NULL */
164 SetReg(PCONTEXT Context
, BYTE Reg
, DWORD64 Value
)
166 ((DWORD64
*)(&Context
->Rax
))[Reg
] = Value
;
171 GetReg(PCONTEXT Context
, BYTE Reg
)
173 return ((DWORD64
*)(&Context
->Rax
))[Reg
];
178 PopReg(PCONTEXT Context
, BYTE Reg
)
180 DWORD64 Value
= *(DWORD64
*)Context
->Rsp
;
182 SetReg(Context
, Reg
, Value
);
185 /* Helper function that tries to unwind epilog instructions.
186 * Returns TRUE we have been in an epilog and it could be unwound.
187 * FALSE if the instructions were not allowed for an epilog.
189 * http://msdn.microsoft.com/en-us/library/8ydc79k6(VS.80).aspx
190 * http://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
192 * - Test and compare with Windows behaviour
197 RtlpTryToUnwindEpilog(
200 PRUNTIME_FUNCTION FunctionEntry
)
202 CONTEXT LocalContext
;
208 /* Make a local copy of the context */
209 LocalContext
= *Context
;
211 InstrPtr
= (BYTE
*)LocalContext
.Rip
;
213 /* Check if first instruction of epilog is "add rsp, x" */
214 Instr
= *(DWORD
*)InstrPtr
;
215 if ( (Instr
& 0x00fffdff) == 0x00c48148 )
217 if ( (Instr
& 0x0000ff00) == 0x8300 )
219 /* This is "add rsp, 0x??" */
220 LocalContext
.Rsp
+= Instr
>> 24;
225 /* This is "add rsp, 0x???????? */
226 LocalContext
.Rsp
+= *(DWORD
*)(InstrPtr
+ 3);
230 /* Check if first instruction of epilog is "lea rsp, ..." */
231 else if ( (Instr
& 0x38fffe) == 0x208d48 )
233 /* Get the register */
234 Reg
= ((Instr
<< 8) | (Instr
>> 16)) & 0x7;
236 LocalContext
.Rsp
= GetReg(&LocalContext
, Reg
);
238 /* Get adressing mode */
239 Mod
= (Instr
>> 22) & 0x3;
242 /* No displacement */
247 /* 1 byte displacement */
248 LocalContext
.Rsp
+= Instr
>> 24;
253 /* 4 bytes displacement */
254 LocalContext
.Rsp
+= *(DWORD
*)(InstrPtr
+ 3);
259 /* Loop the following instructions */
260 EndAddress
= FunctionEntry
->EndAddress
+ ImageBase
;
261 while((DWORD64
)InstrPtr
< EndAddress
)
263 Instr
= *(DWORD
*)InstrPtr
;
265 /* Check for a simple pop */
266 if ( (Instr
& 0xf8) == 0x58 )
268 /* Opcode pops a basic register from stack */
270 PopReg(&LocalContext
, Reg
);
275 /* Check for REX + pop */
276 if ( (Instr
& 0xf8fb) == 0x5841 )
278 /* Opcode is pop r8 .. r15 */
279 Reg
= (Instr
& 0x7) + 8;
280 PopReg(&LocalContext
, Reg
);
285 /* Check for retn / retf */
286 if ( (Instr
& 0xf7) == 0xc3 )
288 /* We are finished */
292 /* Opcode not allowed for Epilog */
296 /* Unwind is finished, pop new Rip from Stack */
297 LocalContext
.Rip
= *(DWORD64
*)LocalContext
.Rsp
;
298 LocalContext
.Rsp
+= sizeof(DWORD64
);
300 *Context
= LocalContext
;
308 IN ULONG HandlerType
,
309 IN ULONG64 ImageBase
,
310 IN ULONG64 ControlPc
,
311 IN PRUNTIME_FUNCTION FunctionEntry
,
312 IN OUT PCONTEXT Context
,
313 OUT PVOID
*HandlerData
,
314 OUT PULONG64 EstablisherFrame
,
315 IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers
)
317 PUNWIND_INFO UnwindInfo
;
320 UNWIND_CODE UnwindCode
;
323 /* Use relative virtual address */
324 ControlPc
-= ImageBase
;
327 if ( (ControlPc
< FunctionEntry
->BeginAddress
) ||
328 (ControlPc
>= FunctionEntry
->EndAddress
) )
333 /* Get a pointer to the unwind info */
334 UnwindInfo
= RVA(ImageBase
, FunctionEntry
->UnwindData
);
336 /* Calculate relative offset to function start */
337 CodeOffset
= ControlPc
- FunctionEntry
->BeginAddress
;
339 /* Check if we are in the function epilog and try to finish it */
340 if (CodeOffset
> UnwindInfo
->SizeOfProlog
)
342 if (RtlpTryToUnwindEpilog(Context
, ImageBase
, FunctionEntry
))
344 /* There's no exception routine */
349 /* Skip all Ops with an offset greater than the current Offset */
351 while (i
< UnwindInfo
->CountOfCodes
&&
352 CodeOffset
< UnwindInfo
->UnwindCode
[i
].CodeOffset
)
354 UnwindCode
= UnwindInfo
->UnwindCode
[i
];
355 switch (UnwindCode
.UnwindOp
)
357 case UWOP_SAVE_NONVOL
:
359 case UWOP_SAVE_XMM128
:
363 case UWOP_SAVE_NONVOL_FAR
:
364 case UWOP_SAVE_XMM_FAR
:
365 case UWOP_SAVE_XMM128_FAR
:
369 case UWOP_ALLOC_LARGE
:
370 i
+= UnwindCode
.OpInfo
? 3 : 2;
378 /* Process the left Ops */
379 while (i
< UnwindInfo
->CountOfCodes
)
381 UnwindCode
= UnwindInfo
->UnwindCode
[i
];
382 switch (UnwindCode
.UnwindOp
)
384 case UWOP_PUSH_NONVOL
:
385 Reg
= UnwindCode
.OpInfo
;
386 SetReg(Context
, Reg
, *(DWORD64
*)Context
->Rsp
);
387 Context
->Rsp
+= sizeof(DWORD64
);
391 case UWOP_ALLOC_LARGE
:
392 if (UnwindCode
.OpInfo
)
394 ULONG Offset
= *(ULONG
*)(&UnwindInfo
->UnwindCode
[i
+1]);
395 Context
->Rsp
+= Offset
;
400 USHORT Offset
= UnwindInfo
->UnwindCode
[i
+1].FrameOffset
;
401 Context
->Rsp
+= Offset
* 8;
406 case UWOP_ALLOC_SMALL
:
407 Context
->Rsp
+= (UnwindCode
.OpInfo
+ 1) * 8;
415 case UWOP_SAVE_NONVOL
:
419 case UWOP_SAVE_NONVOL_FAR
:
427 case UWOP_SAVE_XMM_FAR
:
429 case UWOP_SAVE_XMM128
:
431 case UWOP_SAVE_XMM128_FAR
:
433 case UWOP_PUSH_MACHFRAME
:
438 /* Unwind is finished, pop new Rip from Stack */
439 Context
->Rip
= *(DWORD64
*)Context
->Rsp
;
440 Context
->Rsp
+= sizeof(DWORD64
);
448 IN ULONG64 TargetFrame
,
450 IN PEXCEPTION_RECORD ExceptionRecord
,
451 IN PVOID ReturnValue
,
452 OUT PCONTEXT OriginalContext
,
453 IN PUNWIND_HISTORY_TABLE HistoryTable
)
462 IN PVOID TargetFrame
,
464 IN PEXCEPTION_RECORD ExceptionRecord
,
465 IN PVOID ReturnValue
)
473 RtlWalkFrameChain(OUT PVOID
*Callers
,
478 ULONG64 ControlPc
, ImageBase
, EstablisherFrame
;
479 ULONG64 StackLow
, StackHigh
;
482 PRUNTIME_FUNCTION FunctionEntry
;
484 DPRINT("Enter RtlWalkFrameChain\n");
486 /* Capture the current Context */
487 RtlCaptureContext(&Context
);
488 ControlPc
= Context
.Rip
;
490 /* Get the stack limits */
491 RtlpGetStackLimits(&StackLow
, &StackHigh
);
493 /* Check if we want the user-mode stack frame */
498 /* Loop the frames */
499 for (i
= 0; i
< Count
; i
++)
501 /* Lookup the FunctionEntry for the current ControlPc */
502 FunctionEntry
= RtlLookupFunctionEntry(ControlPc
, &ImageBase
, NULL
);
504 /* Is this a leaf function? */
507 Context
.Rip
= *(DWORD64
*)Context
.Rsp
;
508 Context
.Rsp
+= sizeof(DWORD64
);
509 DPRINT("leaf funtion, new Rip = %p, new Rsp = %p\n", (PVOID
)Context
.Rip
, (PVOID
)Context
.Rsp
);
521 DPRINT("normal funtion, new Rip = %p, new Rsp = %p\n", (PVOID
)Context
.Rip
, (PVOID
)Context
.Rsp
);
524 /* Check if new Rip is valid */
530 /* Check, if we have left our stack */
531 if ((Context
.Rsp
< StackLow
) || (Context
.Rsp
> StackHigh
))
536 /* Save this frame and continue with new Rip */
537 ControlPc
= Context
.Rip
;
538 Callers
[i
] = (PVOID
)ControlPc
;
541 DPRINT("RtlWalkFrameChain returns %ld\n", i
);
545 // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Debug/RtlGetCallersAddress.html
546 #undef RtlGetCallersAddress
549 RtlGetCallersAddress(
550 OUT PVOID
*CallersAddress
,
551 OUT PVOID
*CallersCaller
)
557 * RtlWalkFrameChain -> RtlGetCallersAddress -> x -> y */
558 Number
= RtlWalkFrameChain(Callers
, 4, 0);
562 *CallersAddress
= (Number
>= 3) ? Callers
[2] : NULL
;
566 *CallersCaller
= (Number
== 4) ? Callers
[3] : NULL
;