diff options
Diffstat (limited to 'src/inc/regdisp.h')
-rw-r--r-- | src/inc/regdisp.h | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/src/inc/regdisp.h b/src/inc/regdisp.h new file mode 100644 index 0000000000..09a6a38f62 --- /dev/null +++ b/src/inc/regdisp.h @@ -0,0 +1,584 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#ifndef __REGDISP_H +#define __REGDISP_H + + +#ifdef DEBUG_REGDISPLAY +class Thread; +#endif // DEBUG_REGDISPLAY + + +#if defined(_TARGET_X86_) + +struct REGDISPLAY { + PCONTEXT pContext; // points to current Context; either + // returned by GetContext or provided + // at exception time. + + // TODO: Unify with pCurrentContext / pCallerContext used on 64-bit + PCONTEXT pContextForUnwind; // scratch context for unwinding + // used to preserve context saved in the frame that + // could be otherwise wiped by the unwinding + + DWORD * pEdi; + DWORD * pEsi; + DWORD * pEbx; + DWORD * pEdx; + DWORD * pEcx; + DWORD * pEax; + + DWORD * pEbp; + DWORD Esp; // (Esp) Stack Pointer + PCODE ControlPC; + TADDR PCTAddr; + +}; + +inline TADDR GetRegdisplaySP(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + + return (TADDR)display->Esp; +} + +inline void SetRegdisplaySP(REGDISPLAY *display, LPVOID sp ) { + LIMITED_METHOD_DAC_CONTRACT; + + (display->Esp) = (DWORD)(size_t)sp; +} + +inline TADDR GetRegdisplayFP(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + + return (TADDR)*(display->pEbp); +} + +inline LPVOID GetRegdisplayFPAddress(REGDISPLAY *display) { + LIMITED_METHOD_CONTRACT; + + return (LPVOID)display->pEbp; +} + +inline PCODE GetControlPC(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + + return display->ControlPC; +} + +// This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame +inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) { + LIMITED_METHOD_CONTRACT; + + return (TADDR)stackPointer < display->PCTAddr; +} +inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + + return display->PCTAddr; +} + +#elif defined(_WIN64) + +#if defined(_TARGET_ARM64_) +typedef struct _Arm64VolatileContextPointer +{ + union { + struct { + PDWORD64 X0; + PDWORD64 X1; + PDWORD64 X2; + PDWORD64 X3; + PDWORD64 X4; + PDWORD64 X5; + PDWORD64 X6; + PDWORD64 X7; + PDWORD64 X8; + PDWORD64 X9; + PDWORD64 X10; + PDWORD64 X11; + PDWORD64 X12; + PDWORD64 X13; + PDWORD64 X14; + PDWORD64 X15; + PDWORD64 X16; + PDWORD64 X17; + //X18 is reserved by OS, in userspace it represents TEB + }; + PDWORD64 X[18]; + }; +} Arm64VolatileContextPointer; +#endif //_TARGET_ARM64_ +struct REGDISPLAY { + PT_CONTEXT pContext; // This is the context of the active call frame. This + // will be used to resume execution, so do not use trash it! + // But DO update any static registers here. + + PT_CONTEXT pCurrentContext; // [trashed] points to current Context of stackwalk + PT_CONTEXT pCallerContext; // [trashed] points to the Context of the caller during stackwalk -- used for GC crawls + + size_t ControlPC; + + size_t SP; + + T_KNONVOLATILE_CONTEXT_POINTERS *pCurrentContextPointers; // [trashed] points to current context pointers of stackwalk + T_KNONVOLATILE_CONTEXT_POINTERS *pCallerContextPointers; // [trashed] points to the context pointers of the caller during stackwalk -- used for GC crawls +#ifdef _TARGET_ARM64_ + Arm64VolatileContextPointer volatileCurrContextPointers; +#endif + + BOOL IsCallerContextValid; // TRUE if pCallerContext really contains the caller's context + BOOL IsCallerSPValid; // Don't add usage of this field. This is only temporary. + + T_CONTEXT ctxOne; // used by stackwalk + T_CONTEXT ctxTwo; // used by stackwalk + + T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsOne; // used by stackwalk + T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsTwo; // used by stackwalk + +#ifdef DEBUG_REGDISPLAY + Thread *_pThread; +#endif // DEBUG_REGDISPLAY +}; + +inline TADDR GetRegdisplaySP(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + return (TADDR)display->SP; +} + +inline TADDR GetRegdisplayFP(REGDISPLAY *display) { + LIMITED_METHOD_CONTRACT; + return NULL; +} + +inline TADDR GetRegdisplayFPAddress(REGDISPLAY *display) { + LIMITED_METHOD_CONTRACT; + return NULL; +} + +inline PCODE GetControlPC(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + return (PCODE)(display->ControlPC); +} + +#ifdef DEBUG_REGDISPLAY +void CheckRegDisplaySP (REGDISPLAY *pRD); +#endif // DEBUG_REGDISPLAY + +inline void SyncRegDisplayToCurrentContext(REGDISPLAY* pRD) +{ + LIMITED_METHOD_CONTRACT; + + pRD->SP = (INT_PTR)GetSP(pRD->pCurrentContext); + +#ifdef DEBUG_REGDISPLAY + CheckRegDisplaySP(pRD); +#endif // DEBUG_REGDISPLAY + + pRD->ControlPC = INT_PTR(GetIP(pRD->pCurrentContext)); +} + +// This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame +inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) +{ + LIMITED_METHOD_CONTRACT; + return stackPointer < ((LPVOID)(display->SP)); +} + +inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) +{ +#if defined(_TARGET_AMD64_) + // On AMD64, the MemoryStackFp value is the current sp (i.e. the sp value when calling another method). + _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext)); + return GetRegdisplaySP(display); + +#elif defined(_TARGET_ARM64_) + + _ASSERTE(display->IsCallerContextValid); + return GetSP(display->pCallerContext); + +#else // _TARGET_AMD64_ + PORTABILITY_ASSERT("GetRegdisplayStackMark NYI for this platform (Regdisp.h)"); + return NULL; +#endif // _TARGET_AMD64_ +} + +// This needs to be implemented for platforms that have funclets. +inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display) +{ + LIMITED_METHOD_CONTRACT; + +#if defined(_TARGET_AMD64_) + return (LPVOID)display->pCurrentContext->Rax; +#elif defined(_TARGET_ARM64_) + return (LPVOID)display->pCurrentContext->X0; +#else + PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)"); + return NULL; +#endif +} + +#elif defined(_TARGET_ARM_) + +// ResumableFrame is pushed on the stack before +// starting the GC. registers r0-r3 in ResumableFrame can +// contain roots which might need to be updated if they are +// relocated. On Stack walking the addresses of the registers in the +// resumable Frame are passed to GC using pCurrentContextPointers +// member in _REGDISPLAY. However On ARM KNONVOLATILE_CONTEXT_POINTERS +// does not contain pointers for volatile registers. Therefore creating +// this structure to store pointers to volatile registers and adding an object +// as member in _REGDISPLAY +typedef struct _ArmVolatileContextPointer +{ + PDWORD R0; + PDWORD R1; + PDWORD R2; + PDWORD R3; + PDWORD R12; +} ArmVolatileContextPointer; + +struct REGDISPLAY { + PT_CONTEXT pContext; // points to current Context; either + // returned by GetContext or provided + // at exception time. + + PT_CONTEXT pCurrentContext; // [trashed] points to current Context of stackwalk + PT_CONTEXT pCallerContext; // [trashed] points to the Context of the caller during stackwalk -- used for GC crawls + + T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsOne; // used by stackwalk + T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsTwo; // used by stackwalk + + PT_KNONVOLATILE_CONTEXT_POINTERS pCurrentContextPointers; + PT_KNONVOLATILE_CONTEXT_POINTERS pCallerContextPointers; + ArmVolatileContextPointer volatileCurrContextPointers; + + BOOL IsCallerContextValid; // TRUE if pCallerContext really contains the caller's context + BOOL IsCallerSPValid; // Don't add usage of this field. This is only temporary. + + DWORD SP; + DWORD ControlPC; + DWORD * pPC; // processor neutral name + + T_CONTEXT ctxOne; // used by stackwalk + T_CONTEXT ctxTwo; // used in ExceptionTracker::InitializeCrawlFrame + + REGDISPLAY() + { + // Initialize regdisplay + memset(this, 0, sizeof(REGDISPLAY)); + + // Setup the pointer to ControlPC field + pPC = &ControlPC; + } + +#ifdef DEBUG_REGDISPLAY + Thread *_pThread; +#endif // DEBUG_REGDISPLAY + +}; + +#ifdef DEBUG_REGDISPLAY +void CheckRegDisplaySP (REGDISPLAY *pRD); +#endif // DEBUG_REGDISPLAY + +inline TADDR GetRegdisplaySP(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + return (TADDR)(size_t)display->SP; +} + +inline PCODE GetControlPC(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + return (PCODE)(display->ControlPC); +} + + +// This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame +inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) { + LIMITED_METHOD_CONTRACT; + return stackPointer < ((LPVOID)(TADDR)(display->SP)); +} + +inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) { + LIMITED_METHOD_CONTRACT; + // ARM uses the establisher frame as the marker + _ASSERTE(display->IsCallerContextValid); + return GetSP(display->pCallerContext); +} + +inline void SyncRegDisplayToCurrentContext(REGDISPLAY* pRD) +{ + LIMITED_METHOD_CONTRACT; + pRD->SP = (DWORD)GetSP(pRD->pCurrentContext); + pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext); +} + +// This needs to be implemented for platforms that have funclets. +inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display) +{ + LIMITED_METHOD_CONTRACT; + + return (LPVOID)display->pCurrentContext->R0; +} + +#else // none of the above processors + +PORTABILITY_WARNING("RegDisplay functions are not implemented on this platform.") + +struct REGDISPLAY { + PCONTEXT pContext; // points to current Context + size_t SP; + size_t * FramePtr; + SLOT * pPC; +}; + +inline PCODE GetControlPC(REGDISPLAY *display) { + LIMITED_METHOD_CONTRACT; + return (PCODE) NULL; +} + +inline LPVOID GetRegdisplaySP(REGDISPLAY *display) { + LIMITED_METHOD_DAC_CONTRACT; + return (LPVOID)display->SP; +} + +inline TADDR GetRegdisplayFP(REGDISPLAY *display) { + LIMITED_METHOD_CONTRACT; + return (TADDR)*(display->FramePtr); +} + +inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) { + LIMITED_METHOD_CONTRACT; + return FALSE; +} +inline LPVOID GetRegdisplayStackMark(REGDISPLAY *display) { + LIMITED_METHOD_CONTRACT; + return (LPVOID)display->SP; +} + +#endif + +typedef REGDISPLAY *PREGDISPLAY; + + +inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pCallerCtx = NULL) +{ + WRAPPER_NO_CONTRACT; + + SUPPORTS_DAC; + +#ifdef _TARGET_X86_ + pRD->pContext = pctx; + pRD->pContextForUnwind = NULL; + pRD->pEdi = &(pctx->Edi); + pRD->pEsi = &(pctx->Esi); + pRD->pEbx = &(pctx->Ebx); + pRD->pEbp = &(pctx->Ebp); + pRD->pEax = &(pctx->Eax); + pRD->pEcx = &(pctx->Ecx); + pRD->pEdx = &(pctx->Edx); + pRD->Esp = pctx->Esp; + pRD->ControlPC = (PCODE)(pctx->Eip); + pRD->PCTAddr = (UINT_PTR)&(pctx->Eip); +#elif defined(_WIN64) + pRD->pContext = pctx; +#ifdef _TARGET_AMD64_ + for (int i = 0; i < 16; i++) + { + *(&pRD->ctxPtrsOne.Rax + i) = (&pctx->Rax + i); + } +#elif defined(_TARGET_ARM64_) + for (int i = 0; i < 12; i++) + { + *(&pRD->ctxPtrsOne.X19 + i) = (&pctx->X19 + i); + } +#endif // _TARGET_AMD64_ + + pRD->pCurrentContextPointers = &pRD->ctxPtrsOne; + pRD->pCallerContextPointers = &pRD->ctxPtrsTwo; + + pRD->pCurrentContext = &(pRD->ctxOne); + pRD->pCallerContext = &(pRD->ctxTwo); + + // copy the active context to initialize our stackwalk + *(pRD->pCurrentContext) = *(pctx); + + // copy the caller context as well if it's specified + if (pCallerCtx == NULL) + { + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. + } + else + { + *(pRD->pCallerContext) = *(pCallerCtx); + pRD->IsCallerContextValid = TRUE; + pRD->IsCallerSPValid = TRUE; // Don't add usage of this field. This is only temporary. + } + +#ifdef DEBUG_REGDISPLAY + pRD->_pThread = NULL; +#endif // DEBUG_REGDISPLAY + + SyncRegDisplayToCurrentContext(pRD); +#elif defined(_TARGET_ARM_) + pRD->pContext = pctx; + + // Copy over the nonvolatile integer registers (R4-R11) + for (int i = 0; i < 8; i++) + { + *(&pRD->ctxPtrsOne.R4 + i) = (&pctx->R4 + i); + } + + pRD->ctxPtrsOne.Lr = &pctx->Lr; + + // Setup the references + pRD->pCurrentContextPointers = &pRD->ctxPtrsOne; + pRD->pCallerContextPointers = &pRD->ctxPtrsTwo; + + pRD->pCurrentContext = &(pRD->ctxOne); + pRD->pCallerContext = &(pRD->ctxTwo); + + // copy the active context to initialize our stackwalk + *(pRD->pCurrentContext) = *(pctx); + + // copy the caller context as well if it's specified + if (pCallerCtx == NULL) + { + pRD->IsCallerContextValid = FALSE; + pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary. + } + else + { + *(pRD->pCallerContext) = *(pCallerCtx); + pRD->IsCallerContextValid = TRUE; + pRD->IsCallerSPValid = TRUE; // Don't add usage of this field. This is only temporary. + } + + pRD->pPC = &pRD->pCurrentContext->Pc; + +#ifdef DEBUG_REGDISPLAY + pRD->_pThread = NULL; +#endif // DEBUG_REGDISPLAY + + // This will setup the PC and SP + SyncRegDisplayToCurrentContext(pRD); +#else + PORTABILITY_ASSERT("@NYI Platform - InitRegDisplay (Threads.cpp)"); +#endif +} + +// Initialize a new REGDISPLAY/CONTEXT pair from an existing valid REGDISPLAY. +inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEXT *pOutCtx) +{ + WRAPPER_NO_CONTRACT; + + // The general strategy is to extract the register state from the input REGDISPLAY + // into the new CONTEXT then simply call FillRegDisplay. + + T_CONTEXT* pOutCallerCtx = NULL; + +#ifdef _TARGET_X86_ + if (pInRD->pEdi != NULL) {pOutCtx->Edi = *pInRD->pEdi;} else {pInRD->pEdi = NULL;} + if (pInRD->pEsi != NULL) {pOutCtx->Esi = *pInRD->pEsi;} else {pInRD->pEsi = NULL;} + if (pInRD->pEbx != NULL) {pOutCtx->Ebx = *pInRD->pEbx;} else {pInRD->pEbx = NULL;} + if (pInRD->pEbp != NULL) {pOutCtx->Ebp = *pInRD->pEbp;} else {pInRD->pEbp = NULL;} + if (pInRD->pEax != NULL) {pOutCtx->Eax = *pInRD->pEax;} else {pInRD->pEax = NULL;} + if (pInRD->pEcx != NULL) {pOutCtx->Ecx = *pInRD->pEcx;} else {pInRD->pEcx = NULL;} + if (pInRD->pEdx != NULL) {pOutCtx->Edx = *pInRD->pEdx;} else {pInRD->pEdx = NULL;} + pOutCtx->Esp = pInRD->Esp; + pOutCtx->Eip = pInRD->ControlPC; +#else + *pOutCtx = *(pInRD->pCurrentContext); + if (pInRD->IsCallerContextValid) + { + pOutCallerCtx = pInRD->pCallerContext; + } +#endif + + if (pOutRD) + FillRegDisplay(pOutRD, pOutCtx, pOutCallerCtx); +} + +// Get address of a register in a CONTEXT given the reg number. For X86, +// the reg number is the R/M number from ModR/M byte or base in SIB byte +inline size_t * getRegAddr (unsigned regNum, PTR_CONTEXT regs) +{ +#ifdef _TARGET_X86_ + switch (regNum) + { + case 0: + return (size_t *)®s->Eax; + break; + case 1: + return (size_t *)®s->Ecx; + break; + case 2: + return (size_t *)®s->Edx; + break; + case 3: + return (size_t *)®s->Ebx; + break; + case 4: + return (size_t *)®s->Esp; + break; + case 5: + return (size_t *)®s->Ebp; + break; + case 6: + return (size_t *)®s->Esi; + break; + case 7: + return (size_t *)®s->Edi; + break; + default: + _ASSERTE (!"unknown regNum"); + } +#elif defined(_TARGET_AMD64_) + _ASSERTE(regNum < 16); + return ®s->Rax + regNum; +#elif defined(_TARGET_ARM_) + _ASSERTE(regNum < 16); + return (size_t *)®s->R0 + regNum; +#elif defined(_TARGET_ARM64_) + _ASSERTE(regNum < 31); + return (size_t *)®s->X0 + regNum; +#else + _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)"); +#endif + return(0); +} + +//--------------------------------------------------------------------------------------- +// +// This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT. +// +// Arguments: +// pRegDisp - the REGDISPLAY to be converted +// pContext - the buffer for storing the converted CONTEXT +// +inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext) +{ + _ASSERTE((pRegDisp != NULL) && (pContext != NULL)); + +#if defined(_TARGET_X86_) + pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL); + pContext->Edi = *pRegDisp->pEdi; + pContext->Esi = *pRegDisp->pEsi; + pContext->Ebx = *pRegDisp->pEbx; + pContext->Ebp = *pRegDisp->pEbp; + pContext->Eax = *pRegDisp->pEax; + pContext->Ecx = *pRegDisp->pEcx; + pContext->Edx = *pRegDisp->pEdx; + pContext->Esp = pRegDisp->Esp; + pContext->Eip = pRegDisp->ControlPC; +#else + *pContext = *pRegDisp->pCurrentContext; +#endif +} + + +#endif // __REGDISP_H + + |