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.
4
5#ifndef __REGDISP_H
6#define __REGDISP_H
7
8
9#ifdef DEBUG_REGDISPLAY
10class Thread;
11struct REGDISPLAY;
12void CheckRegDisplaySP (REGDISPLAY *pRD);
13#endif // DEBUG_REGDISPLAY
14
15struct REGDISPLAY_BASE {
16 PT_CONTEXT pContext; // This is the context of the active call frame;
17 // either returned by GetContext or provided at
18 // exception time.
19 //
20 // This will be used to resume execution, so
21 // do NOT trash it! But DO update any static
22 // registers here.
23
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
27
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;
32
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.
35
36 T_CONTEXT ctxOne; // used by stackwalk
37 T_CONTEXT ctxTwo; // used by stackwalk
38
39 T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsOne; // used by stackwalk
40 T_KNONVOLATILE_CONTEXT_POINTERS ctxPtrsTwo; // used by stackwalk
41#endif // WIN64EXCEPTIONS
42
43#ifdef DEBUG_REGDISPLAY
44 Thread *_pThread;
45#endif // DEBUG_REGDISPLAY
46
47 TADDR SP;
48 TADDR ControlPC;
49};
50
51inline PCODE GetControlPC(REGDISPLAY_BASE *pRD) {
52 LIMITED_METHOD_DAC_CONTRACT;
53 return (PCODE)(pRD->ControlPC);
54}
55
56inline TADDR GetRegdisplaySP(REGDISPLAY_BASE *pRD) {
57 LIMITED_METHOD_DAC_CONTRACT;
58
59 return pRD->SP;
60}
61
62inline void SetRegdisplaySP(REGDISPLAY_BASE *pRD, LPVOID sp) {
63 LIMITED_METHOD_DAC_CONTRACT;
64
65 pRD->SP = (TADDR)sp;
66}
67
68#if defined(_TARGET_X86_)
69
70struct REGDISPLAY : public REGDISPLAY_BASE {
71
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
77
78 DWORD * pEdi;
79 DWORD * pEsi;
80 DWORD * pEbx;
81 DWORD * pEdx;
82 DWORD * pEcx;
83 DWORD * pEax;
84
85 DWORD * pEbp;
86#endif // !WIN64EXCEPTIONS
87
88#ifndef WIN64EXCEPTIONS
89
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; }
93
94#else // !WIN64EXCEPTIONS
95
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; }
99
100#endif // WIN64EXCEPTIONS
101
102 REG_METHODS(Eax)
103 REG_METHODS(Ecx)
104 REG_METHODS(Edx)
105
106 REG_METHODS(Ebx)
107 REG_METHODS(Esi)
108 REG_METHODS(Edi)
109 REG_METHODS(Ebp)
110
111#undef REG_METHODS
112
113 TADDR PCTAddr;
114};
115
116inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
117 LIMITED_METHOD_DAC_CONTRACT;
118
119 return (TADDR)*display->GetEbpLocation();
120}
121
122inline LPVOID GetRegdisplayFPAddress(REGDISPLAY *display) {
123 LIMITED_METHOD_CONTRACT;
124
125 return (LPVOID)display->GetEbpLocation();
126}
127
128
129// This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
130inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
131 LIMITED_METHOD_CONTRACT;
132
133#ifdef WIN64EXCEPTIONS
134 return stackPointer < ((LPVOID)(display->SP));
135#else
136 return (TADDR)stackPointer < display->PCTAddr;
137#endif
138}
139inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
140 LIMITED_METHOD_DAC_CONTRACT;
141
142#ifdef WIN64EXCEPTIONS
143 _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext));
144 return GetRegdisplaySP(display);
145#else
146 return display->PCTAddr;
147#endif
148}
149
150#elif defined(_TARGET_64BIT_)
151
152#if defined(_TARGET_ARM64_)
153typedef struct _Arm64VolatileContextPointer
154{
155 union {
156 struct {
157 PDWORD64 X0;
158 PDWORD64 X1;
159 PDWORD64 X2;
160 PDWORD64 X3;
161 PDWORD64 X4;
162 PDWORD64 X5;
163 PDWORD64 X6;
164 PDWORD64 X7;
165 PDWORD64 X8;
166 PDWORD64 X9;
167 PDWORD64 X10;
168 PDWORD64 X11;
169 PDWORD64 X12;
170 PDWORD64 X13;
171 PDWORD64 X14;
172 PDWORD64 X15;
173 PDWORD64 X16;
174 PDWORD64 X17;
175 //X18 is reserved by OS, in userspace it represents TEB
176 };
177 PDWORD64 X[18];
178 };
179} Arm64VolatileContextPointer;
180#endif //_TARGET_ARM64_
181struct REGDISPLAY : public REGDISPLAY_BASE {
182#ifdef _TARGET_ARM64_
183 Arm64VolatileContextPointer volatileCurrContextPointers;
184#endif
185
186 REGDISPLAY()
187 {
188 // Initialize
189 memset(this, 0, sizeof(REGDISPLAY));
190 }
191};
192
193
194inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
195 LIMITED_METHOD_CONTRACT;
196 return NULL;
197}
198
199inline TADDR GetRegdisplayFPAddress(REGDISPLAY *display) {
200 LIMITED_METHOD_CONTRACT;
201 return NULL;
202}
203
204// This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
205inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer)
206{
207 LIMITED_METHOD_CONTRACT;
208 return stackPointer < ((LPVOID)(display->SP));
209}
210
211inline TADDR GetRegdisplayStackMark(REGDISPLAY *display)
212{
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);
217
218#elif defined(_TARGET_ARM64_)
219
220 _ASSERTE(display->IsCallerContextValid);
221 return GetSP(display->pCallerContext);
222
223#else // _TARGET_AMD64_
224 PORTABILITY_ASSERT("GetRegdisplayStackMark NYI for this platform (Regdisp.h)");
225 return NULL;
226#endif // _TARGET_AMD64_
227}
228
229#elif defined(_TARGET_ARM_)
230
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
240typedef struct _ArmVolatileContextPointer
241{
242 PDWORD R0;
243 PDWORD R1;
244 PDWORD R2;
245 PDWORD R3;
246 PDWORD R12;
247} ArmVolatileContextPointer;
248
249struct REGDISPLAY : public REGDISPLAY_BASE {
250 ArmVolatileContextPointer volatileCurrContextPointers;
251
252 DWORD * pPC; // processor neutral name
253#ifndef CROSSGEN_COMPILE
254 REGDISPLAY()
255 {
256 // Initialize regdisplay
257 memset(this, 0, sizeof(REGDISPLAY));
258
259 // Setup the pointer to ControlPC field
260 pPC = &ControlPC;
261 }
262#else
263private:
264 REGDISPLAY();
265#endif
266};
267
268// This function tells us if the given stack pointer is in one of the frames of the functions called by the given frame
269inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
270 LIMITED_METHOD_CONTRACT;
271 return stackPointer < ((LPVOID)(TADDR)(display->SP));
272}
273
274inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
275 LIMITED_METHOD_CONTRACT;
276 // ARM uses the establisher frame as the marker
277 _ASSERTE(display->IsCallerContextValid);
278 return GetSP(display->pCallerContext);
279}
280
281#else // none of the above processors
282#error "RegDisplay functions are not implemented on this platform."
283#endif
284
285#if defined(_TARGET_64BIT_) || defined(_TARGET_ARM_) || (defined(_TARGET_X86_) && defined(WIN64EXCEPTIONS))
286// This needs to be implemented for platforms that have funclets.
287inline LPVOID GetRegdisplayReturnValue(REGDISPLAY *display)
288{
289 LIMITED_METHOD_CONTRACT;
290
291#if defined(_TARGET_AMD64_)
292 return (LPVOID)display->pCurrentContext->Rax;
293#elif defined(_TARGET_ARM64_)
294 return (LPVOID)display->pCurrentContext->X0;
295#elif defined(_TARGET_ARM_)
296 return (LPVOID)((TADDR)display->pCurrentContext->R0);
297#elif defined(_TARGET_X86_)
298 return (LPVOID)display->pCurrentContext->Eax;
299#else
300 PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)");
301 return NULL;
302#endif
303}
304
305inline void SyncRegDisplayToCurrentContext(REGDISPLAY* pRD)
306{
307 LIMITED_METHOD_CONTRACT;
308
309#if defined(_TARGET_64BIT_)
310 pRD->SP = (INT_PTR)GetSP(pRD->pCurrentContext);
311 pRD->ControlPC = INT_PTR(GetIP(pRD->pCurrentContext));
312#elif defined(_TARGET_ARM_)
313 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
314 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
315#elif defined(_TARGET_X86_)
316 pRD->SP = (DWORD)GetSP(pRD->pCurrentContext);
317 pRD->ControlPC = (DWORD)GetIP(pRD->pCurrentContext);
318#else // _TARGET_X86_
319 PORTABILITY_ASSERT("SyncRegDisplayToCurrentContext");
320#endif
321
322#ifdef DEBUG_REGDISPLAY
323 CheckRegDisplaySP(pRD);
324#endif // DEBUG_REGDISPLAY
325}
326#endif // _TARGET_64BIT_ || _TARGET_ARM_ || (_TARGET_X86_ && WIN64EXCEPTIONS)
327
328typedef REGDISPLAY *PREGDISPLAY;
329
330#ifdef WIN64EXCEPTIONS
331inline void FillContextPointers(PT_KNONVOLATILE_CONTEXT_POINTERS pCtxPtrs, PT_CONTEXT pCtx)
332{
333#ifdef _TARGET_AMD64_
334 for (int i = 0; i < 16; i++)
335 {
336 *(&pCtxPtrs->Rax + i) = (&pCtx->Rax + i);
337 }
338#elif defined(_TARGET_ARM64_) // _TARGET_AMD64_
339 for (int i = 0; i < 12; i++)
340 {
341 *(&pCtxPtrs->X19 + i) = (&pCtx->X19 + i);
342 }
343#elif defined(_TARGET_ARM_) // _TARGET_ARM64_
344 // Copy over the nonvolatile integer registers (R4-R11)
345 for (int i = 0; i < 8; i++)
346 {
347 *(&pCtxPtrs->R4 + i) = (&pCtx->R4 + i);
348 }
349#elif defined(_TARGET_X86_) // _TARGET_ARM_
350 for (int i = 0; i < 7; i++)
351 {
352 *(&pCtxPtrs->Edi + i) = (&pCtx->Edi + i);
353 }
354#else // _TARGET_X86_
355 PORTABILITY_ASSERT("FillContextPointers");
356#endif // _TARGET_???_ (ELSE)
357}
358#endif // WIN64EXCEPTIONS
359
360inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pCallerCtx = NULL)
361{
362 WRAPPER_NO_CONTRACT;
363
364 SUPPORTS_DAC;
365
366#ifndef WIN64EXCEPTIONS
367#ifdef _TARGET_X86_
368 pRD->pContext = pctx;
369 pRD->pContextForUnwind = NULL;
370 pRD->pEdi = &(pctx->Edi);
371 pRD->pEsi = &(pctx->Esi);
372 pRD->pEbx = &(pctx->Ebx);
373 pRD->pEbp = &(pctx->Ebp);
374 pRD->pEax = &(pctx->Eax);
375 pRD->pEcx = &(pctx->Ecx);
376 pRD->pEdx = &(pctx->Edx);
377 pRD->SP = pctx->Esp;
378 pRD->ControlPC = (PCODE)(pctx->Eip);
379 pRD->PCTAddr = (UINT_PTR)&(pctx->Eip);
380#else // _TARGET_X86_
381 PORTABILITY_ASSERT("FillRegDisplay");
382#endif // _TARGET_???_ (ELSE)
383
384#else // !WIN64EXCEPTIONS
385 pRD->pContext = pctx;
386
387 // Setup the references
388 pRD->pCurrentContextPointers = &pRD->ctxPtrsOne;
389 pRD->pCallerContextPointers = &pRD->ctxPtrsTwo;
390
391 pRD->pCurrentContext = &(pRD->ctxOne);
392 pRD->pCallerContext = &(pRD->ctxTwo);
393
394 // copy the active context to initialize our stackwalk
395 *(pRD->pCurrentContext) = *(pctx);
396
397 // copy the caller context as well if it's specified
398 if (pCallerCtx == NULL)
399 {
400 pRD->IsCallerContextValid = FALSE;
401 pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
402 }
403 else
404 {
405 *(pRD->pCallerContext) = *(pCallerCtx);
406 pRD->IsCallerContextValid = TRUE;
407 pRD->IsCallerSPValid = TRUE; // Don't add usage of this field. This is only temporary.
408 }
409
410 FillContextPointers(&pRD->ctxPtrsOne, pctx);
411
412#if defined(_TARGET_ARM_)
413 // Fill volatile context pointers. They can be used by GC in the case of the leaf frame
414 pRD->volatileCurrContextPointers.R0 = &pctx->R0;
415 pRD->volatileCurrContextPointers.R1 = &pctx->R1;
416 pRD->volatileCurrContextPointers.R2 = &pctx->R2;
417 pRD->volatileCurrContextPointers.R3 = &pctx->R3;
418 pRD->volatileCurrContextPointers.R12 = &pctx->R12;
419
420 pRD->ctxPtrsOne.Lr = &pctx->Lr;
421 pRD->pPC = &pRD->pCurrentContext->Pc;
422#elif defined(_TARGET_ARM64_) // _TARGET_ARM_
423 // Fill volatile context pointers. They can be used by GC in the case of the leaf frame
424 for (int i=0; i < 18; i++)
425 pRD->volatileCurrContextPointers.X[i] = &pctx->X[i];
426#endif // _TARGET_ARM64_
427
428#ifdef DEBUG_REGDISPLAY
429 pRD->_pThread = NULL;
430#endif // DEBUG_REGDISPLAY
431
432 // This will setup the PC and SP
433 SyncRegDisplayToCurrentContext(pRD);
434#endif // !WIN64EXCEPTIONS
435}
436
437// Initialize a new REGDISPLAY/CONTEXT pair from an existing valid REGDISPLAY.
438inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEXT *pOutCtx)
439{
440 WRAPPER_NO_CONTRACT;
441
442 // The general strategy is to extract the register state from the input REGDISPLAY
443 // into the new CONTEXT then simply call FillRegDisplay.
444
445 T_CONTEXT* pOutCallerCtx = NULL;
446
447#ifndef WIN64EXCEPTIONS
448
449#if defined(_TARGET_X86_)
450 if (pInRD->pEdi != NULL) {pOutCtx->Edi = *pInRD->pEdi;} else {pInRD->pEdi = NULL;}
451 if (pInRD->pEsi != NULL) {pOutCtx->Esi = *pInRD->pEsi;} else {pInRD->pEsi = NULL;}
452 if (pInRD->pEbx != NULL) {pOutCtx->Ebx = *pInRD->pEbx;} else {pInRD->pEbx = NULL;}
453 if (pInRD->pEbp != NULL) {pOutCtx->Ebp = *pInRD->pEbp;} else {pInRD->pEbp = NULL;}
454 if (pInRD->pEax != NULL) {pOutCtx->Eax = *pInRD->pEax;} else {pInRD->pEax = NULL;}
455 if (pInRD->pEcx != NULL) {pOutCtx->Ecx = *pInRD->pEcx;} else {pInRD->pEcx = NULL;}
456 if (pInRD->pEdx != NULL) {pOutCtx->Edx = *pInRD->pEdx;} else {pInRD->pEdx = NULL;}
457 pOutCtx->Esp = pInRD->SP;
458 pOutCtx->Eip = pInRD->ControlPC;
459#else // _TARGET_X86_
460 PORTABILITY_ASSERT("CopyRegDisplay");
461#endif // _TARGET_???_
462
463#else // WIN64EXCEPTIONS
464
465 *pOutCtx = *(pInRD->pCurrentContext);
466 if (pInRD->IsCallerContextValid)
467 {
468 pOutCallerCtx = pInRD->pCallerContext;
469 }
470
471#endif // WIN64EXCEPTIONS
472
473 if (pOutRD)
474 FillRegDisplay(pOutRD, pOutCtx, pOutCallerCtx);
475}
476
477// Get address of a register in a CONTEXT given the reg number. For X86,
478// the reg number is the R/M number from ModR/M byte or base in SIB byte
479inline size_t * getRegAddr (unsigned regNum, PTR_CONTEXT regs)
480{
481#ifdef _TARGET_X86_
482 _ASSERTE(regNum < 8);
483
484 static const SIZE_T OFFSET_OF_REGISTERS[] =
485 {
486 offsetof(CONTEXT, Eax),
487 offsetof(CONTEXT, Ecx),
488 offsetof(CONTEXT, Edx),
489 offsetof(CONTEXT, Ebx),
490 offsetof(CONTEXT, Esp),
491 offsetof(CONTEXT, Ebp),
492 offsetof(CONTEXT, Esi),
493 offsetof(CONTEXT, Edi),
494 };
495
496 return (PTR_size_t)(PTR_BYTE(regs) + OFFSET_OF_REGISTERS[regNum]);
497#elif defined(_TARGET_AMD64_)
498 _ASSERTE(regNum < 16);
499 return &regs->Rax + regNum;
500#elif defined(_TARGET_ARM_)
501 _ASSERTE(regNum < 16);
502 return (size_t *)&regs->R0 + regNum;
503#elif defined(_TARGET_ARM64_)
504 _ASSERTE(regNum < 31);
505 return (size_t *)&regs->X0 + regNum;
506#else
507 _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)");
508#endif
509 return(0);
510}
511
512//---------------------------------------------------------------------------------------
513//
514// This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT.
515//
516// Arguments:
517// pRegDisp - the REGDISPLAY to be converted
518// pContext - the buffer for storing the converted CONTEXT
519//
520inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext)
521{
522 _ASSERTE((pRegDisp != NULL) && (pContext != NULL));
523
524#ifndef WIN64EXCEPTIONS
525
526#if defined(_TARGET_X86_)
527 pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
528 pContext->Edi = *pRegDisp->pEdi;
529 pContext->Esi = *pRegDisp->pEsi;
530 pContext->Ebx = *pRegDisp->pEbx;
531 pContext->Ebp = *pRegDisp->pEbp;
532 pContext->Eax = *pRegDisp->pEax;
533 pContext->Ecx = *pRegDisp->pEcx;
534 pContext->Edx = *pRegDisp->pEdx;
535 pContext->Esp = pRegDisp->SP;
536 pContext->Eip = pRegDisp->ControlPC;
537#else // _TARGET_X86_
538 PORTABILITY_ASSERT("UpdateContextFromRegDisp");
539#endif // _TARGET_???_
540
541#else // WIN64EXCEPTIONS
542
543 *pContext = *pRegDisp->pCurrentContext;
544
545#endif // WIN64EXCEPTIONS
546}
547
548
549#endif // __REGDISP_H
550
551
552