static unsigned int our_stackwalk(ULONG_PTR retaddr, ULONG_PTR sp, PVOID *backtrace, unsigned int count) { /* derived from http://www.nynaeve.net/Code/StackWalk64.cpp */ CONTEXT ctx; DWORD64 imgbase; PRUNTIME_FUNCTION runfunc; KNONVOLATILE_CONTEXT_POINTERS nvctx; PVOID handlerdata; ULONG_PTR establisherframe; unsigned int frame; RtlCaptureContext(&ctx); for (frame = 0; frame < count; frame++) { backtrace[frame] = (PVOID)ctx.Rip; runfunc = RtlLookupFunctionEntry(ctx.Rip, &imgbase, NULL); memset(&nvctx, 0, sizeof(nvctx)); if (runfunc == NULL) { ctx.Rip = (ULONG_PTR)(*(ULONG_PTR *)ctx.Rsp); ctx.Rsp += 8; } else { RtlVirtualUnwind(UNW_FLAG_NHANDLER, imgbase, ctx.Rip, runfunc, &ctx, &handlerdata, &establisherframe, &nvctx); } if (!ctx.Rip) break; } return frame + 1; }
static void __cdecl capture_current_context(CONTEXT* pContextRecord) { ULONG64 ControlPc; ULONG64 EstablisherFrame; ULONG64 ImageBase; PRUNTIME_FUNCTION FunctionEntry; PVOID HandlerData; RtlCaptureContext(pContextRecord); ControlPc = pContextRecord->Rip; FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL); if (FunctionEntry != NULL) { RtlVirtualUnwind( UNW_FLAG_NHANDLER, ImageBase, ControlPc, FunctionEntry, pContextRecord, &HandlerData, &EstablisherFrame, NULL); } }
// // Returns the establisher frame pointers. For catch handlers it is the parent's frame pointer. // EHRegistrationNode * __cdecl _GetEstablisherFrame( EHRegistrationNode *pRN, DispatcherContext *pDC, FuncInfo *pFuncInfo, EHRegistrationNode *pEstablisher ) { TryBlockMapEntry *pEntry; HandlerType *pHandler; ULONG_PTR HandlerAdd, ImageBase; unsigned num_of_try_blocks = FUNC_NTRYBLOCKS(*pFuncInfo); unsigned index, i; __ehstate_t curState; curState = __StateFromControlPc(pFuncInfo, pDC); *pEstablisher = *pRN; for (index = num_of_try_blocks; index > 0; index--) { pEntry = FUNC_PTRYBLOCK(*pFuncInfo, index -1, pDC->ImageBase); if (curState > TBME_HIGH(*pEntry) && curState <= TBME_CATCHHIGH(*pEntry)) { // Get catch handler address. HandlerAdd = (*RtlLookupFunctionEntry(pDC->ControlPc, &ImageBase, nullptr)).BeginAddress; pHandler = TBME_PLIST(*pEntry, ImageBase); for ( i = 0; i < (unsigned)TBME_NCATCHES(*pEntry) && static_cast<ULONG_PTR>(pHandler[i].dispOfHandler) != HandlerAdd ; i++); if ( i < (unsigned)TBME_NCATCHES(*pEntry)) { *pEstablisher = *(EHRegistrationNode *)OffsetToAddress(pHandler[i].dispFrame, *pRN); break; } } } return pEstablisher; }
static DWORD64 WINAPI JuliaGetModuleBase64( _In_ HANDLE hProcess, _In_ DWORD64 dwAddr) { //jl_printf(JL_STDOUT, "lookup base %d\n", dwAddr); #ifdef _CPU_X86_64_ DWORD64 ImageBase; PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry(dwAddr, &ImageBase, &HistoryTable); if (fn) return ImageBase; if (jl_in_stackwalk) { return 0; } jl_in_stackwalk = 1; DWORD64 fbase = SymGetModuleBase64(hProcess, dwAddr); jl_in_stackwalk = 0; return fbase; #else if (dwAddr == HistoryTable.dwAddr) return HistoryTable.ImageBase; DWORD64 ImageBase = jl_getUnwindInfo(dwAddr); if (ImageBase) { HistoryTable.dwAddr = dwAddr; HistoryTable.ImageBase = ImageBase; return ImageBase; } return SymGetModuleBase64(hProcess, dwAddr); #endif }
// returns the size (number of bytes) in an X64 function ULONG Getx64FunctionSize ( PVOID Function ) { PIMAGE_RUNTIME_FUNCTION_ENTRY RuntimeFunction; ULONGLONG ImageBase = 0; ULONG FunctionSize = 0; // RtlLookupFunctionEntry() locates the start and end of the function // using the X64 exception table information in the PE file // RtlLookupFunctionEntry() is only available on X64 systems // using it makes this this module incompatible with x86 systems RuntimeFunction = (PIMAGE_RUNTIME_FUNCTION_ENTRY)RtlLookupFunctionEntry( (ULONGLONG)Function, &ImageBase, NULL ); // if there is no valid runtime entry structure then the extents of // the functions cannot be determined if ( ! RuntimeFunction ) { DPF(("%s!%s RtlLookupFunctionEntry(%p)\n", __MODULE__, __FUNCTION__, Function )); goto Exit; } // compute the number of functions in the function body based on the // functions extents, note this logic will not work for BBT'd functions FunctionSize = RuntimeFunction->EndAddress - RuntimeFunction->BeginAddress; DPF(("%s!%s PRUNTIME_FUNCTION(BeginAddress=0x%08x EndAddress=0x%08x) Size=%u\n", __MODULE__, __FUNCTION__, RuntimeFunction->BeginAddress, RuntimeFunction->EndAddress, FunctionSize )); Exit : return FunctionSize; } // Getx64FunctionSize()
/* static */ void XDataAllocator::Register(XDataAllocation * xdataInfo, ULONG_PTR functionStart, DWORD functionSize) { #ifdef _WIN32 ULONG_PTR baseAddress = functionStart; xdataInfo->pdata.BeginAddress = (DWORD)(functionStart - baseAddress); xdataInfo->pdata.UnwindData = (DWORD)((intptr_t)xdataInfo->address - baseAddress); NTSTATUS status = NtdllLibrary::Instance->AddGrowableFunctionTable(&xdataInfo->functionTable, &xdataInfo->pdata, /*MaxEntryCount*/ 1, /*Valid entry count*/ 1, /*RangeBase*/ functionStart, /*RangeEnd*/ functionStart + functionSize); BOOLEAN success = NT_SUCCESS(status); Assert(!success || xdataInfo->functionTable != nullptr); if (!success) { Js::Throw::XDataRegistrationError(status, functionStart); } #if DBG // Validate that the PDATA registration succeeded ULONG64 imageBase = 0; RUNTIME_FUNCTION *runtimeFunction = RtlLookupFunctionEntry((DWORD64)functionStart, &imageBase, nullptr); Assert(runtimeFunction != NULL); #endif #else // !_WIN32 Assert(ReadHead(xdataInfo->address)); // should be non-empty .eh_frame __REGISTER_FRAME(xdataInfo->address); #endif }
__declspec(noreturn) void __cdecl __report_gsfailure (ULONG_PTR StackCookie) { volatile UINT_PTR cookie[2] __MINGW_ATTRIB_UNUSED; #if defined(_WIN64) && !defined(__aarch64__) ULONG64 controlPC, imgBase, establisherFrame; PRUNTIME_FUNCTION fctEntry; PVOID hndData; RtlCaptureContext (&GS_ContextRecord); controlPC = GS_ContextRecord.Rip; fctEntry = RtlLookupFunctionEntry (controlPC, &imgBase, NULL); if (fctEntry != NULL) { RtlVirtualUnwind (UNW_FLAG_NHANDLER, imgBase, controlPC, fctEntry, &GS_ContextRecord, &hndData, &establisherFrame, NULL); } else #endif /* _WIN64 */ { #if defined(__x86_64__) || defined(_AMD64_) GS_ContextRecord.Rip = (ULONGLONG) _ReturnAddress(); GS_ContextRecord.Rsp = (ULONGLONG) _AddressOfReturnAddress() + 8; #elif defined(__i386__) || defined(_X86_) GS_ContextRecord.Eip = (DWORD) _ReturnAddress(); GS_ContextRecord.Esp = (DWORD) _AddressOfReturnAddress() + 4; #elif defined(__arm__) || defined(_ARM_) GS_ContextRecord.Pc = (DWORD) _ReturnAddress(); GS_ContextRecord.Sp = (DWORD) _AddressOfReturnAddress() + 4; #endif /* _WIN64 */ } #if defined(__x86_64__) || defined(_AMD64_) GS_ExceptionRecord.ExceptionAddress = (PVOID) GS_ContextRecord.Rip; GS_ContextRecord.Rcx = StackCookie; #elif defined(__i386__) || defined(_X86_) GS_ExceptionRecord.ExceptionAddress = (PVOID) GS_ContextRecord.Eip; GS_ContextRecord.Ecx = StackCookie; #elif defined(__arm__) || defined(_ARM_) GS_ExceptionRecord.ExceptionAddress = (PVOID) GS_ContextRecord.Pc; UNUSED_PARAM(StackCookie); #endif /* _WIN64 */ GS_ExceptionRecord.ExceptionCode = STATUS_STACK_BUFFER_OVERRUN; GS_ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; cookie[0] = __security_cookie; cookie[1] = __security_cookie_complement; SetUnhandledExceptionFilter (NULL); UnhandledExceptionFilter ((EXCEPTION_POINTERS *) &GS_ExceptionPointers); TerminateProcess (GetCurrentProcess (), STATUS_STACK_BUFFER_OVERRUN); abort(); }
void longjmpex( jmp_buf jb, int ret ) { EXCEPTION_RECORD er; _JUMPEXDATA *jd = (_JUMPEXDATA *)jb; er.ExceptionCode = 0xE5670123; er.ExceptionFlags = 0x00000002; er.ExceptionRecord = 0L; er.ExceptionAddress = 0L; er.NumberParameters = 1L; er.ExceptionInformation[0] = jd->sp; if( !_ProcSetsFP( RtlLookupFunctionEntry( jd->pc ) ) ) RtlUnwind( (void *)jd->sp, (void *)jd->pc, &er, (void *)ret ); else RtlUnwindRfp( jd->fp, jd->pc, &er, ret ); } /* longjmp() */
static PVOID CALLBACK JuliaFunctionTableAccess64( _In_ HANDLE hProcess, _In_ DWORD64 AddrBase) { //jl_printf(JL_STDOUT, "lookup %d\n", AddrBase); #ifdef _CPU_X86_64_ DWORD64 ImageBase; PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry(AddrBase, &ImageBase, &HistoryTable); if (fn) return fn; if (jl_in_stackwalk) { return 0; } jl_in_stackwalk = 1; PVOID ftable = SymFunctionTableAccess64(hProcess, AddrBase); jl_in_stackwalk = 0; return ftable; #else return SymFunctionTableAccess64(hProcess, AddrBase); #endif }
/* static */ void XDataAllocator::Register(XDataAllocation * xdataInfo, ULONG_PTR functionStart, DWORD functionSize) { #ifdef _WIN32 ULONG_PTR baseAddress = functionStart; xdataInfo->pdata.BeginAddress = (DWORD)(functionStart - baseAddress); xdataInfo->pdata.EndAddress = (DWORD)(xdataInfo->pdata.BeginAddress + functionSize); xdataInfo->pdata.UnwindInfoAddress = (DWORD)((intptr_t)xdataInfo->address - baseAddress); BOOLEAN success = FALSE; if (AutoSystemInfo::Data.IsWin8OrLater()) { DWORD status = NtdllLibrary::Instance->AddGrowableFunctionTable(&xdataInfo->functionTable, &xdataInfo->pdata, /*MaxEntryCount*/ 1, /*Valid entry count*/ 1, /*RangeBase*/ functionStart, /*RangeEnd*/ functionStart + functionSize); success = NT_SUCCESS(status); if (success) { Assert(xdataInfo->functionTable != nullptr); } } else { success = RtlAddFunctionTable(&xdataInfo->pdata, 1, functionStart); } Js::Throw::CheckAndThrowOutOfMemory(success); #if DBG // Validate that the PDATA registration succeeded ULONG64 imageBase = 0; RUNTIME_FUNCTION *runtimeFunction = RtlLookupFunctionEntry((DWORD64)functionStart, &imageBase, nullptr); Assert(runtimeFunction != NULL); #endif #else // !_WIN32 Assert(ReadHead(xdataInfo->address)); // should be non-empty .eh_frame __register_frame(xdataInfo->address); #endif }
static void __cdecl capture_previous_context(CONTEXT* pContextRecord) { ULONG64 ControlPc; ULONG64 EstablisherFrame; ULONG64 ImageBase; PRUNTIME_FUNCTION FunctionEntry; PVOID HandlerData; INT frames; RtlCaptureContext(pContextRecord); ControlPc = pContextRecord->Rip; // Unwind "twice" to get the context of the caller to the "previous" caller: for (frames = 0; frames < 2; ++frames) { FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL); if (FunctionEntry != NULL) { RtlVirtualUnwind( UNW_FLAG_NHANDLER, ImageBase, ControlPc, FunctionEntry, pContextRecord, &HandlerData, &EstablisherFrame, NULL); } else { break; } } }
VOID RtlGetCallersAddress ( OUT PVOID *CallersPc, OUT PVOID *CallersCallersPc ) /*++ Routine Description: This routine returns the address of the routine that called the routine that called this routine, and the routine that called the routine that called this routine. For example, if A called B called C which called this routine, the return addresses in A and B would be returned. Arguments: CallersPc - Supplies a pointer to a variable that receives the address of the caller of the caller of this routine (B). CallersCallersPc - Supplies a pointer to a variable that receives the address of the caller of the caller of the caller of this routine (A). Return Value: None. Note: If either of the calling stack frames exceeds the limits of the stack, they are set to NULL. --*/ { CONTEXT ContextRecord; ULONG EstablisherFrame; PRUNTIME_FUNCTION FunctionEntry; BOOLEAN InFunction; ULONG NextPc; ULONG HighLimit, LowLimit; // // Assume the function table entries for the various routines cannot be // found or there are not four procedure activation records on the stack. // *CallersPc = NULL; *CallersCallersPc = NULL; // // Capture the current context. // RtlCaptureContext(&ContextRecord); NextPc = (ULONG)ContextRecord.XIntRa; // // Get the high and low limits of the current thread's stack. // RtlpGetStackLimits(&LowLimit, &HighLimit); // // Attempt to unwind to the caller of this routine (C). // FunctionEntry = RtlLookupFunctionEntry(NextPc); if (FunctionEntry != NULL) { // // A function entry was found for this routine. Virtually unwind // to the caller of this routine (C). // NextPc = RtlVirtualUnwind(NextPc | 1, FunctionEntry, &ContextRecord, &InFunction, &EstablisherFrame, NULL); // // Attempt to unwind to the caller of the caller of this routine (B). // FunctionEntry = RtlLookupFunctionEntry(NextPc); if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.XIntSp < HighLimit)) { // // A function table entry was found for the caller of the caller // of this routine (B). Virtually unwind to the caller of the // caller of this routine (B). // NextPc = RtlVirtualUnwind(NextPc | 1, FunctionEntry, &ContextRecord, &InFunction, &EstablisherFrame, NULL); *CallersPc = (PVOID)NextPc; // // Attempt to unwind to the caller of the caller of the caller // of the caller of this routine (A). // FunctionEntry = RtlLookupFunctionEntry(NextPc); if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.XIntSp < HighLimit)) { // // A function table entry was found for the caller of the // caller of the caller of this routine (A). Virtually unwind // to the caller of the caller of the caller of this routine // (A). // NextPc = RtlVirtualUnwind(NextPc | 1, FunctionEntry, &ContextRecord, &InFunction, &EstablisherFrame, NULL); *CallersCallersPc = (PVOID)NextPc; } } } return; }
VOID ExpRaiseException ( IN PEXCEPTION_RECORD ExceptionRecord ) /*++ Routine Description: This function raises a software exception by building a context record and calling the exception dispatcher directly. Arguments: ExceptionRecord - Supplies a pointer to an exception record. Return Value: None. --*/ { ULONG ControlPc; CONTEXT ContextRecord; ULONG EstablisherFrame; PRUNTIME_FUNCTION FunctionEntry; BOOLEAN InFunction; ULONG NextPc; NTSTATUS Status; // // Capture the current context, virtually unwind to the caller of this // routine, set the fault instruction address to that of the caller, and // call the exception dispatcher. // RtlCaptureContext(&ContextRecord); ControlPc = ContextRecord.Lr - 4; FunctionEntry = RtlLookupFunctionEntry(ControlPc); NextPc = RtlVirtualUnwind(ControlPc, FunctionEntry, &ContextRecord, &InFunction, &EstablisherFrame, NULL, 0, 0xffffffff); ContextRecord.Iar = NextPc + 4; ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Iar; // // If the exception is successfully dispatched, then continue execution. // Otherwise, give the kernel debugger a chance to handle the exception. // if (RtlDispatchException(ExceptionRecord, &ContextRecord)) { Status = ZwContinue(&ContextRecord, FALSE); } else { Status = ZwRaiseException(ExceptionRecord, &ContextRecord, FALSE); } // // Either the attempt to continue execution or the attempt to give // the kernel debugger a chance to handle the exception failed. Raise // a noncontinuable exception. // ExRaiseStatus(Status); }
int __gnat_backtrace (void **array, int size, void *exclude_min, void *exclude_max, int skip_frames) { CONTEXT context; UNWIND_HISTORY_TABLE history; int i; /* Get the context. */ RtlCaptureContext (&context); /* Setup unwind history table (a cached to speed-up unwinding). */ memset (&history, 0, sizeof (history)); i = 0; while (1) { PRUNTIME_FUNCTION RuntimeFunction; KNONVOLATILE_CONTEXT_POINTERS NvContext; ULONG64 ImageBase; VOID *HandlerData; ULONG64 EstablisherFrame; /* Get function metadata. */ RuntimeFunction = RtlLookupFunctionEntry (context.Rip, &ImageBase, &history); if (!RuntimeFunction) { /* In case of failure, assume this is a leaf function. */ context.Rip = *(ULONG64 *) context.Rsp; context.Rsp += 8; } else { /* Unwind. */ memset (&NvContext, 0, sizeof (KNONVOLATILE_CONTEXT_POINTERS)); RtlVirtualUnwind (0, ImageBase, context.Rip, RuntimeFunction, &context, &HandlerData, &EstablisherFrame, &NvContext); } /* 0 means bottom of the stack. */ if (context.Rip == 0) break; /* Skip frames. */ if (skip_frames > 1) { skip_frames--; continue; } /* Excluded frames. */ if ((void *)context.Rip >= exclude_min && (void *)context.Rip <= exclude_max) continue; array[i++] = (void *)(context.Rip - 2); if (i >= size) break; } return i; }
int setjmp ( IN jmp_buf JumpBuffer ) /*++ Routine Description: This function performs a set jump operation by capturing the current context, virtualy unwinding to the caller of set jump, and returns zero to the caller. Arguments: JumpBuffer - Supplies the address of a jump buffer to store the virtual frame pointer and target address of the caller. N.B. This is an array of double to force quadword alignment. Return Value: A value of zero is returned. --*/ { CONTEXT ContextRecord; ULONG EstablisherFrame; PRUNTIME_FUNCTION FunctionEntry; BOOLEAN InFunction; PULONG JumpArray; ULONG NextPc; // // Capture the current context, virtually unwind to the caller of set // jump, and return zero to the caller. // JumpArray = (PULONG)&JumpBuffer[0]; RtlCaptureContext(&ContextRecord); NextPc = ContextRecord.Lr - 4; FunctionEntry = RtlLookupFunctionEntry(NextPc); NextPc = RtlVirtualUnwind(NextPc, FunctionEntry, &ContextRecord, &InFunction, &EstablisherFrame, NULL, 0, 0xffffffff); JumpArray[1] = NextPc + 4; FunctionEntry = RtlLookupFunctionEntry(NextPc); NextPc = RtlVirtualUnwind(NextPc, FunctionEntry, &ContextRecord, &InFunction, &EstablisherFrame, NULL, 0, 0xffffffff); JumpArray[0] = EstablisherFrame; return 0; }
void RtlUnwindActions2 ( IN PVOID TargetFrame OPTIONAL, IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, IN PCONTEXT ContextRecord ) /*++ Routine Description: This function initiates an unwind of procedure call frames. The machine state at the time of the call to unwind is captured in a context record and the unwinding flag is set in the exception flags of the exception record. If the TargetFrame parameter is not specified, then the exit unwind flag is also set in the exception flags of the exception record. A backward scan through the procedure call frames is then performed to find the target of the unwind operation. As each frame is encounter, the PC where control left the corresponding function is determined and used to lookup exception handler information in the runtime function table built by the linker. If the respective routine has an exception handler, then the handler is called. N.B. This routine is provided for backward compatibility with release 1. Arguments: TargetFrame - Supplies an optional pointer to the call frame that is the target of the unwind. If this parameter is not specified, then an exit unwind is performed. TargetIp - Supplies an optional instruction address that specifies the continuation address of the unwind. This address is ignored if the target frame parameter is not specified. ExceptionRecord - Supplies an optional pointer to an exception record. ReturnValue - Supplies a value that is to be placed in the integer function return register just before continuing execution. Return Value: None. --*/ { // These were originally parameters but are not // not needed in this unwinding variant because // no context switch to TargetIp will be performed. PVOID TargetIp = NULL; PVOID ReturnValue = NULL; ULONG ControlPc; #if EH_DBG ULONG ControlPcHistory[PC_HISTORY_DEPTH]; ULONG ControlPcHistoryIndex = 0; #endif DISPATCHER_CONTEXT DispatcherContext; EXCEPTION_DISPOSITION Disposition; FRAME_POINTERS EstablisherFrame; ULONG ExceptionFlags; EXCEPTION_RECORD ExceptionRecord1; #if EH_DBG LONG FrameDepth = 0; #endif PRUNTIME_FUNCTION FunctionEntry; ULONG HighLimit; BOOLEAN InFunction; ULONG LowLimit; ULONG NextPc; #if EH_DBG if (EHRtlDebugFlags & RTL_DBG_UNWIND) { DbgPrint("\nRtlUnwindActions2(TargetFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n", TargetFrame, TargetIp, ReturnValue); } #endif // // Get current stack limits, capture the current context, virtually // unwind to the caller of this routine, get the initial PC value, and // set the unwind target address. // __CxxGetStackLimits(&LowLimit, &HighLimit); RtlCaptureContext(ContextRecord); ControlPc = (ULONG)ContextRecord->IntRa - 4; FunctionEntry = RtlLookupFunctionEntry(ControlPc); NextPc = RtlVirtualUnwind(ControlPc, FunctionEntry, ContextRecord, &InFunction, &EstablisherFrame, NULL); ControlPc = NextPc; ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp; // // If an exception record is not specified, then build a local exception // record for use in calling exception handlers during the unwind operation. // if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { ExceptionRecord = &ExceptionRecord1; ExceptionRecord1.ExceptionCode = STATUS_UNWIND; ExceptionRecord1.ExceptionRecord = NULL; ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc; ExceptionRecord1.NumberParameters = 0; } // // If the target frame of the unwind is specified, then a normal unwind // is being performed. Otherwise, an exit unwind is being performed. // ExceptionFlags = EXCEPTION_UNWINDING; if (ARGUMENT_PRESENT(TargetFrame) == FALSE) { ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND; } // // Scan backward through the call frame hierarchy and call exception // handlers until the target frame of the unwind is reached. // do { #if EH_DBG if (EHRtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { DbgPrint("RtlUnwindActions2: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n", FrameDepth, ContextRecord->IntSp, ControlPc); FrameDepth -= 1; } #endif // // Lookup the function table entry using the point at which control // left the procedure. // FunctionEntry = RtlLookupFunctionEntry(ControlPc); // // If there is a function table entry for the routine, then virtually // unwind to the caller of the routine to obtain the virtual frame // pointer of the establisher, but don't update the context record. // if (FunctionEntry != NULL) { { CONTEXT LocalContext; RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT)); NextPc = RtlVirtualUnwind(ControlPc, FunctionEntry, &LocalContext, &InFunction, &EstablisherFrame, NULL); } // // If the virtual frame pointer is not within the specified stack // limits, the virtual frame pointer is unaligned, or the target // frame is below the virtual frame and an exit unwind is not being // performed, then raise the exception STATUS_BAD_STACK. Otherwise, // check to determine if the current routine has an exception // handler. // if ((EstablisherFrame.Virtual < LowLimit) || (EstablisherFrame.Virtual > HighLimit) || ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && ((ULONG)TargetFrame < EstablisherFrame.Virtual)) || ((EstablisherFrame.Virtual & 0xF) != 0)) { #if EH_DBG DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n"); DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n", EstablisherFrame.Virtual, EstablisherFrame.Real); DbgPrint(" TargetFrame = %08lx\n", TargetFrame); if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && ((ULONG)TargetFrame < EstablisherFrame.Virtual)) { DbgPrint(" TargetFrame is below EstablisherFrame!\n"); } DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n", (ULONG)ContextRecord->IntSp); DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n", LowLimit, HighLimit); DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n", NextPc, ControlPc); DbgPrint(" Now raising STATUS_BAD_STACK exception.\n"); #endif _write(2, "C++ EH run-time failure 2 - aborting\n", 37); abort(); } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) { #if EH_DBG if (EHRtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { DbgPrint("RtlUnwindActions2: ExceptionHandler = %lx, HandlerData = %lx\n", FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData); } #endif // // The frame has an exception handler. // // The control PC, establisher frame pointer, the address // of the function table entry, and the address of the // context record are all stored in the dispatcher context. // This information is used by the unwind linkage routine // and can be used by the exception handler itself. // // A linkage routine written in assembler is used to actually // call the actual exception handler. This is required by the // exception handler that is associated with the linkage // routine so it can have access to two sets of dispatcher // context when it is called. // DispatcherContext.ControlPc = ControlPc; DispatcherContext.FunctionEntry = FunctionEntry; DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual; DispatcherContext.ContextRecord = ContextRecord; // // Call the exception handler. // do { // // If the establisher frame is the target of the unwind // operation, then set the target unwind flag. // if ((ULONG)TargetFrame == EstablisherFrame.Virtual) { ExceptionFlags |= EXCEPTION_TARGET_UNWIND; } ExceptionRecord->ExceptionFlags = ExceptionFlags; // // Set the specified return value in case the exception // handler directly continues execution. // ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue; #if EH_DBG if (EHRtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { DbgPrint("RtlUnwindActions2: calling __CxxExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc); } #endif Disposition = __CxxExecuteHandlerForUnwind(ExceptionRecord, (PVOID)EstablisherFrame.Virtual, ContextRecord, &DispatcherContext, FunctionEntry->ExceptionHandler); #if EH_DBG if (EHRtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { DbgPrint("RtlUnwindActions2: __CxxExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition); } #endif // // Clear target unwind and collided unwind flags. // ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND | EXCEPTION_TARGET_UNWIND); // // Case on the handler disposition. // switch (Disposition) { // // The disposition is to continue the search. // // If the target frame has not been reached, then // virtually unwind to the caller of the current // routine, update the context record, and continue // the search for a handler. // case ExceptionContinueSearch : if (EstablisherFrame.Virtual != (ULONG)TargetFrame) { NextPc = RtlVirtualUnwind(ControlPc, FunctionEntry, ContextRecord, &InFunction, &EstablisherFrame, NULL); } break; // // The disposition is collided unwind. // // Set the target of the current unwind to the context // record of the previous unwind, and reexecute the // exception handler from the collided frame with the // collided unwind flag set in the exception record. // case ExceptionCollidedUnwind : ControlPc = DispatcherContext.ControlPc; FunctionEntry = DispatcherContext.FunctionEntry; ContextRecord = DispatcherContext.ContextRecord; ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp; ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; EstablisherFrame.Virtual = DispatcherContext.EstablisherFrame; break; // // All other disposition values are invalid. // // Raise invalid disposition exception. // default : _write(2, "C++ EH run-time failure 3 - aborting\n", 37); abort(); } } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0); } else { // // Virtually unwind to the caller of the current routine and // update the context record. // if (EstablisherFrame.Virtual != (ULONG)TargetFrame) { NextPc = RtlVirtualUnwind(ControlPc, FunctionEntry, ContextRecord, &InFunction, &EstablisherFrame, NULL); } } } else { // // Set point at which control left the previous routine. // NextPc = (ULONG)ContextRecord->IntRa - 4; // // If the next control PC is the same as the old control PC, then // the function table is not correctly formed. // if (NextPc == ControlPc) { #if EH_DBG ULONG Count; DbgPrint("\n****** Warning - malformed function table (unwind).\n"); DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc); for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) { if (ControlPcHistoryIndex > 0) { ControlPcHistoryIndex -= 1; ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH]; DbgPrint(", %08lx", ControlPc); } } DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n"); DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n"); #endif _write(2, "C++ EH run-time failure 4 - aborting\n", 37); abort(); } } // // Set point at which control left the previous routine. // #if EH_DBG ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc; ControlPcHistoryIndex += 1; #endif ControlPc = NextPc; } while ((EstablisherFrame.Virtual < HighLimit) && (EstablisherFrame.Virtual != (ULONG)TargetFrame)); // // If the establisher stack pointer is equal to the target frame // pointer, then continue execution. // Otherwise, something truely horrible has happend. // if (EstablisherFrame.Virtual == (ULONG)TargetFrame) { ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue; #if EH_DBG if (EHRtlDebugFlags & RTL_DBG_UNWIND) { DbgPrint("RtlUnwindActions2: finished unwinding, and calling RtlpRestoreContext(%lx)\n",ContextRecord); } #endif // Do not restore the context here. // For C++ we need to "logicaly" unwind // the stack, perform several actions, // and then finally "physically" unwind // the stack. // RtlpRestoreContext(ContextRecord); return; } else { _write(2, "C++ EH run-time failure 1 - aborting\n", 37); abort(); } }
_WCRTLINK _EXCINFO *_SetPData( _EXCINFO *info ) { info->FunctionEntry = RtlLookupFunctionEntry( info->ControlPC ); return info; }
_Use_decl_annotations_ EXTERN_C IMAGE_RUNTIME_FUNCTION_ENTRY * FnparseLookupFunctionEntry(FARPROC FunctionAddress, PVOID *ImageBase) { PAGED_CODE(); return RtlLookupFunctionEntry(FunctionAddress, ImageBase, nullptr); }
VOID ExpRaiseStatus ( IN NTSTATUS ExceptionCode ) /*++ Routine Description: This function raises an exception with the specified status value by building an exception record, building a context record, and calling the exception dispatcher directly. The exception is marked as noncontinuable with no parameters. There is no return from this function. Arguments: ExceptionCode - Supplies the status value to be used as the exception code for the exception that is to be raised. Return Value: None. --*/ { ULONG ControlPc; CONTEXT ContextRecord; ULONG EstablisherFrame; EXCEPTION_RECORD ExceptionRecord; PRUNTIME_FUNCTION FunctionEntry; BOOLEAN InFunction; ULONG NextPc; NTSTATUS Status; // // Construct an exception record. // ExceptionRecord.ExceptionCode = ExceptionCode; ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL; ExceptionRecord.NumberParameters = 0; ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; // // Capture the current context, virtually unwind to the caller of this // routine, set the fault instruction address to that of the caller, and // call the exception dispatcher. // RtlCaptureContext(&ContextRecord); ControlPc = ContextRecord.Lr - 4; FunctionEntry = RtlLookupFunctionEntry(ControlPc); NextPc = RtlVirtualUnwind(ControlPc, FunctionEntry, &ContextRecord, &InFunction, &EstablisherFrame, NULL, 0, 0xffffffff); ContextRecord.Iar = NextPc + 4; ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Iar; RtlDispatchException(&ExceptionRecord, &ContextRecord); // // An unwind was not initiated during the dispatching of a noncontinuable // exception. Give the kernel debugger a chance to handle the exception. // Status = ZwRaiseException(&ExceptionRecord, &ContextRecord, FALSE); // // The attempt to give the kernel debugger a chance to handle the exception // failed. Raise another noncontinuable exception. // ExRaiseStatus(Status); }
void StackDump( LPVOID pMem, SIZE_T dwBytes) { CONTEXT Context; //KNONVOLATILE_CONTEXT_POINTERS NvContext; //UNWIND_HISTORY_TABLE UnwindHistoryTable; PRUNTIME_FUNCTION RuntimeFunction; PVOID HandlerData; ULONG64 EstablisherFrame; ULONG64 ImageBase; //OutputDebugString(L"StackTrace64: Executing stack trace...\n"); // // First, we'll get the caller's context. // RtlCaptureContext(&Context); // // Initialize the (optional) unwind history table. // /*RtlZeroMemory( &UnwindHistoryTable, sizeof(UNWIND_HISTORY_TABLE));*/ //BYTE SymBol[ sizeof(SYMBOL_INFO) + STACKWALK_MAX_NAMELEN ] = {0}; //SYMBOL_INFO* pSymbol = (SYMBOL_INFO*)SymBol; //DWORD64 dwDisplacement; HANDLE hProcess = GetCurrentProcess(); MEM_INFO stInfo; //stInfo.parCallStack = new STACK_ARRAY; //void * p = AllocMem(sizeof(STACK_ARRAY)); //stInfo.parCallStack = new( (void*)p ) STACK_ARRAY; stInfo.nMemSize = dwBytes; for( int i =0; i < g_StackDepth ; i++ )// only retrieve 40 functions { // // Try to look up unwind metadata for the current function. // RuntimeFunction = RtlLookupFunctionEntry( Context.Rip, &ImageBase, NULL ); /*RtlZeroMemory( &NvContext, sizeof(KNONVOLATILE_CONTEXT_POINTERS));*/ if (!RuntimeFunction) { // // If we don't have a RUNTIME_FUNCTION, then we've encountered // a leaf function. Adjust the stack approprately. // Context.Rip = (ULONG64)(*(PULONG64)Context.Rsp); Context.Rsp += 8; } else { // // Otherwise, call upon RtlVirtualUnwind to execute the unwind for // us. // RtlVirtualUnwind( 0, //UNW_FLAG_NHANDLER, ImageBase, Context.Rip, RuntimeFunction, &Context, &HandlerData, &EstablisherFrame, NULL ); } // // If we reach an RIP of zero, this means that we've walked off the end // of the call stack and are done. // if (!Context.Rip) break; ////////////////////////////////////////////////////////////////////////// //if( SymFromAddr( hProcess, Context.Rip, &dwDisplacement, pSymbol )) //{ // CString cs = "Ordinal823"; // if( cs == pSymbol->Name) // { // break; // } // //} ////////////////////////////////////////////////////////////////////////// if( i <= 1 )// ignore the functions on the top of stack which is our own. { continue; } stInfo.parCallStack.push_back( Context.Rip ); } g_Config::m_MemMap[pMem] = stInfo; }
static __EXC_INFO* setPdata // SET PDATA INFORMATION ( __EXC_INFO* info ) // - exception info { info->dctx.pdata = (PData*)RtlLookupFunctionEntry( info->dctx.pc ); return info; }