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 return (TADDR)stackPointer < display->PCTAddr;
140 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
141 LIMITED_METHOD_DAC_CONTRACT;
143 return display->PCTAddr;
146 #elif defined(_WIN64)
148 #if defined(_TARGET_ARM64_)
149 typedef struct _Arm64VolatileContextPointer
171 //X18 is reserved by OS, in userspace it represents TEB
175 } Arm64VolatileContextPointer;
176 #endif //_TARGET_ARM64_
177 struct REGDISPLAY : public REGDISPLAY_BASE {
178 #ifdef _TARGET_ARM64_
179 Arm64VolatileContextPointer volatileCurrContextPointers;
185 memset(this, 0, sizeof(REGDISPLAY));
189 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
190 LIMITED_METHOD_DAC_CONTRACT;
191 return (TADDR)display->SP;
194 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
195 LIMITED_METHOD_CONTRACT;
199 inline TADDR GetRegdisplayFPAddress(REGDISPLAY *display) {
200 LIMITED_METHOD_CONTRACT;
204 inline PCODE GetControlPC(REGDISPLAY *display) {
205 LIMITED_METHOD_DAC_CONTRACT;
206 return (PCODE)(display->ControlPC);
211 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
212 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer)
214 LIMITED_METHOD_CONTRACT;
215 return stackPointer < ((LPVOID)(display->SP));
218 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display)
220 #if defined(_TARGET_AMD64_)
221 // On AMD64, the MemoryStackFp value is the current sp (i.e. the sp value when calling another method).
222 _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext));
223 return GetRegdisplaySP(display);
225 #elif defined(_TARGET_ARM64_)
227 _ASSERTE(display->IsCallerContextValid);
228 return GetSP(display->pCallerContext);
230 #else // _TARGET_AMD64_
231 PORTABILITY_ASSERT("GetRegdisplayStackMark NYI for this platform (Regdisp.h)");
233 #endif // _TARGET_AMD64_
237 #elif defined(_TARGET_ARM_)
239 // ResumableFrame is pushed on the stack before
240 // starting the GC. registers r0-r3 in ResumableFrame can
241 // contain roots which might need to be updated if they are
242 // relocated. On Stack walking the addresses of the registers in the
243 // resumable Frame are passed to GC using pCurrentContextPointers
244 // member in _REGDISPLAY. However On ARM KNONVOLATILE_CONTEXT_POINTERS
245 // does not contain pointers for volatile registers. Therefore creating
246 // this structure to store pointers to volatile registers and adding an object
247 // as member in _REGDISPLAY
248 typedef struct _ArmVolatileContextPointer
255 } ArmVolatileContextPointer;
257 struct REGDISPLAY : public REGDISPLAY_BASE {
258 ArmVolatileContextPointer volatileCurrContextPointers;
260 DWORD * pPC; // processor neutral name
264 // Initialize regdisplay
265 memset(this, 0, sizeof(REGDISPLAY));
267 // Setup the pointer to ControlPC field
272 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
273 LIMITED_METHOD_DAC_CONTRACT;
274 return (TADDR)(size_t)display->SP;
277 inline PCODE GetControlPC(REGDISPLAY *display) {
278 LIMITED_METHOD_DAC_CONTRACT;
279 return (PCODE)(display->ControlPC);
283 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
284 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
285 LIMITED_METHOD_CONTRACT;
286 return stackPointer < ((LPVOID)(TADDR)(display->SP));
289 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
290 LIMITED_METHOD_CONTRACT;
291 // ARM uses the establisher frame as the marker
292 _ASSERTE(display->IsCallerContextValid);
293 return GetSP(display->pCallerContext);
296 #else // none of the above processors
298 PORTABILITY_WARNING("RegDisplay functions are not implemented on this platform.")
300 struct REGDISPLAY : public REGDISPLAY_BASE {
305 inline PCODE GetControlPC(REGDISPLAY *display) {
306 LIMITED_METHOD_CONTRACT;
310 inline LPVOID GetRegdisplaySP(REGDISPLAY *display) {
311 LIMITED_METHOD_DAC_CONTRACT;
312 return (LPVOID)display->SP;
315 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
316 LIMITED_METHOD_CONTRACT;
317 return (TADDR)*(display->FramePtr);
320 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
321 LIMITED_METHOD_CONTRACT;
324 inline LPVOID GetRegdisplayStackMark(REGDISPLAY *display) {
325 LIMITED_METHOD_CONTRACT;
326 return (LPVOID)display->SP;
331 #if defined(_WIN64) || defined(_TARGET_ARM_) || (defined(_TARGET_X86_) && defined(WIN64EXCEPTIONS))
332 // This needs to be implemented for platforms that have funclets.
333 inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display)
335 LIMITED_METHOD_CONTRACT;
337 #if defined(_TARGET_AMD64_)
338 return (LPVOID)display->pCurrentContext->Rax;
339 #elif defined(_TARGET_ARM64_)
340 return (LPVOID)display->pCurrentContext->X0;
341 #elif defined(_TARGET_ARM_)
342 return (LPVOID)display->pCurrentContext->R0;
343 #elif defined(_TARGET_X86_)
344 return (LPVOID)display->pCurrentContext->Eax;
346 PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)");
351 inline void SyncRegDisplayToCurrentContext(REGDISPLAY* pRD)
353 LIMITED_METHOD_CONTRACT;
356 pRD->SP = (INT_PTR)GetSP(pRD->pCurrentContext);
357 pRD->ControlPC = INT_PTR(GetIP(pRD->pCurrentContext));
358 #elif defined(_TARGET_ARM_) // _WIN64
359 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
360 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
361 #elif defined(_TARGET_X86_) // _TARGET_ARM_
362 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
363 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
364 #else // _TARGET_X86_
365 PORTABILITY_ASSERT("SyncRegDisplayToCurrentContext");
366 #endif // _TARGET_ARM_ || _TARGET_X86_
368 #ifdef DEBUG_REGDISPLAY
369 CheckRegDisplaySP(pRD);
370 #endif // DEBUG_REGDISPLAY
372 #endif // _WIN64 || _TARGET_ARM_ || (_TARGET_X86_ && WIN64EXCEPTIONS)
374 typedef REGDISPLAY *PREGDISPLAY;
377 inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pCallerCtx = NULL)
383 #ifndef WIN64EXCEPTIONS
385 pRD->pContext = pctx;
386 pRD->pContextForUnwind = NULL;
387 pRD->pEdi = &(pctx->Edi);
388 pRD->pEsi = &(pctx->Esi);
389 pRD->pEbx = &(pctx->Ebx);
390 pRD->pEbp = &(pctx->Ebp);
391 pRD->pEax = &(pctx->Eax);
392 pRD->pEcx = &(pctx->Ecx);
393 pRD->pEdx = &(pctx->Edx);
395 pRD->ControlPC = (PCODE)(pctx->Eip);
396 pRD->PCTAddr = (UINT_PTR)&(pctx->Eip);
397 #else // _TARGET_X86_
398 PORTABILITY_ASSERT("FillRegDisplay");
399 #endif // _TARGET_???_ (ELSE)
401 #else // !WIN64EXCEPTIONS
402 pRD->pContext = pctx;
404 // Setup the references
405 pRD->pCurrentContextPointers = &pRD->ctxPtrsOne;
406 pRD->pCallerContextPointers = &pRD->ctxPtrsTwo;
408 pRD->pCurrentContext = &(pRD->ctxOne);
409 pRD->pCallerContext = &(pRD->ctxTwo);
411 // copy the active context to initialize our stackwalk
412 *(pRD->pCurrentContext) = *(pctx);
414 // copy the caller context as well if it's specified
415 if (pCallerCtx == NULL)
417 pRD->IsCallerContextValid = FALSE;
418 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
422 *(pRD->pCallerContext) = *(pCallerCtx);
423 pRD->IsCallerContextValid = TRUE;
424 pRD->IsCallerSPValid = TRUE; // Don't add usage of this field. This is only temporary.
427 #ifdef _TARGET_AMD64_
428 for (int i = 0; i < 16; i++)
430 *(&pRD->ctxPtrsOne.Rax + i) = (&pctx->Rax + i);
432 #elif defined(_TARGET_ARM64_) // _TARGET_AMD64_
433 for (int i = 0; i < 12; i++)
435 *(&pRD->ctxPtrsOne.X19 + i) = (&pctx->X19 + i);
437 #elif defined(_TARGET_ARM_) // _TARGET_ARM64_
438 // Copy over the nonvolatile integer registers (R4-R11)
439 for (int i = 0; i < 8; i++)
441 *(&pRD->ctxPtrsOne.R4 + i) = (&pctx->R4 + i);
444 pRD->ctxPtrsOne.Lr = &pctx->Lr;
445 pRD->pPC = &pRD->pCurrentContext->Pc;
446 #elif defined(_TARGET_X86_) // _TARGET_ARM_
447 for (int i = 0; i < 7; i++)
449 *(&pRD->ctxPtrsOne.Esi + i) = (&pctx->Esi + i);
451 #else // _TARGET_X86_
452 PORTABILITY_ASSERT("FillRegDisplay");
453 #endif // _TARGET_???_ (ELSE)
455 #ifdef DEBUG_REGDISPLAY
456 pRD->_pThread = NULL;
457 #endif // DEBUG_REGDISPLAY
459 // This will setup the PC and SP
460 SyncRegDisplayToCurrentContext(pRD);
461 #endif // !WIN64EXCEPTIONS
464 // Initialize a new REGDISPLAY/CONTEXT pair from an existing valid REGDISPLAY.
465 inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEXT *pOutCtx)
469 // The general strategy is to extract the register state from the input REGDISPLAY
470 // into the new CONTEXT then simply call FillRegDisplay.
472 T_CONTEXT* pOutCallerCtx = NULL;
474 #ifndef WIN64EXCEPTIONS
476 #if defined(_TARGET_X86_)
477 if (pInRD->pEdi != NULL) {pOutCtx->Edi = *pInRD->pEdi;} else {pInRD->pEdi = NULL;}
478 if (pInRD->pEsi != NULL) {pOutCtx->Esi = *pInRD->pEsi;} else {pInRD->pEsi = NULL;}
479 if (pInRD->pEbx != NULL) {pOutCtx->Ebx = *pInRD->pEbx;} else {pInRD->pEbx = NULL;}
480 if (pInRD->pEbp != NULL) {pOutCtx->Ebp = *pInRD->pEbp;} else {pInRD->pEbp = NULL;}
481 if (pInRD->pEax != NULL) {pOutCtx->Eax = *pInRD->pEax;} else {pInRD->pEax = NULL;}
482 if (pInRD->pEcx != NULL) {pOutCtx->Ecx = *pInRD->pEcx;} else {pInRD->pEcx = NULL;}
483 if (pInRD->pEdx != NULL) {pOutCtx->Edx = *pInRD->pEdx;} else {pInRD->pEdx = NULL;}
484 pOutCtx->Esp = pInRD->SP;
485 pOutCtx->Eip = pInRD->ControlPC;
486 #else // _TARGET_X86_
487 PORTABILITY_ASSERT("CopyRegDisplay");
488 #endif // _TARGET_???_
490 #else // WIN64EXCEPTIONS
492 *pOutCtx = *(pInRD->pCurrentContext);
493 if (pInRD->IsCallerContextValid)
495 pOutCallerCtx = pInRD->pCallerContext;
498 #endif // WIN64EXCEPTIONS
501 FillRegDisplay(pOutRD, pOutCtx, pOutCallerCtx);
504 // Get address of a register in a CONTEXT given the reg number. For X86,
505 // the reg number is the R/M number from ModR/M byte or base in SIB byte
506 inline size_t * getRegAddr (unsigned regNum, PTR_CONTEXT regs)
512 return (size_t *)®s->Eax;
515 return (size_t *)®s->Ecx;
518 return (size_t *)®s->Edx;
521 return (size_t *)®s->Ebx;
524 return (size_t *)®s->Esp;
527 return (size_t *)®s->Ebp;
530 return (size_t *)®s->Esi;
533 return (size_t *)®s->Edi;
536 _ASSERTE (!"unknown regNum");
538 #elif defined(_TARGET_AMD64_)
539 _ASSERTE(regNum < 16);
540 return ®s->Rax + regNum;
541 #elif defined(_TARGET_ARM_)
542 _ASSERTE(regNum < 16);
543 return (size_t *)®s->R0 + regNum;
544 #elif defined(_TARGET_ARM64_)
545 _ASSERTE(regNum < 31);
546 return (size_t *)®s->X0 + regNum;
548 _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)");
553 //---------------------------------------------------------------------------------------
555 // This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT.
558 // pRegDisp - the REGDISPLAY to be converted
559 // pContext - the buffer for storing the converted CONTEXT
561 inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext)
563 _ASSERTE((pRegDisp != NULL) && (pContext != NULL));
565 #ifndef WIN64EXCEPTIONS
567 #if defined(_TARGET_X86_)
568 pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
569 pContext->Edi = *pRegDisp->pEdi;
570 pContext->Esi = *pRegDisp->pEsi;
571 pContext->Ebx = *pRegDisp->pEbx;
572 pContext->Ebp = *pRegDisp->pEbp;
573 pContext->Eax = *pRegDisp->pEax;
574 pContext->Ecx = *pRegDisp->pEcx;
575 pContext->Edx = *pRegDisp->pEdx;
576 pContext->Esp = pRegDisp->SP;
577 pContext->Eip = pRegDisp->ControlPC;
578 #else // _TARGET_X86_
579 PORTABILITY_ASSERT("UpdateContextFromRegDisp");
580 #endif // _TARGET_???_
582 #else // WIN64EXCEPTIONS
584 *pContext = *pRegDisp->pCurrentContext;
586 #endif // WIN64EXCEPTIONS
590 #endif // __REGDISP_H