1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
9 #ifdef DEBUG_REGDISPLAY
12 void CheckRegDisplaySP (REGDISPLAY *pRD);
13 #endif // DEBUG_REGDISPLAY
15 struct REGDISPLAY_BASE {
16 PT_CONTEXT pContext; // This is the context of the active call frame;
17 // either returned by GetContext or provided at
20 // This will be used to resume execution, so
21 // do NOT trash it! But DO update any static
24 #ifdef WIN64EXCEPTIONS
25 PT_CONTEXT pCurrentContext; // [trashed] points to current Context of stackwalk
26 PT_CONTEXT pCallerContext; // [trashed] points to the Context of the caller during stackwalk -- used for GC crawls
28 // [trashed] points to current context pointers of stackwalk
29 T_KNONVOLATILE_CONTEXT_POINTERS *pCurrentContextPointers;
30 // [trashed] points to the context pointers of the caller during stackwalk -- used for GC crawls
31 T_KNONVOLATILE_CONTEXT_POINTERS *pCallerContextPointers;
33 BOOL IsCallerContextValid; // TRUE if pCallerContext really contains the caller's context
34 BOOL IsCallerSPValid; // Don't add usage of this field. This is only temporary.
36 T_CONTEXT ctxOne; // used by stackwalk
37 T_CONTEXT ctxTwo; // used by stackwalk
39 T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsOne; // used by stackwalk
40 T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsTwo; // used by stackwalk
41 #endif // WIN64EXCEPTIONS
43 #ifdef DEBUG_REGDISPLAY
45 #endif // DEBUG_REGDISPLAY
47 #if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
48 DWORD SP; // Stack Pointer
50 #else // _TARGET_X86_ || _TARGET_ARM_
53 #endif // !_TARGET_X86_ && !_TARGET_ARM
56 #if defined(_TARGET_X86_)
58 struct REGDISPLAY : public REGDISPLAY_BASE {
60 #ifndef WIN64EXCEPTIONS
61 // TODO: Unify with pCurrentContext / pCallerContext used on 64-bit
62 PCONTEXT pContextForUnwind; // scratch context for unwinding
63 // used to preserve context saved in the frame that
64 // could be otherwise wiped by the unwinding
74 #endif // !WIN64EXCEPTIONS
76 #ifndef WIN64EXCEPTIONS
78 #define REG_METHODS(reg) \
79 inline PDWORD Get##reg##Location(void) { return p##reg; } \
80 inline void Set##reg##Location(PDWORD p##reg) { this->p##reg = p##reg; }
82 #else // !WIN64EXCEPTIONS
84 #define REG_METHODS(reg) \
85 inline PDWORD Get##reg##Location(void) { return pCurrentContextPointers->reg; } \
86 inline void Set##reg##Location(PDWORD p##reg) { pCurrentContextPointers->reg = p##reg; }
88 #endif // WIN64EXCEPTIONS
104 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
105 LIMITED_METHOD_DAC_CONTRACT;
107 return (TADDR)display->SP;
110 inline void SetRegdisplaySP(REGDISPLAY *display, LPVOID sp ) {
111 LIMITED_METHOD_DAC_CONTRACT;
113 (display->SP) = (DWORD)(size_t)sp;
116 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
117 LIMITED_METHOD_DAC_CONTRACT;
119 return (TADDR)*display->GetEbpLocation();
122 inline LPVOID GetRegdisplayFPAddress(REGDISPLAY *display) {
123 LIMITED_METHOD_CONTRACT;
125 return (LPVOID)display->GetEbpLocation();
128 inline PCODE GetControlPC(REGDISPLAY *display) {
129 LIMITED_METHOD_DAC_CONTRACT;
131 return display->ControlPC;
134 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
135 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
136 LIMITED_METHOD_CONTRACT;
138 #ifdef WIN64EXCEPTIONS
139 return stackPointer < ((LPVOID)(display->SP));
141 return (TADDR)stackPointer < display->PCTAddr;
144 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
145 LIMITED_METHOD_DAC_CONTRACT;
147 #ifdef WIN64EXCEPTIONS
148 _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext));
149 return GetRegdisplaySP(display);
151 return display->PCTAddr;
155 #elif defined(_WIN64)
157 #if defined(_TARGET_ARM64_)
158 typedef struct _Arm64VolatileContextPointer
180 //X18 is reserved by OS, in userspace it represents TEB
184 } Arm64VolatileContextPointer;
185 #endif //_TARGET_ARM64_
186 struct REGDISPLAY : public REGDISPLAY_BASE {
187 #ifdef _TARGET_ARM64_
188 Arm64VolatileContextPointer volatileCurrContextPointers;
194 memset(this, 0, sizeof(REGDISPLAY));
198 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
199 LIMITED_METHOD_DAC_CONTRACT;
200 return (TADDR)display->SP;
203 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
204 LIMITED_METHOD_CONTRACT;
208 inline TADDR GetRegdisplayFPAddress(REGDISPLAY *display) {
209 LIMITED_METHOD_CONTRACT;
213 inline PCODE GetControlPC(REGDISPLAY *display) {
214 LIMITED_METHOD_DAC_CONTRACT;
215 return (PCODE)(display->ControlPC);
220 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
221 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer)
223 LIMITED_METHOD_CONTRACT;
224 return stackPointer < ((LPVOID)(display->SP));
227 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display)
229 #if defined(_TARGET_AMD64_)
230 // On AMD64, the MemoryStackFp value is the current sp (i.e. the sp value when calling another method).
231 _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext));
232 return GetRegdisplaySP(display);
234 #elif defined(_TARGET_ARM64_)
236 _ASSERTE(display->IsCallerContextValid);
237 return GetSP(display->pCallerContext);
239 #else // _TARGET_AMD64_
240 PORTABILITY_ASSERT("GetRegdisplayStackMark NYI for this platform (Regdisp.h)");
242 #endif // _TARGET_AMD64_
246 #elif defined(_TARGET_ARM_)
248 // ResumableFrame is pushed on the stack before
249 // starting the GC. registers r0-r3 in ResumableFrame can
250 // contain roots which might need to be updated if they are
251 // relocated. On Stack walking the addresses of the registers in the
252 // resumable Frame are passed to GC using pCurrentContextPointers
253 // member in _REGDISPLAY. However On ARM KNONVOLATILE_CONTEXT_POINTERS
254 // does not contain pointers for volatile registers. Therefore creating
255 // this structure to store pointers to volatile registers and adding an object
256 // as member in _REGDISPLAY
257 typedef struct _ArmVolatileContextPointer
264 } ArmVolatileContextPointer;
266 struct REGDISPLAY : public REGDISPLAY_BASE {
267 ArmVolatileContextPointer volatileCurrContextPointers;
269 DWORD * pPC; // processor neutral name
273 // Initialize regdisplay
274 memset(this, 0, sizeof(REGDISPLAY));
276 // Setup the pointer to ControlPC field
281 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
282 LIMITED_METHOD_DAC_CONTRACT;
283 return (TADDR)(size_t)display->SP;
286 inline PCODE GetControlPC(REGDISPLAY *display) {
287 LIMITED_METHOD_DAC_CONTRACT;
288 return (PCODE)(display->ControlPC);
292 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
293 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
294 LIMITED_METHOD_CONTRACT;
295 return stackPointer < ((LPVOID)(TADDR)(display->SP));
298 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
299 LIMITED_METHOD_CONTRACT;
300 // ARM uses the establisher frame as the marker
301 _ASSERTE(display->IsCallerContextValid);
302 return GetSP(display->pCallerContext);
305 #else // none of the above processors
307 PORTABILITY_WARNING("RegDisplay functions are not implemented on this platform.")
309 struct REGDISPLAY : public REGDISPLAY_BASE {
314 inline PCODE GetControlPC(REGDISPLAY *display) {
315 LIMITED_METHOD_CONTRACT;
319 inline LPVOID GetRegdisplaySP(REGDISPLAY *display) {
320 LIMITED_METHOD_DAC_CONTRACT;
321 return (LPVOID)display->SP;
324 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
325 LIMITED_METHOD_CONTRACT;
326 return (TADDR)*(display->FramePtr);
329 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
330 LIMITED_METHOD_CONTRACT;
333 inline LPVOID GetRegdisplayStackMark(REGDISPLAY *display) {
334 LIMITED_METHOD_CONTRACT;
335 return (LPVOID)display->SP;
340 #if defined(_WIN64) || defined(_TARGET_ARM_) || (defined(_TARGET_X86_) && defined(WIN64EXCEPTIONS))
341 // This needs to be implemented for platforms that have funclets.
342 inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display)
344 LIMITED_METHOD_CONTRACT;
346 #if defined(_TARGET_AMD64_)
347 return (LPVOID)display->pCurrentContext->Rax;
348 #elif defined(_TARGET_ARM64_)
349 return (LPVOID)display->pCurrentContext->X0;
350 #elif defined(_TARGET_ARM_)
351 return (LPVOID)display->pCurrentContext->R0;
352 #elif defined(_TARGET_X86_)
353 return (LPVOID)display->pCurrentContext->Eax;
355 PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)");
360 inline void SyncRegDisplayToCurrentContext(REGDISPLAY* pRD)
362 LIMITED_METHOD_CONTRACT;
365 pRD->SP = (INT_PTR)GetSP(pRD->pCurrentContext);
366 pRD->ControlPC = INT_PTR(GetIP(pRD->pCurrentContext));
367 #elif defined(_TARGET_ARM_) // _WIN64
368 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
369 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
370 #elif defined(_TARGET_X86_) // _TARGET_ARM_
371 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
372 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
373 #else // _TARGET_X86_
374 PORTABILITY_ASSERT("SyncRegDisplayToCurrentContext");
375 #endif // _TARGET_ARM_ || _TARGET_X86_
377 #ifdef DEBUG_REGDISPLAY
378 CheckRegDisplaySP(pRD);
379 #endif // DEBUG_REGDISPLAY
381 #endif // _WIN64 || _TARGET_ARM_ || (_TARGET_X86_ && WIN64EXCEPTIONS)
383 typedef REGDISPLAY *PREGDISPLAY;
386 inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pCallerCtx = NULL)
392 #ifndef WIN64EXCEPTIONS
394 pRD->pContext = pctx;
395 pRD->pContextForUnwind = NULL;
396 pRD->pEdi = &(pctx->Edi);
397 pRD->pEsi = &(pctx->Esi);
398 pRD->pEbx = &(pctx->Ebx);
399 pRD->pEbp = &(pctx->Ebp);
400 pRD->pEax = &(pctx->Eax);
401 pRD->pEcx = &(pctx->Ecx);
402 pRD->pEdx = &(pctx->Edx);
404 pRD->ControlPC = (PCODE)(pctx->Eip);
405 pRD->PCTAddr = (UINT_PTR)&(pctx->Eip);
406 #else // _TARGET_X86_
407 PORTABILITY_ASSERT("FillRegDisplay");
408 #endif // _TARGET_???_ (ELSE)
410 #else // !WIN64EXCEPTIONS
411 pRD->pContext = pctx;
413 // Setup the references
414 pRD->pCurrentContextPointers = &pRD->ctxPtrsOne;
415 pRD->pCallerContextPointers = &pRD->ctxPtrsTwo;
417 pRD->pCurrentContext = &(pRD->ctxOne);
418 pRD->pCallerContext = &(pRD->ctxTwo);
420 // copy the active context to initialize our stackwalk
421 *(pRD->pCurrentContext) = *(pctx);
423 // copy the caller context as well if it's specified
424 if (pCallerCtx == NULL)
426 pRD->IsCallerContextValid = FALSE;
427 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
431 *(pRD->pCallerContext) = *(pCallerCtx);
432 pRD->IsCallerContextValid = TRUE;
433 pRD->IsCallerSPValid = TRUE; // Don't add usage of this field. This is only temporary.
436 #ifdef _TARGET_AMD64_
437 for (int i = 0; i < 16; i++)
439 *(&pRD->ctxPtrsOne.Rax + i) = (&pctx->Rax + i);
441 #elif defined(_TARGET_ARM64_) // _TARGET_AMD64_
442 for (int i = 0; i < 12; i++)
444 *(&pRD->ctxPtrsOne.X19 + i) = (&pctx->X19 + i);
446 #elif defined(_TARGET_ARM_) // _TARGET_ARM64_
447 // Copy over the nonvolatile integer registers (R4-R11)
448 for (int i = 0; i < 8; i++)
450 *(&pRD->ctxPtrsOne.R4 + i) = (&pctx->R4 + i);
453 pRD->ctxPtrsOne.Lr = &pctx->Lr;
454 pRD->pPC = &pRD->pCurrentContext->Pc;
455 #elif defined(_TARGET_X86_) // _TARGET_ARM_
456 for (int i = 0; i < 7; i++)
458 *(&pRD->ctxPtrsOne.Esi + i) = (&pctx->Esi + i);
460 #else // _TARGET_X86_
461 PORTABILITY_ASSERT("FillRegDisplay");
462 #endif // _TARGET_???_ (ELSE)
464 #ifdef DEBUG_REGDISPLAY
465 pRD->_pThread = NULL;
466 #endif // DEBUG_REGDISPLAY
468 // This will setup the PC and SP
469 SyncRegDisplayToCurrentContext(pRD);
470 #endif // !WIN64EXCEPTIONS
473 // Initialize a new REGDISPLAY/CONTEXT pair from an existing valid REGDISPLAY.
474 inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEXT *pOutCtx)
478 // The general strategy is to extract the register state from the input REGDISPLAY
479 // into the new CONTEXT then simply call FillRegDisplay.
481 T_CONTEXT* pOutCallerCtx = NULL;
483 #ifndef WIN64EXCEPTIONS
485 #if defined(_TARGET_X86_)
486 if (pInRD->pEdi != NULL) {pOutCtx->Edi = *pInRD->pEdi;} else {pInRD->pEdi = NULL;}
487 if (pInRD->pEsi != NULL) {pOutCtx->Esi = *pInRD->pEsi;} else {pInRD->pEsi = NULL;}
488 if (pInRD->pEbx != NULL) {pOutCtx->Ebx = *pInRD->pEbx;} else {pInRD->pEbx = NULL;}
489 if (pInRD->pEbp != NULL) {pOutCtx->Ebp = *pInRD->pEbp;} else {pInRD->pEbp = NULL;}
490 if (pInRD->pEax != NULL) {pOutCtx->Eax = *pInRD->pEax;} else {pInRD->pEax = NULL;}
491 if (pInRD->pEcx != NULL) {pOutCtx->Ecx = *pInRD->pEcx;} else {pInRD->pEcx = NULL;}
492 if (pInRD->pEdx != NULL) {pOutCtx->Edx = *pInRD->pEdx;} else {pInRD->pEdx = NULL;}
493 pOutCtx->Esp = pInRD->SP;
494 pOutCtx->Eip = pInRD->ControlPC;
495 #else // _TARGET_X86_
496 PORTABILITY_ASSERT("CopyRegDisplay");
497 #endif // _TARGET_???_
499 #else // WIN64EXCEPTIONS
501 *pOutCtx = *(pInRD->pCurrentContext);
502 if (pInRD->IsCallerContextValid)
504 pOutCallerCtx = pInRD->pCallerContext;
507 #endif // WIN64EXCEPTIONS
510 FillRegDisplay(pOutRD, pOutCtx, pOutCallerCtx);
513 // Get address of a register in a CONTEXT given the reg number. For X86,
514 // the reg number is the R/M number from ModR/M byte or base in SIB byte
515 inline size_t * getRegAddr (unsigned regNum, PTR_CONTEXT regs)
521 return (size_t *)®s->Eax;
524 return (size_t *)®s->Ecx;
527 return (size_t *)®s->Edx;
530 return (size_t *)®s->Ebx;
533 return (size_t *)®s->Esp;
536 return (size_t *)®s->Ebp;
539 return (size_t *)®s->Esi;
542 return (size_t *)®s->Edi;
545 _ASSERTE (!"unknown regNum");
547 #elif defined(_TARGET_AMD64_)
548 _ASSERTE(regNum < 16);
549 return ®s->Rax + regNum;
550 #elif defined(_TARGET_ARM_)
551 _ASSERTE(regNum < 16);
552 return (size_t *)®s->R0 + regNum;
553 #elif defined(_TARGET_ARM64_)
554 _ASSERTE(regNum < 31);
555 return (size_t *)®s->X0 + regNum;
557 _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)");
562 //---------------------------------------------------------------------------------------
564 // This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT.
567 // pRegDisp - the REGDISPLAY to be converted
568 // pContext - the buffer for storing the converted CONTEXT
570 inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext)
572 _ASSERTE((pRegDisp != NULL) && (pContext != NULL));
574 #ifndef WIN64EXCEPTIONS
576 #if defined(_TARGET_X86_)
577 pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
578 pContext->Edi = *pRegDisp->pEdi;
579 pContext->Esi = *pRegDisp->pEsi;
580 pContext->Ebx = *pRegDisp->pEbx;
581 pContext->Ebp = *pRegDisp->pEbp;
582 pContext->Eax = *pRegDisp->pEax;
583 pContext->Ecx = *pRegDisp->pEcx;
584 pContext->Edx = *pRegDisp->pEdx;
585 pContext->Esp = pRegDisp->SP;
586 pContext->Eip = pRegDisp->ControlPC;
587 #else // _TARGET_X86_
588 PORTABILITY_ASSERT("UpdateContextFromRegDisp");
589 #endif // _TARGET_???_
591 #else // WIN64EXCEPTIONS
593 *pContext = *pRegDisp->pCurrentContext;
595 #endif // WIN64EXCEPTIONS
599 #endif // __REGDISP_H