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
51 inline PCODE GetControlPC(REGDISPLAY_BASE *pRD) {
52 LIMITED_METHOD_DAC_CONTRACT;
53 return (PCODE)(pRD->ControlPC);
56 inline TADDR GetRegdisplaySP(REGDISPLAY_BASE *pRD) {
57 LIMITED_METHOD_DAC_CONTRACT;
62 inline void SetRegdisplaySP(REGDISPLAY_BASE *pRD, LPVOID sp) {
63 LIMITED_METHOD_DAC_CONTRACT;
68 #if defined(_TARGET_X86_)
70 struct REGDISPLAY : public REGDISPLAY_BASE {
72 #ifndef WIN64EXCEPTIONS
73 // TODO: Unify with pCurrentContext / pCallerContext used on 64-bit
74 PCONTEXT pContextForUnwind; // scratch context for unwinding
75 // used to preserve context saved in the frame that
76 // could be otherwise wiped by the unwinding
86 #endif // !WIN64EXCEPTIONS
88 #ifndef WIN64EXCEPTIONS
90 #define REG_METHODS(reg) \
91 inline PDWORD Get##reg##Location(void) { return p##reg; } \
92 inline void Set##reg##Location(PDWORD p##reg) { this->p##reg = p##reg; }
94 #else // !WIN64EXCEPTIONS
96 #define REG_METHODS(reg) \
97 inline PDWORD Get##reg##Location(void) { return pCurrentContextPointers->reg; } \
98 inline void Set##reg##Location(PDWORD p##reg) { pCurrentContextPointers->reg = p##reg; }
100 #endif // WIN64EXCEPTIONS
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();
129 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
130 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
131 LIMITED_METHOD_CONTRACT;
133 #ifdef WIN64EXCEPTIONS
134 return stackPointer < ((LPVOID)(display->SP));
136 return (TADDR)stackPointer < display->PCTAddr;
139 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
140 LIMITED_METHOD_DAC_CONTRACT;
142 #ifdef WIN64EXCEPTIONS
143 _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext));
144 return GetRegdisplaySP(display);
146 return display->PCTAddr;
150 #elif defined(_WIN64)
152 #if defined(_TARGET_ARM64_)
153 typedef struct _Arm64VolatileContextPointer
175 //X18 is reserved by OS, in userspace it represents TEB
179 } Arm64VolatileContextPointer;
180 #endif //_TARGET_ARM64_
181 struct REGDISPLAY : public REGDISPLAY_BASE {
182 #ifdef _TARGET_ARM64_
183 Arm64VolatileContextPointer volatileCurrContextPointers;
189 memset(this, 0, sizeof(REGDISPLAY));
194 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
195 LIMITED_METHOD_CONTRACT;
199 inline TADDR GetRegdisplayFPAddress(REGDISPLAY *display) {
200 LIMITED_METHOD_CONTRACT;
204 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
205 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer)
207 LIMITED_METHOD_CONTRACT;
208 return stackPointer < ((LPVOID)(display->SP));
211 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display)
213 #if defined(_TARGET_AMD64_)
214 // On AMD64, the MemoryStackFp value is the current sp (i.e. the sp value when calling another method).
215 _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext));
216 return GetRegdisplaySP(display);
218 #elif defined(_TARGET_ARM64_)
220 _ASSERTE(display->IsCallerContextValid);
221 return GetSP(display->pCallerContext);
223 #else // _TARGET_AMD64_
224 PORTABILITY_ASSERT("GetRegdisplayStackMark NYI for this platform (Regdisp.h)");
226 #endif // _TARGET_AMD64_
229 #elif defined(_TARGET_ARM_)
231 // ResumableFrame is pushed on the stack before
232 // starting the GC. registers r0-r3 in ResumableFrame can
233 // contain roots which might need to be updated if they are
234 // relocated. On Stack walking the addresses of the registers in the
235 // resumable Frame are passed to GC using pCurrentContextPointers
236 // member in _REGDISPLAY. However On ARM KNONVOLATILE_CONTEXT_POINTERS
237 // does not contain pointers for volatile registers. Therefore creating
238 // this structure to store pointers to volatile registers and adding an object
239 // as member in _REGDISPLAY
240 typedef struct _ArmVolatileContextPointer
247 } ArmVolatileContextPointer;
249 struct REGDISPLAY : public REGDISPLAY_BASE {
250 ArmVolatileContextPointer volatileCurrContextPointers;
252 DWORD * pPC; // processor neutral name
256 // Initialize regdisplay
257 memset(this, 0, sizeof(REGDISPLAY));
259 // Setup the pointer to ControlPC field
264 // This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
265 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
266 LIMITED_METHOD_CONTRACT;
267 return stackPointer < ((LPVOID)(TADDR)(display->SP));
270 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
271 LIMITED_METHOD_CONTRACT;
272 // ARM uses the establisher frame as the marker
273 _ASSERTE(display->IsCallerContextValid);
274 return GetSP(display->pCallerContext);
277 #else // none of the above processors
278 #error "RegDisplay functions are not implemented on this platform."
281 #if defined(_WIN64) || defined(_TARGET_ARM_) || (defined(_TARGET_X86_) && defined(WIN64EXCEPTIONS))
282 // This needs to be implemented for platforms that have funclets.
283 inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display)
285 LIMITED_METHOD_CONTRACT;
287 #if defined(_TARGET_AMD64_)
288 return (LPVOID)display->pCurrentContext->Rax;
289 #elif defined(_TARGET_ARM64_)
290 return (LPVOID)display->pCurrentContext->X0;
291 #elif defined(_TARGET_ARM_)
292 return (LPVOID)display->pCurrentContext->R0;
293 #elif defined(_TARGET_X86_)
294 return (LPVOID)display->pCurrentContext->Eax;
296 PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)");
301 inline void SyncRegDisplayToCurrentContext(REGDISPLAY* pRD)
303 LIMITED_METHOD_CONTRACT;
306 pRD->SP = (INT_PTR)GetSP(pRD->pCurrentContext);
307 pRD->ControlPC = INT_PTR(GetIP(pRD->pCurrentContext));
308 #elif defined(_TARGET_ARM_) // _WIN64
309 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
310 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
311 #elif defined(_TARGET_X86_) // _TARGET_ARM_
312 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
313 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
314 #else // _TARGET_X86_
315 PORTABILITY_ASSERT("SyncRegDisplayToCurrentContext");
316 #endif // _TARGET_ARM_ || _TARGET_X86_
318 #ifdef DEBUG_REGDISPLAY
319 CheckRegDisplaySP(pRD);
320 #endif // DEBUG_REGDISPLAY
322 #endif // _WIN64 || _TARGET_ARM_ || (_TARGET_X86_ && WIN64EXCEPTIONS)
324 typedef REGDISPLAY *PREGDISPLAY;
326 #ifdef WIN64EXCEPTIONS
327 inline void FillContextPointers(PT_KNONVOLATILE_CONTEXT_POINTERS pCtxPtrs, PT_CONTEXT pCtx)
329 #ifdef _TARGET_AMD64_
330 for (int i = 0; i < 16; i++)
332 *(&pCtxPtrs->Rax + i) = (&pCtx->Rax + i);
334 #elif defined(_TARGET_ARM64_) // _TARGET_AMD64_
335 for (int i = 0; i < 12; i++)
337 *(&pCtxPtrs->X19 + i) = (&pCtx->X19 + i);
339 #elif defined(_TARGET_ARM_) // _TARGET_ARM64_
340 // Copy over the nonvolatile integer registers (R4-R11)
341 for (int i = 0; i < 8; i++)
343 *(&pCtxPtrs->R4 + i) = (&pCtx->R4 + i);
345 #elif defined(_TARGET_X86_) // _TARGET_ARM_
346 for (int i = 0; i < 7; i++)
348 *(&pCtxPtrs->Edi + i) = (&pCtx->Edi + i);
350 #else // _TARGET_X86_
351 PORTABILITY_ASSERT("FillContextPointers");
352 #endif // _TARGET_???_ (ELSE)
354 #endif // WIN64EXCEPTIONS
356 inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pCallerCtx = NULL)
362 #ifndef WIN64EXCEPTIONS
364 pRD->pContext = pctx;
365 pRD->pContextForUnwind = NULL;
366 pRD->pEdi = &(pctx->Edi);
367 pRD->pEsi = &(pctx->Esi);
368 pRD->pEbx = &(pctx->Ebx);
369 pRD->pEbp = &(pctx->Ebp);
370 pRD->pEax = &(pctx->Eax);
371 pRD->pEcx = &(pctx->Ecx);
372 pRD->pEdx = &(pctx->Edx);
374 pRD->ControlPC = (PCODE)(pctx->Eip);
375 pRD->PCTAddr = (UINT_PTR)&(pctx->Eip);
376 #else // _TARGET_X86_
377 PORTABILITY_ASSERT("FillRegDisplay");
378 #endif // _TARGET_???_ (ELSE)
380 #else // !WIN64EXCEPTIONS
381 pRD->pContext = pctx;
383 // Setup the references
384 pRD->pCurrentContextPointers = &pRD->ctxPtrsOne;
385 pRD->pCallerContextPointers = &pRD->ctxPtrsTwo;
387 pRD->pCurrentContext = &(pRD->ctxOne);
388 pRD->pCallerContext = &(pRD->ctxTwo);
390 // copy the active context to initialize our stackwalk
391 *(pRD->pCurrentContext) = *(pctx);
393 // copy the caller context as well if it's specified
394 if (pCallerCtx == NULL)
396 pRD->IsCallerContextValid = FALSE;
397 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
401 *(pRD->pCallerContext) = *(pCallerCtx);
402 pRD->IsCallerContextValid = TRUE;
403 pRD->IsCallerSPValid = TRUE; // Don't add usage of this field. This is only temporary.
406 FillContextPointers(&pRD->ctxPtrsOne, pctx);
408 #if defined(_TARGET_ARM_)
409 // Fill volatile context pointers. They can be used by GC in the case of the leaf frame
410 pRD->volatileCurrContextPointers.R0 = &pctx->R0;
411 pRD->volatileCurrContextPointers.R1 = &pctx->R1;
412 pRD->volatileCurrContextPointers.R2 = &pctx->R2;
413 pRD->volatileCurrContextPointers.R3 = &pctx->R3;
414 pRD->volatileCurrContextPointers.R12 = &pctx->R12;
416 pRD->ctxPtrsOne.Lr = &pctx->Lr;
417 pRD->pPC = &pRD->pCurrentContext->Pc;
418 #elif defined(_TARGET_ARM64_) // _TARGET_ARM_
419 // Fill volatile context pointers. They can be used by GC in the case of the leaf frame
420 for (int i=0; i < 18; i++)
421 pRD->volatileCurrContextPointers.X[i] = &pctx->X[i];
422 #endif // _TARGET_ARM64_
424 #ifdef DEBUG_REGDISPLAY
425 pRD->_pThread = NULL;
426 #endif // DEBUG_REGDISPLAY
428 // This will setup the PC and SP
429 SyncRegDisplayToCurrentContext(pRD);
430 #endif // !WIN64EXCEPTIONS
433 // Initialize a new REGDISPLAY/CONTEXT pair from an existing valid REGDISPLAY.
434 inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEXT *pOutCtx)
438 // The general strategy is to extract the register state from the input REGDISPLAY
439 // into the new CONTEXT then simply call FillRegDisplay.
441 T_CONTEXT* pOutCallerCtx = NULL;
443 #ifndef WIN64EXCEPTIONS
445 #if defined(_TARGET_X86_)
446 if (pInRD->pEdi != NULL) {pOutCtx->Edi = *pInRD->pEdi;} else {pInRD->pEdi = NULL;}
447 if (pInRD->pEsi != NULL) {pOutCtx->Esi = *pInRD->pEsi;} else {pInRD->pEsi = NULL;}
448 if (pInRD->pEbx != NULL) {pOutCtx->Ebx = *pInRD->pEbx;} else {pInRD->pEbx = NULL;}
449 if (pInRD->pEbp != NULL) {pOutCtx->Ebp = *pInRD->pEbp;} else {pInRD->pEbp = NULL;}
450 if (pInRD->pEax != NULL) {pOutCtx->Eax = *pInRD->pEax;} else {pInRD->pEax = NULL;}
451 if (pInRD->pEcx != NULL) {pOutCtx->Ecx = *pInRD->pEcx;} else {pInRD->pEcx = NULL;}
452 if (pInRD->pEdx != NULL) {pOutCtx->Edx = *pInRD->pEdx;} else {pInRD->pEdx = NULL;}
453 pOutCtx->Esp = pInRD->SP;
454 pOutCtx->Eip = pInRD->ControlPC;
455 #else // _TARGET_X86_
456 PORTABILITY_ASSERT("CopyRegDisplay");
457 #endif // _TARGET_???_
459 #else // WIN64EXCEPTIONS
461 *pOutCtx = *(pInRD->pCurrentContext);
462 if (pInRD->IsCallerContextValid)
464 pOutCallerCtx = pInRD->pCallerContext;
467 #endif // WIN64EXCEPTIONS
470 FillRegDisplay(pOutRD, pOutCtx, pOutCallerCtx);
473 // Get address of a register in a CONTEXT given the reg number. For X86,
474 // the reg number is the R/M number from ModR/M byte or base in SIB byte
475 inline size_t * getRegAddr (unsigned regNum, PTR_CONTEXT regs)
478 _ASSERTE(regNum < 8);
480 static const SIZE_T OFFSET_OF_REGISTERS[] =
482 offsetof(CONTEXT, Eax),
483 offsetof(CONTEXT, Ecx),
484 offsetof(CONTEXT, Edx),
485 offsetof(CONTEXT, Ebx),
486 offsetof(CONTEXT, Esp),
487 offsetof(CONTEXT, Ebp),
488 offsetof(CONTEXT, Esi),
489 offsetof(CONTEXT, Edi),
492 return (PTR_size_t)(PTR_BYTE(regs) + OFFSET_OF_REGISTERS[regNum]);
493 #elif defined(_TARGET_AMD64_)
494 _ASSERTE(regNum < 16);
495 return ®s->Rax + regNum;
496 #elif defined(_TARGET_ARM_)
497 _ASSERTE(regNum < 16);
498 return (size_t *)®s->R0 + regNum;
499 #elif defined(_TARGET_ARM64_)
500 _ASSERTE(regNum < 31);
501 return (size_t *)®s->X0 + regNum;
503 _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)");
508 //---------------------------------------------------------------------------------------
510 // This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT.
513 // pRegDisp - the REGDISPLAY to be converted
514 // pContext - the buffer for storing the converted CONTEXT
516 inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext)
518 _ASSERTE((pRegDisp != NULL) && (pContext != NULL));
520 #ifndef WIN64EXCEPTIONS
522 #if defined(_TARGET_X86_)
523 pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
524 pContext->Edi = *pRegDisp->pEdi;
525 pContext->Esi = *pRegDisp->pEsi;
526 pContext->Ebx = *pRegDisp->pEbx;
527 pContext->Ebp = *pRegDisp->pEbp;
528 pContext->Eax = *pRegDisp->pEax;
529 pContext->Ecx = *pRegDisp->pEcx;
530 pContext->Edx = *pRegDisp->pEdx;
531 pContext->Esp = pRegDisp->SP;
532 pContext->Eip = pRegDisp->ControlPC;
533 #else // _TARGET_X86_
534 PORTABILITY_ASSERT("UpdateContextFromRegDisp");
535 #endif // _TARGET_???_
537 #else // WIN64EXCEPTIONS
539 *pContext = *pRegDisp->pCurrentContext;
541 #endif // WIN64EXCEPTIONS
545 #endif // __REGDISP_H