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/*****************************************************************
6 *
7 * GC Information Decoding API
8 *
9 *****************************************************************/
10
11#ifndef _GC_INFO_DECODER_
12#define _GC_INFO_DECODER_
13
14#define _max(a, b) (((a) > (b)) ? (a) : (b))
15#define _min(a, b) (((a) < (b)) ? (a) : (b))
16
17#if !defined(_TARGET_X86_)
18#define USE_GC_INFO_DECODER
19#endif
20
21#if !defined(GCINFODECODER_NO_EE)
22
23#include "eetwain.h"
24
25#else
26
27#ifdef FEATURE_REDHAWK
28
29typedef ArrayDPTR(const uint8_t) PTR_CBYTE;
30
31#define LIMITED_METHOD_CONTRACT
32#define SUPPORTS_DAC
33
34#define LOG(x)
35#define LOG_PIPTR(pObjRef, gcFlags, hCallBack)
36#define DAC_ARG(x)
37
38#define VALIDATE_ROOT(isInterior, hCallBack, pObjRef)
39
40#define _ASSERTE(x) assert(x)
41
42#define UINT32 UInt32
43#define INT32 Int32
44#define UINT16 UInt16
45#define UINT UInt32
46#define SIZE_T UIntNative
47#define SSIZE_T IntNative
48#define LPVOID void*
49
50typedef void * OBJECTREF;
51
52#define GET_CALLER_SP(pREGDISPLAY) ((TADDR)0)
53
54#else // FEATURE_REDHAWK
55
56// Stuff from cgencpu.h:
57
58#ifndef __cgencpu_h__
59
60inline void SetIP(T_CONTEXT* context, PCODE rip)
61{
62 _ASSERTE(!"don't call this");
63}
64
65inline TADDR GetSP(T_CONTEXT* context)
66{
67#ifdef _TARGET_AMD64_
68 return (TADDR)context->Rsp;
69#elif defined(_TARGET_ARM_)
70 return (TADDR)context->Sp;
71#elif defined(_TARGET_ARM64_)
72 return (TADDR)context->Sp;
73#else
74 _ASSERTE(!"nyi for platform");
75#endif
76}
77
78inline PCODE GetIP(T_CONTEXT* context)
79{
80#ifdef _TARGET_AMD64_
81 return (PCODE) context->Rip;
82#elif defined(_TARGET_ARM_)
83 return (PCODE)context->Pc;
84#elif defined(_TARGET_ARM64_)
85 return (PCODE)context->Pc;
86#else
87 _ASSERTE(!"nyi for platform");
88#endif
89}
90
91#endif // !__cgencpu_h__
92
93// Misc. VM types:
94
95#ifndef DEFINE_OBJECTREF
96#define DEFINE_OBJECTREF
97class Object;
98typedef Object *OBJECTREF;
99#endif
100typedef SIZE_T TADDR;
101
102// Stuff from gc.h:
103
104#ifndef __GC_H
105
106#define GC_CALL_INTERIOR 0x1
107#define GC_CALL_PINNED 0x2
108
109#endif // !__GC_H
110
111// Stuff from stdmacros.h (can't include because it includes contract.h, which uses #pragma once)
112
113#ifndef _stdmacros_h_
114
115inline BOOL IS_ALIGNED( size_t val, size_t alignment )
116{
117 // alignment must be a power of 2 for this implementation to work (need modulo otherwise)
118 _ASSERTE( 0 == (alignment & (alignment - 1)) );
119 return 0 == (val & (alignment - 1));
120}
121inline BOOL IS_ALIGNED( void* val, size_t alignment )
122{
123 return IS_ALIGNED( (size_t) val, alignment );
124}
125
126#define FMT_REG "r%d "
127#define FMT_STK "sp%s0x%02x "
128
129#define DBG_STK(off) \
130 (off >= 0) ? "+" : "-", \
131 (off >= 0) ? off : -off
132
133#endif
134
135// Stuff from check.h:
136
137#ifndef UNREACHABLE
138#define UNREACHABLE() __assume(0)
139#endif
140
141// Stuff from eetwain.h:
142
143#ifndef _EETWAIN_H
144
145typedef void (*GCEnumCallback)(
146 void * hCallback, // callback data
147 OBJECTREF* pObject, // address of obect-reference we are reporting
148 uint32_t flags // is this a pinned and/or interior pointer
149);
150
151#endif // !_EETWAIN_H
152
153#include "regdisp.h"
154
155#endif // FEATURE_REDHAWK
156
157#ifndef _strike_h
158
159enum ICodeManagerFlags
160{
161 ActiveStackFrame = 0x0001, // this is the currently active function
162 ExecutionAborted = 0x0002, // execution of this function has been aborted
163 // (i.e. it will not continue execution at the
164 // current location)
165 ParentOfFuncletStackFrame
166 = 0x0040, // A funclet for this frame was previously reported
167
168 NoReportUntracked
169 = 0x0080, // EnumGCRefs/EnumerateLiveSlots should *not* include
170 // any untracked slots
171};
172
173#endif // !_strike_h
174
175#endif // GCINFODECODER_NO_EE
176
177
178#include "gcinfotypes.h"
179
180#ifdef _DEBUG
181 #define MAX_PREDECODED_SLOTS 4
182#else
183 #define MAX_PREDECODED_SLOTS 64
184#endif
185
186
187
188enum GcInfoDecoderFlags
189{
190 DECODE_EVERYTHING = 0x0,
191 DECODE_SECURITY_OBJECT = 0x01, // stack location of security object
192 DECODE_CODE_LENGTH = 0x02,
193 DECODE_VARARG = 0x04,
194 DECODE_INTERRUPTIBILITY = 0x08,
195 DECODE_GC_LIFETIMES = 0x10,
196 DECODE_NO_VALIDATION = 0x20,
197 DECODE_PSP_SYM = 0x40,
198 DECODE_GENERICS_INST_CONTEXT = 0x80, // stack location of instantiation context for generics
199 // (this may be either the 'this' ptr or the instantiation secret param)
200 DECODE_GS_COOKIE = 0x100, // stack location of the GS cookie
201 DECODE_FOR_RANGES_CALLBACK = 0x200,
202 DECODE_PROLOG_LENGTH = 0x400, // length of the prolog (used to avoid reporting generics context)
203 DECODE_EDIT_AND_CONTINUE = 0x800,
204 DECODE_REVERSE_PINVOKE_VAR = 0x1000,
205 DECODE_RETURN_KIND = 0x2000,
206#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
207 DECODE_HAS_TAILCALLS = 0x4000,
208#endif // _TARGET_ARM_ || _TARGET_ARM64_
209};
210
211enum GcInfoHeaderFlags
212{
213 GC_INFO_IS_VARARG = 0x1,
214 GC_INFO_HAS_SECURITY_OBJECT = 0x2,
215 GC_INFO_HAS_GS_COOKIE = 0x4,
216 GC_INFO_HAS_PSP_SYM = 0x8,
217 GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK = 0x30,
218 GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE = 0x00,
219 GC_INFO_HAS_GENERICS_INST_CONTEXT_MT = 0x10,
220 GC_INFO_HAS_GENERICS_INST_CONTEXT_MD = 0x20,
221 GC_INFO_HAS_GENERICS_INST_CONTEXT_THIS = 0x30,
222 GC_INFO_HAS_STACK_BASE_REGISTER = 0x40,
223#ifdef _TARGET_AMD64_
224 GC_INFO_WANTS_REPORT_ONLY_LEAF = 0x80,
225#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
226 GC_INFO_HAS_TAILCALLS = 0x80,
227#endif // _TARGET_AMD64_
228 GC_INFO_HAS_EDIT_AND_CONTINUE_PRESERVED_SLOTS = 0x100,
229 GC_INFO_REVERSE_PINVOKE_FRAME = 0x200,
230
231 GC_INFO_FLAGS_BIT_SIZE_VERSION_1 = 9,
232 GC_INFO_FLAGS_BIT_SIZE = 10,
233};
234
235class BitStreamReader
236{
237public:
238 BitStreamReader()
239 {
240 SUPPORTS_DAC;
241 }
242
243 BitStreamReader( PTR_CBYTE pBuffer )
244 {
245 SUPPORTS_DAC;
246
247 _ASSERTE( pBuffer != NULL );
248
249 m_pCurrent = m_pBuffer = dac_cast<PTR_size_t>((size_t)dac_cast<TADDR>(pBuffer) & ~((size_t)sizeof(size_t)-1));
250 m_RelPos = m_InitialRelPos = (int)((size_t)dac_cast<TADDR>(pBuffer) % sizeof(size_t)) * 8/*BITS_PER_BYTE*/;
251 }
252
253 BitStreamReader(const BitStreamReader& other)
254 {
255 SUPPORTS_DAC;
256
257 m_pBuffer = other.m_pBuffer;
258 m_InitialRelPos = other.m_InitialRelPos;
259 m_pCurrent = other.m_pCurrent;
260 m_RelPos = other.m_RelPos;
261 }
262
263 const BitStreamReader& operator=(const BitStreamReader& other)
264 {
265 SUPPORTS_DAC;
266
267 m_pBuffer = other.m_pBuffer;
268 m_InitialRelPos = other.m_InitialRelPos;
269 m_pCurrent = other.m_pCurrent;
270 m_RelPos = other.m_RelPos;
271 return *this;
272 }
273
274 // NOTE: This routine is perf-critical
275 __forceinline size_t Read( int numBits )
276 {
277 SUPPORTS_DAC;
278
279 _ASSERTE(numBits > 0 && numBits <= BITS_PER_SIZE_T);
280
281 size_t result = (*m_pCurrent) >> m_RelPos;
282 int newRelPos = m_RelPos + numBits;
283 if(newRelPos >= BITS_PER_SIZE_T)
284 {
285 m_pCurrent++;
286 newRelPos -= BITS_PER_SIZE_T;
287 if(newRelPos > 0)
288 {
289 size_t extraBits = (*m_pCurrent) << (numBits - newRelPos);
290 result ^= extraBits;
291 }
292 }
293 m_RelPos = newRelPos;
294 result &= SAFE_SHIFT_LEFT(1, numBits) - 1;
295 return result;
296 }
297
298 // This version reads one bit, returning zero/non-zero (not 0/1)
299 // NOTE: This routine is perf-critical
300 __forceinline size_t ReadOneFast()
301 {
302 SUPPORTS_DAC;
303
304 size_t result = (*m_pCurrent) & (((size_t)1) << m_RelPos);
305 if(++m_RelPos == BITS_PER_SIZE_T)
306 {
307 m_pCurrent++;
308 m_RelPos = 0;
309 }
310 return result;
311 }
312
313
314 __forceinline size_t GetCurrentPos()
315 {
316 SUPPORTS_DAC;
317 return (size_t) ((m_pCurrent - m_pBuffer) * BITS_PER_SIZE_T + m_RelPos - m_InitialRelPos);
318 }
319
320 __forceinline void SetCurrentPos( size_t pos )
321 {
322 size_t adjPos = pos + m_InitialRelPos;
323 m_pCurrent = m_pBuffer + adjPos / BITS_PER_SIZE_T;
324 m_RelPos = (int)(adjPos % BITS_PER_SIZE_T);
325 _ASSERTE(GetCurrentPos() == pos);
326 }
327
328 __forceinline void Skip( SSIZE_T numBitsToSkip )
329 {
330 SUPPORTS_DAC;
331
332 SetCurrentPos(GetCurrentPos() + numBitsToSkip);
333 }
334
335 __forceinline void AlignUpToByte()
336 {
337 if(m_RelPos <= BITS_PER_SIZE_T - 8)
338 {
339 m_RelPos = (m_RelPos + 7) & ~7;
340 }
341 else
342 {
343 m_RelPos = 0;
344 m_pCurrent++;
345 }
346 }
347
348 __forceinline size_t ReadBitAtPos( size_t pos )
349 {
350 size_t adjPos = pos + m_InitialRelPos;
351 size_t* ptr = m_pBuffer + adjPos / BITS_PER_SIZE_T;
352 int relPos = (int)(adjPos % BITS_PER_SIZE_T);
353 return (*ptr) & (((size_t)1) << relPos);
354 }
355
356
357 //--------------------------------------------------------------------------
358 // Decode variable length numbers
359 // See the corresponding methods on BitStreamWriter for more information on the format
360 //--------------------------------------------------------------------------
361
362 inline size_t DecodeVarLengthUnsigned( int base )
363 {
364 _ASSERTE((base > 0) && (base < (int)BITS_PER_SIZE_T));
365 size_t numEncodings = size_t{ 1 } << base;
366 size_t result = 0;
367 for(int shift=0; ; shift+=base)
368 {
369 _ASSERTE(shift+base <= (int)BITS_PER_SIZE_T);
370
371 size_t currentChunk = Read(base+1);
372 result |= (currentChunk & (numEncodings-1)) << shift;
373 if(!(currentChunk & numEncodings))
374 {
375 // Extension bit is not set, we're done.
376 return result;
377 }
378 }
379 }
380
381 inline SSIZE_T DecodeVarLengthSigned( int base )
382 {
383 _ASSERTE((base > 0) && (base < (int)BITS_PER_SIZE_T));
384 size_t numEncodings = size_t{ 1 } << base;
385 SSIZE_T result = 0;
386 for(int shift=0; ; shift+=base)
387 {
388 _ASSERTE(shift+base <= (int)BITS_PER_SIZE_T);
389
390 size_t currentChunk = Read(base+1);
391 result |= (currentChunk & (numEncodings-1)) << shift;
392 if(!(currentChunk & numEncodings))
393 {
394 // Extension bit is not set, sign-extend and we're done.
395 int sbits = BITS_PER_SIZE_T - (shift+base);
396 result <<= sbits;
397 result >>= sbits; // This provides the sign extension
398 return result;
399 }
400 }
401 }
402
403private:
404 PTR_size_t m_pBuffer;
405 int m_InitialRelPos;
406 PTR_size_t m_pCurrent;
407 int m_RelPos;
408};
409
410struct GcSlotDesc
411{
412 union
413 {
414 UINT32 RegisterNumber;
415 GcStackSlot Stack;
416 } Slot;
417 GcSlotFlags Flags;
418};
419
420class GcSlotDecoder
421{
422public:
423 GcSlotDecoder()
424 {}
425
426 void DecodeSlotTable(BitStreamReader& reader);
427
428 UINT32 GetNumSlots()
429 {
430 return m_NumSlots;
431 }
432
433 UINT32 GetNumUntracked()
434 {
435 return m_NumUntracked;
436 }
437
438 UINT32 GetNumTracked()
439 {
440 return m_NumSlots - m_NumUntracked;
441 }
442
443 UINT32 GetNumRegisters()
444 {
445 return m_NumRegisters;
446 }
447
448 const GcSlotDesc* GetSlotDesc(UINT32 slotIndex);
449
450private:
451 GcSlotDesc m_SlotArray[MAX_PREDECODED_SLOTS];
452 BitStreamReader m_SlotReader;
453 UINT32 m_NumSlots;
454 UINT32 m_NumRegisters;
455 UINT32 m_NumUntracked;
456
457 UINT32 m_NumDecodedSlots;
458 GcSlotDesc* m_pLastSlot;
459};
460
461#ifdef USE_GC_INFO_DECODER
462class GcInfoDecoder
463{
464public:
465
466 // If you are not interested in interruptibility or gc lifetime information, pass 0 as instructionOffset
467 GcInfoDecoder(
468 GCInfoToken gcInfoToken,
469 GcInfoDecoderFlags flags = DECODE_EVERYTHING,
470 UINT32 instructionOffset = 0
471 );
472
473 //------------------------------------------------------------------------
474 // Interruptibility
475 //------------------------------------------------------------------------
476
477 bool IsInterruptible();
478
479#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
480 // This is used for gccoverage
481 bool IsSafePoint(UINT32 codeOffset);
482
483 typedef void EnumerateSafePointsCallback (UINT32 offset, void * hCallback);
484 void EnumerateSafePoints(EnumerateSafePointsCallback * pCallback, void * hCallback);
485
486#endif
487 // Returns true to stop enumerating.
488 typedef bool EnumerateInterruptibleRangesCallback (UINT32 startOffset, UINT32 stopOffset, void * hCallback);
489
490 void EnumerateInterruptibleRanges (
491 EnumerateInterruptibleRangesCallback *pCallback,
492 void * hCallback);
493
494 //------------------------------------------------------------------------
495 // GC lifetime information
496 //------------------------------------------------------------------------
497
498 bool EnumerateLiveSlots(
499 PREGDISPLAY pRD,
500 bool reportScratchSlots,
501 unsigned flags,
502 GCEnumCallback pCallBack,
503 void * hCallBack
504 );
505
506 // Public for the gc info dumper
507 void EnumerateUntrackedSlots(
508 PREGDISPLAY pRD,
509 unsigned flags,
510 GCEnumCallback pCallBack,
511 void * hCallBack
512 );
513
514 //------------------------------------------------------------------------
515 // Miscellaneous method information
516 //------------------------------------------------------------------------
517
518 INT32 GetSecurityObjectStackSlot();
519 INT32 GetGSCookieStackSlot();
520 UINT32 GetGSCookieValidRangeStart();
521 UINT32 GetGSCookieValidRangeEnd();
522 UINT32 GetPrologSize();
523 INT32 GetPSPSymStackSlot();
524 INT32 GetGenericsInstContextStackSlot();
525 INT32 GetReversePInvokeFrameStackSlot();
526 bool HasMethodDescGenericsInstContext();
527 bool HasMethodTableGenericsInstContext();
528 bool GetIsVarArg();
529 bool WantsReportOnlyLeaf();
530#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
531 bool HasTailCalls();
532#endif // _TARGET_ARM_ || _TARGET_ARM64_
533 ReturnKind GetReturnKind();
534 UINT32 GetCodeLength();
535 UINT32 GetStackBaseRegister();
536 UINT32 GetSizeOfEditAndContinuePreservedArea();
537 size_t GetNumBytesRead();
538
539#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
540 UINT32 GetSizeOfStackParameterArea();
541#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
542
543
544private:
545 BitStreamReader m_Reader;
546 UINT32 m_InstructionOffset;
547
548 // Pre-decoded information
549 bool m_IsInterruptible;
550 bool m_IsVarArg;
551 bool m_GenericSecretParamIsMD;
552 bool m_GenericSecretParamIsMT;
553#ifdef _TARGET_AMD64_
554 bool m_WantsReportOnlyLeaf;
555#elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
556 bool m_HasTailCalls;
557#endif // _TARGET_AMD64_
558 INT32 m_SecurityObjectStackSlot;
559 INT32 m_GSCookieStackSlot;
560 INT32 m_ReversePInvokeFrameStackSlot;
561 UINT32 m_ValidRangeStart;
562 UINT32 m_ValidRangeEnd;
563 INT32 m_PSPSymStackSlot;
564 INT32 m_GenericsInstContextStackSlot;
565 UINT32 m_CodeLength;
566 UINT32 m_StackBaseRegister;
567 UINT32 m_SizeOfEditAndContinuePreservedArea;
568 ReturnKind m_ReturnKind;
569#ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED
570 UINT32 m_NumSafePoints;
571 UINT32 m_SafePointIndex;
572 UINT32 FindSafePoint(UINT32 codeOffset);
573#endif
574 UINT32 m_NumInterruptibleRanges;
575
576#ifdef FIXED_STACK_PARAMETER_SCRATCH_AREA
577 UINT32 m_SizeOfStackOutgoingAndScratchArea;
578#endif // FIXED_STACK_PARAMETER_SCRATCH_AREA
579
580#ifdef _DEBUG
581 GcInfoDecoderFlags m_Flags;
582 PTR_CBYTE m_GcInfoAddress;
583#endif
584 UINT32 m_Version;
585
586 static bool SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset, void * hCallback);
587
588 OBJECTREF* GetRegisterSlot(
589 int regNum,
590 PREGDISPLAY pRD
591 );
592
593#ifdef FEATURE_PAL
594 OBJECTREF* GetCapturedRegister(
595 int regNum,
596 PREGDISPLAY pRD
597 );
598#endif // FEATURE_PAL
599
600 OBJECTREF* GetStackSlot(
601 INT32 spOffset,
602 GcStackSlotBase spBase,
603 PREGDISPLAY pRD
604 );
605
606#ifdef DACCESS_COMPILE
607 int GetStackReg(int spBase);
608#endif // DACCESS_COMPILE
609
610 bool IsScratchRegister(int regNum, PREGDISPLAY pRD);
611 bool IsScratchStackSlot(INT32 spOffset, GcStackSlotBase spBase, PREGDISPLAY pRD);
612
613 void ReportUntrackedSlots(
614 GcSlotDecoder& slotDecoder,
615 PREGDISPLAY pRD,
616 unsigned flags,
617 GCEnumCallback pCallBack,
618 void * hCallBack
619 );
620
621 void ReportRegisterToGC(
622 int regNum,
623 unsigned gcFlags,
624 PREGDISPLAY pRD,
625 unsigned flags,
626 GCEnumCallback pCallBack,
627 void * hCallBack
628 );
629
630 void ReportStackSlotToGC(
631 INT32 spOffset,
632 GcStackSlotBase spBase,
633 unsigned gcFlags,
634 PREGDISPLAY pRD,
635 unsigned flags,
636 GCEnumCallback pCallBack,
637 void * hCallBack
638 );
639
640
641 inline void ReportSlotToGC(
642 GcSlotDecoder& slotDecoder,
643 UINT32 slotIndex,
644 PREGDISPLAY pRD,
645 bool reportScratchSlots,
646 unsigned inputFlags,
647 GCEnumCallback pCallBack,
648 void * hCallBack
649 )
650 {
651 _ASSERTE(slotIndex < slotDecoder.GetNumSlots());
652 const GcSlotDesc* pSlot = slotDecoder.GetSlotDesc(slotIndex);
653
654 if(slotIndex < slotDecoder.GetNumRegisters())
655 {
656 UINT32 regNum = pSlot->Slot.RegisterNumber;
657 if( reportScratchSlots || !IsScratchRegister( regNum, pRD ) )
658 {
659 ReportRegisterToGC(
660 regNum,
661 pSlot->Flags,
662 pRD,
663 inputFlags,
664 pCallBack,
665 hCallBack
666 );
667 }
668 else
669 {
670 LOG((LF_GCROOTS, LL_INFO1000, "\"Live\" scratch register " FMT_REG " not reported\n", regNum));
671 }
672 }
673 else
674 {
675 INT32 spOffset = pSlot->Slot.Stack.SpOffset;
676 GcStackSlotBase spBase = pSlot->Slot.Stack.Base;
677 if( reportScratchSlots || !IsScratchStackSlot(spOffset, spBase, pRD) )
678 {
679 ReportStackSlotToGC(
680 spOffset,
681 spBase,
682 pSlot->Flags,
683 pRD,
684 inputFlags,
685 pCallBack,
686 hCallBack
687 );
688 }
689 else
690 {
691 LOG((LF_GCROOTS, LL_INFO1000, "\"Live\" scratch stack slot " FMT_STK " not reported\n", DBG_STK(spOffset)));
692 }
693 }
694 }
695};
696#endif // USE_GC_INFO_DECODER
697
698
699#endif // _GC_INFO_DECODER_
700
701