ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

unwind.c
Go to the documentation of this file.
00001 /*
00002  * COPYRIGHT:       See COPYING in the top level directory
00003  * PROJECT:         ReactOS system libraries
00004  * PURPOSE:         Unwinding related functions
00005  * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
00006  */
00007 
00008 /* INCLUDES *****************************************************************/
00009 
00010 #include <rtl.h>
00011 
00012 #define NDEBUG
00013 #include <debug.h>
00014 
00015 #define UNWIND_HISTORY_TABLE_NONE 0
00016 #define UNWIND_HISTORY_TABLE_GLOBAL 1
00017 #define UNWIND_HISTORY_TABLE_LOCAL 2
00018 
00019 #define UWOP_PUSH_NONVOL 0
00020 #define UWOP_ALLOC_LARGE 1
00021 #define UWOP_ALLOC_SMALL 2
00022 #define UWOP_SET_FPREG 3
00023 #define UWOP_SAVE_NONVOL 4
00024 #define UWOP_SAVE_NONVOL_FAR 5
00025 #define UWOP_SAVE_XMM 6
00026 #define UWOP_SAVE_XMM_FAR 7
00027 #define UWOP_SAVE_XMM128 8
00028 #define UWOP_SAVE_XMM128_FAR 9
00029 #define UWOP_PUSH_MACHFRAME 10
00030 
00031 #define UNW_FLAG_NHANDLER 0
00032 #define UNW_FLAG_EHANDLER 1
00033 #define UNW_FLAG_UHANDLER 2
00034 #define UNW_FLAG_CHAININFO 4
00035 
00036 typedef unsigned char UBYTE;
00037 
00038 typedef union _UNWIND_CODE
00039 {
00040     struct
00041     {
00042         UBYTE CodeOffset;
00043         UBYTE UnwindOp:4;
00044         UBYTE OpInfo:4;
00045     };
00046     USHORT FrameOffset;
00047 } UNWIND_CODE, *PUNWIND_CODE;
00048 
00049 typedef struct _UNWIND_INFO
00050 {
00051     UBYTE Version:3;
00052     UBYTE Flags:5;
00053     UBYTE SizeOfProlog;
00054     UBYTE CountOfCodes;
00055     UBYTE FrameRegister:4;
00056     UBYTE FrameOffset:4;
00057     UNWIND_CODE UnwindCode[1];
00058 /*    union {
00059         OPTIONAL ULONG ExceptionHandler;
00060         OPTIONAL ULONG FunctionEntry;
00061     };
00062     OPTIONAL ULONG ExceptionData[];
00063 */
00064 } UNWIND_INFO, *PUNWIND_INFO;
00065 
00066 /* FUNCTIONS *****************************************************************/
00067 
00079 PRUNTIME_FUNCTION
00080 NTAPI
00081 RtlLookupFunctionTable(
00082     IN DWORD64 ControlPc,
00083     OUT PDWORD64 ImageBase,
00084     OUT PULONG Length)
00085 {
00086     PVOID Table;
00087     ULONG Size;
00088 
00089     /* Find corresponding file header from code address */
00090     if (!RtlPcToFileHeader((PVOID)ControlPc, (PVOID*)ImageBase))
00091     {
00092         /* Nothing found */
00093         return NULL;
00094     }
00095 
00096     /* Locate the exception directory */
00097     Table = RtlImageDirectoryEntryToData((PVOID)*ImageBase,
00098                                          TRUE,
00099                                          IMAGE_DIRECTORY_ENTRY_EXCEPTION,
00100                                          &Size);
00101 
00102     /* Return the number of entries */
00103     *Length = Size / sizeof(RUNTIME_FUNCTION);
00104 
00105     /* Return the address of the table */
00106     return Table;
00107 }
00108 
00114 PRUNTIME_FUNCTION
00115 NTAPI
00116 RtlLookupFunctionEntry(
00117     IN DWORD64 ControlPc,
00118     OUT PDWORD64 ImageBase,
00119     OUT PUNWIND_HISTORY_TABLE HistoryTable)
00120 {
00121     PRUNTIME_FUNCTION FunctionTable, FunctionEntry;
00122     ULONG TableLength;
00123     ULONG IndexLo, IndexHi, IndexMid;
00124 
00125     /* Find the corresponding table */
00126     FunctionTable = RtlLookupFunctionTable(ControlPc, ImageBase, &TableLength);
00127 
00128     /* Fail, if no table is found */
00129     if (!FunctionTable)
00130     {
00131         return NULL;
00132     }
00133 
00134     /* Use relative virtual address */
00135     ControlPc -= *ImageBase;
00136 
00137     /* Do a binary search */
00138     IndexLo = 0;
00139     IndexHi = TableLength;
00140     while (IndexHi > IndexLo)
00141     {
00142         IndexMid = (IndexLo + IndexHi) / 2;
00143         FunctionEntry = &FunctionTable[IndexMid];
00144 
00145         if (ControlPc < FunctionEntry->BeginAddress)
00146         {
00147             /* Continue search in lower half */
00148             IndexHi = IndexMid;
00149         }
00150         else if (ControlPc >= FunctionEntry->EndAddress)
00151         {
00152             /* Continue search in upper half */
00153             IndexLo = IndexMid + 1;
00154         }
00155         else
00156         {
00157             /* ControlPc is within limits, return entry */
00158             return FunctionEntry;
00159         }
00160     }
00161 
00162     /* Nothing found, return NULL */
00163     return NULL;
00164 }
00165 
00166 BOOLEAN
00167 NTAPI
00168 RtlAddFunctionTable(
00169     IN PRUNTIME_FUNCTION FunctionTable,
00170     IN DWORD EntryCount,
00171     IN DWORD64 BaseAddress)
00172 {
00173     UNIMPLEMENTED;
00174     return FALSE;
00175 }
00176 
00177 BOOLEAN
00178 NTAPI
00179 RtlDeleteFunctionTable(
00180     IN PRUNTIME_FUNCTION FunctionTable)
00181 {
00182     UNIMPLEMENTED;
00183     return FALSE;
00184 }
00185 
00186 BOOLEAN
00187 NTAPI
00188 RtlInstallFunctionTableCallback(
00189     IN DWORD64 TableIdentifier,
00190     IN DWORD64 BaseAddress,
00191     IN DWORD Length,
00192     IN PGET_RUNTIME_FUNCTION_CALLBACK Callback,
00193     IN PVOID Context,
00194     IN PCWSTR OutOfProcessCallbackDll)
00195 {
00196     UNIMPLEMENTED;
00197     return FALSE;
00198 }
00199 
00200 void
00201 FORCEINLINE
00202 SetReg(PCONTEXT Context, BYTE Reg, DWORD64 Value)
00203 {
00204     ((DWORD64*)(&Context->Rax))[Reg] = Value;
00205 }
00206 
00207 DWORD64
00208 FORCEINLINE
00209 GetReg(PCONTEXT Context, BYTE Reg)
00210 {
00211     return ((DWORD64*)(&Context->Rax))[Reg];
00212 }
00213 
00214 void
00215 FORCEINLINE
00216 PopReg(PCONTEXT Context, BYTE Reg)
00217 {
00218     DWORD64 Value = *(DWORD64*)Context->Rsp;
00219     Context->Rsp += 8;
00220     SetReg(Context, Reg, Value);
00221 }
00222 
00233 BOOLEAN
00234 static
00235 __inline
00236 RtlpTryToUnwindEpilog(
00237     PCONTEXT Context,
00238     ULONG64 ImageBase,
00239     PRUNTIME_FUNCTION FunctionEntry)
00240 {
00241     CONTEXT LocalContext;
00242     BYTE *InstrPtr;
00243     DWORD Instr;
00244     BYTE Reg, Mod;
00245     ULONG64 EndAddress;
00246 
00247     /* Make a local copy of the context */
00248     LocalContext = *Context;
00249 
00250     InstrPtr = (BYTE*)LocalContext.Rip;
00251 
00252     /* Check if first instruction of epilog is "add rsp, x" */
00253     Instr = *(DWORD*)InstrPtr;
00254     if ( (Instr & 0x00fffdff) == 0x00c48148 )
00255     {
00256         if ( (Instr & 0x0000ff00) == 0x8300 )
00257         {
00258             /* This is "add rsp, 0x??" */
00259             LocalContext.Rsp += Instr >> 24;
00260             InstrPtr += 4;
00261         }
00262         else
00263         {
00264             /* This is "add rsp, 0x???????? */
00265             LocalContext.Rsp += *(DWORD*)(InstrPtr + 3);
00266             InstrPtr += 7;
00267         }
00268     }
00269     /* Check if first instruction of epilog is "lea rsp, ..." */
00270     else if ( (Instr & 0x38fffe) == 0x208d48 )
00271     {
00272         /* Get the register */
00273         Reg = ((Instr << 8) | (Instr >> 16)) & 0x7;
00274 
00275         LocalContext.Rsp = GetReg(&LocalContext, Reg);
00276 
00277         /* Get adressing mode */
00278         Mod = (Instr >> 22) & 0x3;
00279         if (Mod == 0)
00280         {
00281             /* No displacement */
00282             InstrPtr += 3;
00283         }
00284         else if (Mod == 1)
00285         {
00286             /* 1 byte displacement */
00287             LocalContext.Rsp += Instr >> 24;
00288             InstrPtr += 4;
00289         }
00290         else if (Mod == 2)
00291         {
00292             /* 4 bytes displacement */
00293             LocalContext.Rsp += *(DWORD*)(InstrPtr + 3);
00294             InstrPtr += 7;
00295         }
00296     }
00297 
00298     /* Loop the following instructions */
00299     EndAddress = FunctionEntry->EndAddress + ImageBase;
00300     while((DWORD64)InstrPtr < EndAddress)
00301     {
00302         Instr = *(DWORD*)InstrPtr;
00303 
00304         /* Check for a simple pop */
00305         if ( (Instr & 0xf8) == 0x58 )
00306         {
00307             /* Opcode pops a basic register from stack */
00308             Reg = Instr & 0x7;
00309             PopReg(&LocalContext, Reg);
00310             InstrPtr++;
00311             continue;
00312         }
00313 
00314         /* Check for REX + pop */
00315         if ( (Instr & 0xf8fb) == 0x5841 )
00316         {
00317             /* Opcode is pop r8 .. r15 */
00318             Reg = (Instr & 0x7) + 8;
00319             PopReg(&LocalContext, Reg);
00320             InstrPtr += 2;
00321             continue;
00322         }
00323 
00324         /* Check for retn / retf */
00325         if ( (Instr & 0xf7) == 0xc3 )
00326         {
00327             /* We are finished */
00328             break;
00329         }
00330 
00331         /* Opcode not allowed for Epilog */
00332         return FALSE;
00333     }
00334 
00335     /* Unwind is finished, pop new Rip from Stack */
00336     LocalContext.Rip = *(DWORD64*)LocalContext.Rsp;
00337     LocalContext.Rsp += sizeof(DWORD64);
00338 
00339     *Context = LocalContext;
00340     return TRUE;
00341 }
00342 
00343 
00344 PEXCEPTION_ROUTINE
00345 NTAPI
00346 RtlVirtualUnwind(
00347     IN ULONG HandlerType,
00348     IN ULONG64 ImageBase,
00349     IN ULONG64 ControlPc,
00350     IN PRUNTIME_FUNCTION FunctionEntry,
00351     IN OUT PCONTEXT Context,
00352     OUT PVOID *HandlerData,
00353     OUT PULONG64 EstablisherFrame,
00354     IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers)
00355 {
00356     PUNWIND_INFO UnwindInfo;
00357     ULONG_PTR CodeOffset;
00358     ULONG i;
00359     UNWIND_CODE UnwindCode;
00360     BYTE Reg;
00361 
00362     /* Use relative virtual address */
00363     ControlPc -= ImageBase;
00364 
00365     /* Sanity checks */
00366     if ( (ControlPc < FunctionEntry->BeginAddress) ||
00367          (ControlPc >= FunctionEntry->EndAddress) )
00368     {
00369         return NULL;
00370     }
00371 
00372     /* Get a pointer to the unwind info */
00373     UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData);
00374 
00375     /* Calculate relative offset to function start */
00376     CodeOffset = ControlPc - FunctionEntry->BeginAddress;
00377 
00378     /* Check if we are in the function epilog and try to finish it */
00379     if (CodeOffset > UnwindInfo->SizeOfProlog)
00380     {
00381         if (RtlpTryToUnwindEpilog(Context, ImageBase, FunctionEntry))
00382         {
00383             /* There's no exception routine */
00384             return NULL;
00385         }
00386     }
00387 
00388     /* Skip all Ops with an offset greater than the current Offset */
00389     i = 0;
00390     while (i < UnwindInfo->CountOfCodes &&
00391            CodeOffset < UnwindInfo->UnwindCode[i].CodeOffset)
00392     {
00393         UnwindCode = UnwindInfo->UnwindCode[i];
00394         switch (UnwindCode.UnwindOp)
00395         {
00396             case UWOP_SAVE_NONVOL:
00397             case UWOP_SAVE_XMM:
00398             case UWOP_SAVE_XMM128:
00399                 i += 2;
00400                 break;
00401 
00402             case UWOP_SAVE_NONVOL_FAR:
00403             case UWOP_SAVE_XMM_FAR:
00404             case UWOP_SAVE_XMM128_FAR:
00405                 i += 3;
00406                 break;
00407 
00408             case UWOP_ALLOC_LARGE:
00409                 i += UnwindCode.OpInfo ? 3 : 2;
00410                 break;
00411 
00412             default:
00413                 i++;
00414         }
00415     }
00416 
00417     /* Process the left Ops */
00418     while (i < UnwindInfo->CountOfCodes)
00419     {
00420         UnwindCode = UnwindInfo->UnwindCode[i];
00421         switch (UnwindCode.UnwindOp)
00422         {
00423             case UWOP_PUSH_NONVOL:
00424                 Reg = UnwindCode.OpInfo;
00425                 SetReg(Context, Reg, *(DWORD64*)Context->Rsp);
00426                 Context->Rsp += sizeof(DWORD64);
00427                 i++;
00428                 break;
00429 
00430             case UWOP_ALLOC_LARGE:
00431                 if (UnwindCode.OpInfo)
00432                 {
00433                     ULONG Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i+1]);
00434                     Context->Rsp += Offset;
00435                     i += 3;
00436                 }
00437                 else
00438                 {
00439                     USHORT Offset = UnwindInfo->UnwindCode[i+1].FrameOffset;
00440                     Context->Rsp += Offset * 8;
00441                     i += 2;
00442                 }
00443                 break;
00444 
00445             case UWOP_ALLOC_SMALL:
00446                 Context->Rsp += (UnwindCode.OpInfo + 1) * 8;
00447                 i++;
00448                 break;
00449 
00450             case UWOP_SET_FPREG:
00451                 i++;
00452                 break;
00453 
00454             case UWOP_SAVE_NONVOL:
00455                 i += 2;
00456                 break;
00457 
00458             case UWOP_SAVE_NONVOL_FAR:
00459                 i += 3;
00460                 break;
00461 
00462             case UWOP_SAVE_XMM:
00463                 i += 2;
00464                 break;
00465 
00466             case UWOP_SAVE_XMM_FAR:
00467                 i += 3;
00468                 break;
00469 
00470             case UWOP_SAVE_XMM128:
00471                 i += 2;
00472                 break;
00473 
00474             case UWOP_SAVE_XMM128_FAR:
00475                 i += 3;
00476                 break;
00477 
00478             case UWOP_PUSH_MACHFRAME:
00479                 i += 1;
00480                 break;
00481         }
00482     }
00483 
00484     /* Unwind is finished, pop new Rip from Stack */
00485     Context->Rip = *(DWORD64*)Context->Rsp;
00486     Context->Rsp += sizeof(DWORD64);
00487 
00488     return 0;
00489 }
00490 
00491 VOID
00492 NTAPI
00493 RtlUnwindEx(
00494    IN ULONG64 TargetFrame,
00495    IN ULONG64 TargetIp,
00496    IN PEXCEPTION_RECORD ExceptionRecord,
00497    IN PVOID ReturnValue,
00498    OUT PCONTEXT OriginalContext,
00499    IN PUNWIND_HISTORY_TABLE HistoryTable)
00500 {
00501     UNIMPLEMENTED;
00502     return;
00503 }
00504 
00505 VOID
00506 NTAPI
00507 RtlUnwind(
00508   IN PVOID TargetFrame,
00509   IN PVOID TargetIp,
00510   IN PEXCEPTION_RECORD ExceptionRecord,
00511   IN PVOID ReturnValue)
00512 {
00513     UNIMPLEMENTED;
00514     return;
00515 }
00516 
00517 ULONG
00518 NTAPI
00519 RtlWalkFrameChain(OUT PVOID *Callers,
00520                   IN ULONG Count,
00521                   IN ULONG Flags)
00522 {
00523     CONTEXT Context;
00524     ULONG64 ControlPc, ImageBase, EstablisherFrame;
00525     ULONG64 StackLow, StackHigh;
00526     PVOID HandlerData;
00527     ULONG i;
00528     PRUNTIME_FUNCTION FunctionEntry;
00529 
00530     DPRINT("Enter RtlWalkFrameChain\n");
00531 
00532     /* Capture the current Context */
00533     RtlCaptureContext(&Context);
00534     ControlPc = Context.Rip;
00535 
00536     /* Get the stack limits */
00537     RtlpGetStackLimits(&StackLow, &StackHigh);
00538 
00539     /* Check if we want the user-mode stack frame */
00540     if (Flags == 1)
00541     {
00542     }
00543 
00544     /* Loop the frames */
00545     for (i = 0; i < Count; i++)
00546     {
00547         /* Lookup the FunctionEntry for the current ControlPc */
00548         FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL);
00549 
00550         /* Is this a leaf function? */
00551         if (!FunctionEntry)
00552         {
00553             Context.Rip = *(DWORD64*)Context.Rsp;
00554             Context.Rsp += sizeof(DWORD64);
00555             DPRINT("leaf funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp);
00556         }
00557         else
00558         {
00559             RtlVirtualUnwind(0,
00560                              ImageBase,
00561                              ControlPc,
00562                              FunctionEntry,
00563                              &Context,
00564                              &HandlerData,
00565                              &EstablisherFrame,
00566                              NULL);
00567             DPRINT("normal funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp);
00568         }
00569 
00570         /* Check if new Rip is valid */
00571         if (!Context.Rip)
00572         {
00573             break;
00574         }
00575 
00576         /* Check, if we have left our stack */
00577         if ((Context.Rsp < StackLow) || (Context.Rsp > StackHigh))
00578         {
00579             break;
00580         }
00581 
00582         /* Save this frame and continue with new Rip */
00583         ControlPc = Context.Rip;
00584         Callers[i] = (PVOID)ControlPc;
00585     }
00586 
00587     DPRINT("RtlWalkFrameChain returns %ld\n", i);
00588     return i;
00589 }
00590 
00594 #undef RtlGetCallersAddress
00595 VOID
00596 NTAPI
00597 RtlGetCallersAddress(
00598     OUT PVOID *CallersAddress,
00599     OUT PVOID *CallersCaller )
00600 {
00601     PVOID Callers[4];
00602     ULONG Number;
00603 
00604     /* Get callers:
00605      * RtlWalkFrameChain -> RtlGetCallersAddress -> x -> y */
00606     Number = RtlWalkFrameChain(Callers, 4, 0);
00607 
00608     if (CallersAddress)
00609     {
00610         *CallersAddress = (Number >= 3) ? Callers[2] : NULL;
00611     }
00612     if (CallersCaller)
00613     {
00614         *CallersCaller = (Number == 4) ? Callers[3] : NULL;
00615     }
00616 
00617     return;
00618 }
00619 
00620 // FIXME: move to different file
00621 VOID
00622 NTAPI
00623 RtlRaiseException(IN PEXCEPTION_RECORD ExceptionRecord)
00624 {
00625     CONTEXT Context;
00626     NTSTATUS Status = STATUS_INVALID_DISPOSITION;
00627     ULONG64 ImageBase;
00628     PRUNTIME_FUNCTION FunctionEntry;
00629     PVOID HandlerData;
00630     ULONG64 EstablisherFrame;
00631 
00632     /* Capture the context */
00633     RtlCaptureContext(&Context);
00634 
00635     /* Get the function entry for this function */
00636     FunctionEntry = RtlLookupFunctionEntry(Context.Rip,
00637                                            &ImageBase,
00638                                            NULL);
00639 
00640     /* Check if we found it */
00641     if (FunctionEntry)
00642     {
00643         /* Unwind to the caller of this function */
00644         RtlVirtualUnwind(UNW_FLAG_NHANDLER,
00645                          ImageBase,
00646                          Context.Rip,
00647                          FunctionEntry,
00648                          &Context,
00649                          &HandlerData,
00650                          &EstablisherFrame,
00651                          NULL);
00652 
00653         /* Save the exception address */
00654         ExceptionRecord->ExceptionAddress = (PVOID)Context.Rip;
00655 
00656         /* Write the context flag */
00657         Context.ContextFlags = CONTEXT_FULL;
00658 
00659         /* Check if user mode debugger is active */
00660         if (RtlpCheckForActiveDebugger())
00661         {
00662             /* Raise an exception immediately */
00663             Status = ZwRaiseException(ExceptionRecord, &Context, TRUE);
00664         }
00665         else
00666         {
00667             /* Dispatch the exception and check if we should continue */
00668             if (!RtlDispatchException(ExceptionRecord, &Context))
00669             {
00670                 /* Raise the exception */
00671                 Status = ZwRaiseException(ExceptionRecord, &Context, FALSE);
00672             }
00673             else
00674             {
00675                 /* Continue, go back to previous context */
00676                 Status = ZwContinue(&Context, FALSE);
00677             }
00678         }
00679     }
00680 
00681     /* If we returned, raise a status */
00682     RtlRaiseStatus(Status);
00683 }
00684 

Generated on Sat May 26 2012 04:35:20 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.