Author: tkreuzer Date: Thu Nov 20 15:55:44 2008 New Revision: 37492
URL: http://svn.reactos.org/svn/reactos?rev=37492&view=rev Log: Implement RtlpTryToUnwindEpilog, which tries to unwind a function, if we are in the epilog by parsing the allowed opcodes.
Modified: branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c
Modified: branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c URL: http://svn.reactos.org/svn/reactos/branches/ros-amd64-bringup/reactos/lib/rt... ============================================================================== --- branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c [iso-8859-1] (original) +++ branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c [iso-8859-1] Thu Nov 20 15:55:44 2008 @@ -163,10 +163,146 @@
void FORCEINLINE -SetReg(PCONTEXT Context, UCHAR Reg, ULONG64 Value) -{ - ((PULONG64)(&Context->Rax))[Reg] = Value; -} +SetReg(PCONTEXT Context, BYTE Reg, DWORD64 Value) +{ + ((DWORD64*)(&Context->Rax))[Reg] = Value; +} + +DWORD64 +FORCEINLINE +GetReg(PCONTEXT Context, BYTE Reg) +{ + return ((DWORD64*)(&Context->Rax))[Reg]; +} + +void +FORCEINLINE +PopReg(PCONTEXT Context, BYTE Reg) +{ + DWORD64 Value = *(DWORD64*)Context->Rsp; + Context->Rsp += 8; + SetReg(Context, Reg, Value); +} + +/* Helper function that tries to unwind epilog instructions. + * Returns TRUE we have been in an epilog and it could be unwound. + * FALSE if the instructions were not allowed for an epilog. + * References: + * http://msdn.microsoft.com/en-us/library/8ydc79k6(VS.80).aspx + * http://msdn.microsoft.com/en-us/library/tawsa7cb.aspx + * TODO: + * - Test and compare with Windows behaviour + */ +BOOLEAN +static +inline +RtlpTryToUnwindEpilog( + PCONTEXT Context, + ULONG64 ImageBase, + PRUNTIME_FUNCTION FunctionEntry) +{ + CONTEXT LocalContext; + BYTE *InstrPtr; + DWORD Instr; + BYTE Reg, Mod; + ULONG64 EndAddress; + + /* Make a local copy of the context */ + LocalContext = *Context; + + InstrPtr = (BYTE*)LocalContext.Rip; + + /* Check if first instruction of epilog is "add rsp, x" */ + Instr = *(DWORD*)InstrPtr; + if ( (Instr & 0x00fffdff) == 0x00c48148 ) + { + if ( (Instr & 0x0000ff00) == 0x8300 ) + { + /* This is "add rsp, 0x??" */ + LocalContext.Rsp += Instr >> 24; + InstrPtr += 4; + } + else + { + /* This is "add rsp, 0x???????? */ + LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); + InstrPtr += 7; + } + } + /* Check if first instruction of epilog is "lea rsp, ..." */ + else if ( (Instr & 0x38fffe) == 0x208d48 ) + { + /* Get the register */ + Reg = ((Instr << 8) | (Instr >> 16)) & 0x7; + + LocalContext.Rsp = GetReg(&LocalContext, Reg); + + /* Get adressing mode */ + Mod = (Instr >> 22) & 0x3; + if (Mod == 0) + { + /* No displacement */ + InstrPtr += 3; + } + else if (Mod == 1) + { + /* 1 byte displacement */ + LocalContext.Rsp += Instr >> 24; + InstrPtr += 4; + } + else if (Mod == 2) + { + /* 4 bytes displacement */ + LocalContext.Rsp += *(DWORD*)(InstrPtr + 3); + InstrPtr += 7; + } + } + + /* Loop the following instructions */ + EndAddress = FunctionEntry->EndAddress + ImageBase; + while((DWORD64)InstrPtr < EndAddress) + { + Instr = *(DWORD*)InstrPtr; + + /* Check for a simple pop */ + if ( (Instr & 0xf8) == 0x58 ) + { + /* Opcode pops a basic register from stack */ + Reg = Instr & 0x7; + PopReg(&LocalContext, Reg); + InstrPtr++; + continue; + } + + /* Check for REX + pop */ + if ( (Instr & 0xf8fb) == 0x5841 ) + { + /* Opcode is pop r8 .. r15 */ + Reg = (Instr & 0x7) + 8; + PopReg(&LocalContext, Reg); + InstrPtr += 2; + continue; + } + + /* Check for retn / retf */ + if ( (Instr & 0xf7) == 0xc3 ) + { + /* We are finished */ + break; + } + + /* Opcode not allowed for Epilog */ + return FALSE; + } + + /* Unwind is finished, pop new Rip from Stack */ + LocalContext.Rip = *(DWORD64*)LocalContext.Rsp; + LocalContext.Rsp += sizeof(DWORD64); + + *Context = LocalContext; + return TRUE; +} +
PEXCEPTION_ROUTINE NTAPI @@ -184,7 +320,7 @@ ULONG CodeOffset; ULONG i; UNWIND_CODE UnwindCode; - UCHAR Reg; + BYTE Reg;
/* Use relative virtual address */ ControlPc -= ImageBase; @@ -201,6 +337,16 @@
/* Calculate relative offset to function start */ CodeOffset = ControlPc - FunctionEntry->BeginAddress; + + /* Check if we are in the function epilog and try to finish it */ + if (CodeOffset > UnwindInfo->SizeOfProlog) + { + if (RtlpTryToUnwindEpilog(Context, ImageBase, FunctionEntry)) + { + /* There's no exception routine */ + return NULL; + } + }
/* Skip all Ops with an offset greater than the current Offset */ i = 0;