Merge pull request #9191 from sandreenko/CORERT-fat-call-transformation
[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 #if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
48     DWORD   SP;                // Stack Pointer
49     PCODE   ControlPC;
50 #else // _TARGET_X86_ || _TARGET_ARM_
51     size_t  SP;
52     size_t  ControlPC;
53 #endif // !_TARGET_X86_ && !_TARGET_ARM
54 };
55
56 #if defined(_TARGET_X86_)
57
58 struct REGDISPLAY : public REGDISPLAY_BASE {
59
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
65
66     DWORD * pEdi;
67     DWORD * pEsi;
68     DWORD * pEbx;
69     DWORD * pEdx;
70     DWORD * pEcx;
71     DWORD * pEax;
72
73     DWORD * pEbp;
74 #endif // !WIN64EXCEPTIONS
75
76 #ifndef WIN64EXCEPTIONS
77
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; }
81
82 #else // !WIN64EXCEPTIONS
83
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; }
87
88 #endif // WIN64EXCEPTIONS
89
90     REG_METHODS(Eax)
91     REG_METHODS(Ecx)
92     REG_METHODS(Edx)
93
94     REG_METHODS(Ebx)
95     REG_METHODS(Esi)
96     REG_METHODS(Edi)
97     REG_METHODS(Ebp)
98
99 #undef REG_METHODS
100
101     TADDR   PCTAddr;
102 };
103
104 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
105     LIMITED_METHOD_DAC_CONTRACT;
106
107     return (TADDR)display->SP;
108 }
109
110 inline void SetRegdisplaySP(REGDISPLAY *display, LPVOID sp ) {
111     LIMITED_METHOD_DAC_CONTRACT;
112
113     (display->SP) = (DWORD)(size_t)sp;
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 inline PCODE GetControlPC(REGDISPLAY *display) {
129     LIMITED_METHOD_DAC_CONTRACT;
130
131     return display->ControlPC;
132 }
133
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;
137
138 #ifdef WIN64EXCEPTIONS
139     return stackPointer < ((LPVOID)(display->SP));
140 #else
141     return (TADDR)stackPointer < display->PCTAddr;
142 #endif
143 }
144 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display) {
145     LIMITED_METHOD_DAC_CONTRACT;
146
147 #ifdef WIN64EXCEPTIONS
148     _ASSERTE(GetRegdisplaySP(display) == GetSP(display->pCurrentContext));
149     return GetRegdisplaySP(display);
150 #else
151     return display->PCTAddr;
152 #endif
153 }
154
155 #elif defined(_WIN64)
156
157 #if defined(_TARGET_ARM64_)
158 typedef struct _Arm64VolatileContextPointer
159 {
160     union {
161         struct {
162             PDWORD64 X0;
163             PDWORD64 X1;
164             PDWORD64 X2;
165             PDWORD64 X3;
166             PDWORD64 X4;
167             PDWORD64 X5;
168             PDWORD64 X6;
169             PDWORD64 X7;
170             PDWORD64 X8;
171             PDWORD64 X9;
172             PDWORD64 X10;
173             PDWORD64 X11;
174             PDWORD64 X12;
175             PDWORD64 X13;
176             PDWORD64 X14;
177             PDWORD64 X15;
178             PDWORD64 X16;
179             PDWORD64 X17;
180             //X18 is reserved by OS, in userspace it represents TEB
181         };
182         PDWORD64 X[18];
183     };
184 } Arm64VolatileContextPointer;
185 #endif //_TARGET_ARM64_
186 struct REGDISPLAY : public REGDISPLAY_BASE {
187 #ifdef _TARGET_ARM64_
188     Arm64VolatileContextPointer     volatileCurrContextPointers;
189 #endif
190
191     REGDISPLAY()
192     {
193         // Initialize
194         memset(this, 0, sizeof(REGDISPLAY));
195     }
196 };
197
198 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
199     LIMITED_METHOD_DAC_CONTRACT;
200     return (TADDR)display->SP;
201 }
202
203 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
204     LIMITED_METHOD_CONTRACT;
205     return NULL; 
206 }
207
208 inline TADDR GetRegdisplayFPAddress(REGDISPLAY *display) {
209     LIMITED_METHOD_CONTRACT;
210     return NULL; 
211 }
212
213 inline PCODE GetControlPC(REGDISPLAY *display) {
214     LIMITED_METHOD_DAC_CONTRACT;
215     return (PCODE)(display->ControlPC);
216 }
217
218
219
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)
222 {
223     LIMITED_METHOD_CONTRACT;
224     return stackPointer < ((LPVOID)(display->SP));
225 }
226
227 inline TADDR GetRegdisplayStackMark(REGDISPLAY *display)
228 {
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);
233
234 #elif defined(_TARGET_ARM64_)
235
236     _ASSERTE(display->IsCallerContextValid);
237     return GetSP(display->pCallerContext);
238
239 #else  // _TARGET_AMD64_
240     PORTABILITY_ASSERT("GetRegdisplayStackMark NYI for this platform (Regdisp.h)");
241     return NULL;
242 #endif // _TARGET_AMD64_
243 }
244
245
246 #elif defined(_TARGET_ARM_)
247
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
258 {
259     PDWORD R0;
260     PDWORD R1;
261     PDWORD R2;
262     PDWORD R3;
263     PDWORD R12;
264 } ArmVolatileContextPointer;
265
266 struct REGDISPLAY : public REGDISPLAY_BASE {
267     ArmVolatileContextPointer     volatileCurrContextPointers;
268
269     DWORD *  pPC;                // processor neutral name
270
271     REGDISPLAY()
272     {
273         // Initialize regdisplay
274         memset(this, 0, sizeof(REGDISPLAY));
275
276         // Setup the pointer to ControlPC field
277         pPC = &ControlPC;
278     }
279 };
280
281 inline TADDR GetRegdisplaySP(REGDISPLAY *display) {
282     LIMITED_METHOD_DAC_CONTRACT;
283     return (TADDR)(size_t)display->SP;
284 }
285
286 inline PCODE GetControlPC(REGDISPLAY *display) {
287     LIMITED_METHOD_DAC_CONTRACT;
288     return (PCODE)(display->ControlPC);
289 }
290
291
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));
296 }
297
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);
303 }
304
305 #else // none of the above processors
306
307 PORTABILITY_WARNING("RegDisplay functions are not implemented on this platform.")
308
309 struct REGDISPLAY : public REGDISPLAY_BASE {
310     size_t * FramePtr;
311     SLOT   * pPC;
312 };
313
314 inline PCODE GetControlPC(REGDISPLAY *display) {
315     LIMITED_METHOD_CONTRACT;
316     return (PCODE) NULL;
317 }
318
319 inline LPVOID GetRegdisplaySP(REGDISPLAY *display) {
320     LIMITED_METHOD_DAC_CONTRACT;
321     return (LPVOID)display->SP;
322 }
323
324 inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
325     LIMITED_METHOD_CONTRACT;
326     return (TADDR)*(display->FramePtr);
327 }
328
329 inline BOOL IsInCalleesFrames(REGDISPLAY *display, LPVOID stackPointer) {
330     LIMITED_METHOD_CONTRACT;
331     return FALSE;
332 }
333 inline LPVOID GetRegdisplayStackMark(REGDISPLAY *display) {
334     LIMITED_METHOD_CONTRACT;
335     return (LPVOID)display->SP;
336 }
337
338 #endif
339
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)
343 {
344     LIMITED_METHOD_CONTRACT;
345
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;
354 #else
355     PORTABILITY_ASSERT("GetRegdisplayReturnValue NYI for this platform (Regdisp.h)");
356     return NULL;
357 #endif
358 }
359
360 inline void SyncRegDisplayToCurrentContext(REGDISPLAY* pRD)
361 {
362     LIMITED_METHOD_CONTRACT;
363
364 #if defined(_WIN64)
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_
376
377 #ifdef DEBUG_REGDISPLAY
378     CheckRegDisplaySP(pRD);
379 #endif // DEBUG_REGDISPLAY
380 }
381 #endif // _WIN64 || _TARGET_ARM_ || (_TARGET_X86_ && WIN64EXCEPTIONS)
382
383 typedef REGDISPLAY *PREGDISPLAY;
384
385
386 inline void FillRegDisplay(const PREGDISPLAY pRD, PT_CONTEXT pctx, PT_CONTEXT pCallerCtx = NULL)
387 {
388     WRAPPER_NO_CONTRACT;
389
390     SUPPORTS_DAC;
391
392 #ifndef WIN64EXCEPTIONS
393 #ifdef _TARGET_X86_
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);
403     pRD->SP   = pctx->Esp;
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)
409
410 #else // !WIN64EXCEPTIONS
411     pRD->pContext   = pctx;
412
413     // Setup the references
414     pRD->pCurrentContextPointers = &pRD->ctxPtrsOne;
415     pRD->pCallerContextPointers = &pRD->ctxPtrsTwo;
416
417     pRD->pCurrentContext = &(pRD->ctxOne);
418     pRD->pCallerContext  = &(pRD->ctxTwo);
419
420     // copy the active context to initialize our stackwalk
421     *(pRD->pCurrentContext)     = *(pctx);
422
423     // copy the caller context as well if it's specified
424     if (pCallerCtx == NULL)
425     {
426         pRD->IsCallerContextValid = FALSE;
427         pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
428     }
429     else
430     {
431         *(pRD->pCallerContext)    = *(pCallerCtx);
432         pRD->IsCallerContextValid = TRUE;
433         pRD->IsCallerSPValid      = TRUE;        // Don't add usage of this field.  This is only temporary.
434     }
435
436 #ifdef _TARGET_AMD64_
437     for (int i = 0; i < 16; i++)
438     {
439         *(&pRD->ctxPtrsOne.Rax + i) = (&pctx->Rax + i);
440     }
441 #elif defined(_TARGET_ARM64_) // _TARGET_AMD64_
442     for (int i = 0; i < 12; i++)
443     {
444         *(&pRD->ctxPtrsOne.X19 + i) = (&pctx->X19 + i);
445     }
446 #elif defined(_TARGET_ARM_) // _TARGET_ARM64_
447     // Copy over the nonvolatile integer registers (R4-R11)
448     for (int i = 0; i < 8; i++)
449     {
450         *(&pRD->ctxPtrsOne.R4 + i) = (&pctx->R4 + i);
451     }
452
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++)
457     {
458         *(&pRD->ctxPtrsOne.Esi + i) = (&pctx->Esi + i);
459     }
460 #else // _TARGET_X86_
461     PORTABILITY_ASSERT("FillRegDisplay");
462 #endif // _TARGET_???_ (ELSE)
463
464 #ifdef DEBUG_REGDISPLAY
465     pRD->_pThread = NULL;
466 #endif // DEBUG_REGDISPLAY
467
468     // This will setup the PC and SP
469     SyncRegDisplayToCurrentContext(pRD);
470 #endif // !WIN64EXCEPTIONS
471 }
472
473 // Initialize a new REGDISPLAY/CONTEXT pair from an existing valid REGDISPLAY.
474 inline void CopyRegDisplay(const PREGDISPLAY pInRD, PREGDISPLAY pOutRD, T_CONTEXT *pOutCtx)
475 {
476     WRAPPER_NO_CONTRACT;
477
478     // The general strategy is to extract the register state from the input REGDISPLAY 
479     // into the new CONTEXT then simply call FillRegDisplay.
480
481     T_CONTEXT* pOutCallerCtx = NULL;
482
483 #ifndef WIN64EXCEPTIONS
484
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_???_
498
499 #else // WIN64EXCEPTIONS
500
501     *pOutCtx = *(pInRD->pCurrentContext);
502     if (pInRD->IsCallerContextValid)
503     {
504         pOutCallerCtx = pInRD->pCallerContext;
505     }
506
507 #endif // WIN64EXCEPTIONS
508
509     if (pOutRD)
510         FillRegDisplay(pOutRD, pOutCtx, pOutCallerCtx);
511 }
512
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)
516 {
517 #ifdef _TARGET_X86_
518     switch (regNum)
519     {
520     case 0:
521         return (size_t *)&regs->Eax;
522         break;
523     case 1:
524         return (size_t *)&regs->Ecx;
525         break;
526     case 2:
527         return (size_t *)&regs->Edx;
528         break;
529     case 3:
530         return (size_t *)&regs->Ebx;
531         break;
532     case 4:
533         return (size_t *)&regs->Esp;
534         break;
535     case 5:
536         return (size_t *)&regs->Ebp;
537         break;
538     case 6:
539         return (size_t *)&regs->Esi;
540         break;
541     case 7:
542         return (size_t *)&regs->Edi;
543         break;
544     default:
545         _ASSERTE (!"unknown regNum");
546     }
547 #elif defined(_TARGET_AMD64_)
548     _ASSERTE(regNum < 16);
549     return &regs->Rax + regNum;
550 #elif defined(_TARGET_ARM_)
551         _ASSERTE(regNum < 16);
552         return (size_t *)&regs->R0 + regNum;
553 #elif defined(_TARGET_ARM64_)
554     _ASSERTE(regNum < 31);
555     return (size_t *)&regs->X0 + regNum;
556 #else
557     _ASSERTE(!"@TODO Port - getRegAddr (Regdisp.h)");
558 #endif
559     return(0);
560 }
561
562 //---------------------------------------------------------------------------------------
563 //
564 // This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT.
565 //
566 // Arguments:
567 //    pRegDisp - the REGDISPLAY to be converted
568 //    pContext - the buffer for storing the converted CONTEXT
569 //
570 inline void UpdateContextFromRegDisp(PREGDISPLAY pRegDisp, PT_CONTEXT pContext)
571 {
572     _ASSERTE((pRegDisp != NULL) && (pContext != NULL));
573
574 #ifndef WIN64EXCEPTIONS
575
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_???_
590
591 #else // WIN64EXCEPTIONS
592
593     *pContext = *pRegDisp->pCurrentContext;
594
595 #endif // WIN64EXCEPTIONS
596 }
597
598
599 #endif  // __REGDISP_H
600
601