Deal with cross-bitness compilation warnings Pt.2 (#19781)
[platform/upstream/coreclr.git] / src / inc / regdisp.h
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
10 class Thread;
11 struct REGDISPLAY;
12 void CheckRegDisplaySP (REGDISPLAY *pRD);
13 #endif // DEBUG_REGDISPLAY
14
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
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
51 inline PCODE GetControlPC(REGDISPLAY_BASE *pRD) {
52     LIMITED_METHOD_DAC_CONTRACT;
53     return (PCODE)(pRD->ControlPC);
54 }
55
56 inline TADDR GetRegdisplaySP(REGDISPLAY_BASE *pRD) {
57     LIMITED_METHOD_DAC_CONTRACT;
58
59     return pRD->SP;
60 }
61
62 inline 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
70 struct 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
116 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
117     LIMITED_METHOD_DAC_CONTRACT;
118
119     return (TADDR)*display->GetEbpLocation();
120 }
121
122 inline 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
130 inline 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 }
139 inline 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_)
153 typedef 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_
181 struct 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
194 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
195     LIMITED_METHOD_CONTRACT;
196     return NULL; 
197 }
198
199 inline 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
205 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer)
206 {
207     LIMITED_METHOD_CONTRACT;
208     return stackPointer < ((LPVOID)(display->SP));
209 }
210
211 inline 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
240 typedef struct _ArmVolatileContextPointer
241 {
242     PDWORD R0;
243     PDWORD R1;
244     PDWORD R2;
245     PDWORD R3;
246     PDWORD R12;
247 } ArmVolatileContextPointer;
248
249 struct 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
263 private:
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
269 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
270     LIMITED_METHOD_CONTRACT;
271     return stackPointer < ((LPVOID)(TADDR)(display->SP));
272 }
273
274 inline 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.
287 inline 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
305 inline 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
328 typedef REGDISPLAY *PREGDISPLAY;
329
330 #ifdef WIN64EXCEPTIONS
331 inline 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
360 inline 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.
438 inline 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
479 inline 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 //
520 inline 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