diff --git a/src/coreclr/gcdump/gcdumpnonx86.cpp b/src/coreclr/gcdump/gcdumpnonx86.cpp index d4366069f14a7f..4568477e3a6064 100644 --- a/src/coreclr/gcdump/gcdumpnonx86.cpp +++ b/src/coreclr/gcdump/gcdumpnonx86.cpp @@ -361,6 +361,7 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, | DECODE_GENERICS_INST_CONTEXT | DECODE_GC_LIFETIMES | DECODE_PROLOG_LENGTH + | DECODE_RETURN_KIND #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) | DECODE_HAS_TAILCALLS #endif @@ -501,6 +502,12 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea()); #endif + if (hdrdecoder.Version() < 4) + { + ReturnKind returnKind = hdrdecoder.GetReturnKind(); + gcPrintf("Return Kind: %s\n", ReturnKindToString(returnKind)); + } + UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength(); gcPrintf("Code size: %x\n", cbEncodedMethodSize); diff --git a/src/coreclr/inc/gcinfo.h b/src/coreclr/inc/gcinfo.h index 80ff267d583e0e..cdcbb9eb14f04e 100644 --- a/src/coreclr/inc/gcinfo.h +++ b/src/coreclr/inc/gcinfo.h @@ -4,6 +4,7 @@ // ****************************************************************************** // WARNING!!!: These values are used by SOS in the diagnostics repo. Values should // added or removed in a backwards and forwards compatible way. +// There are scenarios in diagnostics that support parsing of old GC Info formats. // See: https://github.com/dotnet/diagnostics/blob/main/src/shared/inc/gcinfo.h // ****************************************************************************** @@ -38,6 +39,17 @@ const unsigned this_OFFSET_FLAG = 0x2; // the offset is "this" #define GCINFO_VERSION 4 +#ifdef SOS_INCLUDE +extern bool IsRuntimeVersionAtLeast(DWORD major); +inline int GCInfoVersion() +{ + // In SOS we only care about ability to parse/dump the GC Info. + // Since v2 and v3 had the same file format and v1 is no longer supported, + // we can assume that everything before net10.0 uses GCInfo v3. + return IsRuntimeVersionAtLeast(10) ? 4 : 3; +} +#endif + //----------------------------------------------------------------------------- // GCInfoToken: A wrapper that contains the GcInfo data and version number. // @@ -67,7 +79,11 @@ struct GCInfoToken static uint32_t ReadyToRunVersionToGcInfoVersion(uint32_t readyToRunMajorVersion, uint32_t readyToRunMinorVersion) { +#ifdef SOS_INCLUDE + return GCInfoVersion(); +#else return GCINFO_VERSION; +#endif } }; diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index 2e3b8e7b6b905c..450e1fbf2f9ca8 100644 --- a/src/coreclr/inc/gcinfodecoder.h +++ b/src/coreclr/inc/gcinfodecoder.h @@ -16,6 +16,10 @@ #ifndef _GC_INFO_DECODER_ #define _GC_INFO_DECODER_ +#ifdef SOS_INCLUDE +#define DECODE_OLD_FORMATS +#endif + #define _max(a, b) (((a) > (b)) ? (a) : (b)) #define _min(a, b) (((a) < (b)) ? (a) : (b)) @@ -222,6 +226,7 @@ enum GcInfoDecoderFlags DECODE_PROLOG_LENGTH = 0x400, // length of the prolog (used to avoid reporting generics context) DECODE_EDIT_AND_CONTINUE = 0x800, DECODE_REVERSE_PINVOKE_VAR = 0x1000, + DECODE_RETURN_KIND = 0x2000, // Unused starting with v4 format #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) DECODE_HAS_TAILCALLS = 0x4000, #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 @@ -247,7 +252,6 @@ enum GcInfoHeaderFlags GC_INFO_HAS_EDIT_AND_CONTINUE_INFO = 0x100, GC_INFO_REVERSE_PINVOKE_FRAME = 0x200, - GC_INFO_FLAGS_BIT_SIZE_VERSION_1 = 9, GC_INFO_FLAGS_BIT_SIZE = 10, }; @@ -584,6 +588,7 @@ class TGcInfoDecoder #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool HasTailCalls(); #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || defined(TARGET_RISCV64) + ReturnKind GetReturnKind(); UINT32 GetCodeLength(); UINT32 GetStackBaseRegister(); UINT32 GetSizeOfEditAndContinuePreservedArea(); @@ -596,6 +601,10 @@ class TGcInfoDecoder UINT32 GetSizeOfStackParameterArea(); #endif // FIXED_STACK_PARAMETER_SCRATCH_AREA + inline UINT32 Version() + { + return m_Version; + } private: BitStreamReader m_Reader; @@ -616,6 +625,8 @@ class TGcInfoDecoder #ifdef TARGET_ARM64 UINT32 m_SizeOfEditAndContinueFixedStackFrame; #endif + // Unused starting with v4 format + ReturnKind m_ReturnKind; #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED UINT32 m_NumSafePoints; UINT32 m_SafePointIndex; @@ -634,6 +645,24 @@ class TGcInfoDecoder #endif UINT32 m_Version; + inline UINT32 NormalizeCodeOffset(UINT32 offset) + { +#ifdef DECODE_OLD_FORMATS + if (Version() < 4) + return offset; +#endif + return GcInfoEncoding::NORMALIZE_CODE_OFFSET(offset); + } + + inline UINT32 DenormalizeCodeOffset(UINT32 offset) + { +#ifdef DECODE_OLD_FORMATS + if (Version() < 4) + return offset; +#endif + return GcInfoEncoding::DENORMALIZE_CODE_OFFSET(offset); + } + bool PredecodeFatHeader(int remainingFlags); static bool SetIsInterruptibleCB (UINT32 startOffset, UINT32 stopOffset, void * hCallback); diff --git a/src/coreclr/inc/gcinfotypes.h b/src/coreclr/inc/gcinfotypes.h index 36164759e54429..5756902f8fa4ec 100644 --- a/src/coreclr/inc/gcinfotypes.h +++ b/src/coreclr/inc/gcinfotypes.h @@ -6,7 +6,7 @@ #define __GCINFOTYPES_H__ // HACK: debugreturn.h breaks constexpr -#ifdef debug_instrumented_return +#if defined(debug_instrumented_return) || defined(_DEBUGRETURN_H_) #undef return #endif // debug_instrumented_return @@ -643,6 +643,8 @@ struct AMD64GcInfoEncoding { static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; static const int GS_COOKIE_STACK_SLOT_ENCBASE = 6; static const int CODE_LENGTH_ENCBASE = 8; + static const int SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2; + static const int SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 4; static const int STACK_BASE_REGISTER_ENCBASE = 3; static const int SIZE_OF_STACK_AREA_ENCBASE = 3; static const int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 4; @@ -698,6 +700,8 @@ struct ARM32GcInfoEncoding { static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 5; static const int GS_COOKIE_STACK_SLOT_ENCBASE = 5; static const int CODE_LENGTH_ENCBASE = 7; + static const int SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2; + static const int SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 2; static const int STACK_BASE_REGISTER_ENCBASE = 1; static const int SIZE_OF_STACK_AREA_ENCBASE = 3; static const int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3; @@ -754,6 +758,8 @@ struct ARM64GcInfoEncoding { static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; static const int GS_COOKIE_STACK_SLOT_ENCBASE = 6; static const int CODE_LENGTH_ENCBASE = 8; + static const int SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2; + static const int SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 4; // FP encoded as 0, SP as 2. static const int STACK_BASE_REGISTER_ENCBASE = 2; static const int SIZE_OF_STACK_AREA_ENCBASE = 3; @@ -811,6 +817,8 @@ struct LoongArch64GcInfoEncoding { static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; static const int GS_COOKIE_STACK_SLOT_ENCBASE = 6; static const int CODE_LENGTH_ENCBASE = 8; + static const int SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2; + static const int SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 4; // FP/SP encoded as 0 or 1. static const int STACK_BASE_REGISTER_ENCBASE = 2; static const int SIZE_OF_STACK_AREA_ENCBASE = 3; @@ -867,6 +875,8 @@ struct RISCV64GcInfoEncoding { static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; static const int GS_COOKIE_STACK_SLOT_ENCBASE = 6; static const int CODE_LENGTH_ENCBASE = 8; + static const int SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2; + static const int SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 4; static const int STACK_BASE_REGISTER_ENCBASE = 2; // FP encoded as 0, SP as 1 static const int SIZE_OF_STACK_AREA_ENCBASE = 3; @@ -927,6 +937,8 @@ struct X86GcInfoEncoding { static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; static const int GS_COOKIE_STACK_SLOT_ENCBASE = 6; static const int CODE_LENGTH_ENCBASE = 6; + static const int SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2; + static const int SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 2; static const int STACK_BASE_REGISTER_ENCBASE = 3; static const int SIZE_OF_STACK_AREA_ENCBASE = 6; static const int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3; diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index bccb5313b6a0d9..7dff38b9461a2c 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -88,10 +88,15 @@ template bool TGcInfoDecoder::SetIsInt // returns true if we decoded all that was asked; template bool TGcInfoDecoder::PredecodeFatHeader(int remainingFlags) { - int numFlagBits = (m_Version == 1) ? GC_INFO_FLAGS_BIT_SIZE_VERSION_1 : GC_INFO_FLAGS_BIT_SIZE; - m_headerFlags = (GcInfoHeaderFlags)m_Reader.Read(numFlagBits); + m_headerFlags = (GcInfoHeaderFlags)m_Reader.Read(GC_INFO_FLAGS_BIT_SIZE); - remainingFlags &= ~DECODE_VARARG; +#ifdef DECODE_OLD_FORMATS + if (Version() < 4) + { + m_ReturnKind = (ReturnKind)((UINT32)m_Reader.Read(GcInfoEncoding::SIZE_OF_RETURN_KIND_IN_FAT_HEADER)); + } +#endif + remainingFlags &= ~(DECODE_RETURN_KIND | DECODE_VARARG); #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) remainingFlags &= ~DECODE_HAS_TAILCALLS; #endif @@ -113,21 +118,21 @@ template bool TGcInfoDecoder::Predecod { // Note that normalization as a code offset can be different than // normalization as code length - UINT32 normCodeLength = GcInfoEncoding::NORMALIZE_CODE_OFFSET(m_CodeLength); + UINT32 normCodeLength = NormalizeCodeOffset(m_CodeLength); // Decode prolog/epilog information UINT32 normPrologSize = (UINT32)m_Reader.DecodeVarLengthUnsigned(GcInfoEncoding::NORM_PROLOG_SIZE_ENCBASE) + 1; UINT32 normEpilogSize = (UINT32)m_Reader.DecodeVarLengthUnsigned(GcInfoEncoding::NORM_EPILOG_SIZE_ENCBASE); - m_ValidRangeStart = GcInfoEncoding::DENORMALIZE_CODE_OFFSET(normPrologSize); - m_ValidRangeEnd = GcInfoEncoding::DENORMALIZE_CODE_OFFSET(normCodeLength - normEpilogSize); + m_ValidRangeStart = DenormalizeCodeOffset(normPrologSize); + m_ValidRangeEnd = DenormalizeCodeOffset(normCodeLength - normEpilogSize); _ASSERTE(m_ValidRangeStart < m_ValidRangeEnd); } else if ((m_headerFlags & GC_INFO_HAS_GENERICS_INST_CONTEXT_MASK) != GC_INFO_HAS_GENERICS_INST_CONTEXT_NONE) { // Decode prolog information UINT32 normPrologSize = (UINT32)m_Reader.DecodeVarLengthUnsigned(GcInfoEncoding::NORM_PROLOG_SIZE_ENCBASE) + 1; - m_ValidRangeStart = GcInfoEncoding::DENORMALIZE_CODE_OFFSET(normPrologSize); + m_ValidRangeStart = DenormalizeCodeOffset(normPrologSize); // satisfy asserts that assume m_GSCookieValidRangeStart != 0 ==> m_GSCookieValidRangeStart < m_GSCookieValidRangeEnd m_ValidRangeEnd = m_ValidRangeStart + 1; } @@ -258,6 +263,7 @@ TGcInfoDecoder::TGcInfoDecoder( : m_Reader(dac_cast(gcInfoToken.Info)) , m_InstructionOffset(breakOffset) , m_IsInterruptible(false) + , m_ReturnKind(RT_Illegal) #ifdef _DEBUG , m_Flags( flags ) , m_GcInfoAddress(dac_cast(gcInfoToken.Info)) @@ -297,7 +303,13 @@ TGcInfoDecoder::TGcInfoDecoder( m_StackBaseRegister = NO_STACK_BASE_REGISTER; } - remainingFlags &= ~DECODE_VARARG; +#ifdef DECODE_OLD_FORMATS + if (Version() < 4) + { + m_ReturnKind = (ReturnKind)((UINT32)m_Reader.Read(GcInfoEncoding::SIZE_OF_RETURN_KIND_IN_SLIM_HEADER)); + } +#endif + remainingFlags &= ~(DECODE_RETURN_KIND | DECODE_VARARG); #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) remainingFlags &= ~DECODE_HAS_TAILCALLS; #endif @@ -365,14 +377,25 @@ TGcInfoDecoder::TGcInfoDecoder( { if(m_NumSafePoints) { +#ifdef DECODE_OLD_FORMATS + if (Version() < 4) + { + // Safepoints are encoded with a -1 adjustment + // DECODE_GC_LIFETIMES adjusts the offset accordingly, but DECODE_INTERRUPTIBILITY does not + // adjust here + UINT32 offset = flags & DECODE_INTERRUPTIBILITY ? m_InstructionOffset - 1 : m_InstructionOffset; + m_SafePointIndex = FindSafePoint(offset); + } +#else m_SafePointIndex = FindSafePoint(m_InstructionOffset); +#endif } } else if(flags & DECODE_FOR_RANGES_CALLBACK) { // Note that normalization as a code offset can be different than // normalization as code length - UINT32 normCodeLength = GcInfoEncoding::NORMALIZE_CODE_OFFSET(m_CodeLength); + UINT32 normCodeLength = NormalizeCodeOffset(m_CodeLength); UINT32 numBitsPerOffset = CeilOfLog2(normCodeLength); m_Reader.Skip(m_NumSafePoints * numBitsPerOffset); @@ -441,6 +464,14 @@ template bool TGcInfoDecoder::IsSafePo if(m_NumSafePoints == 0) return false; +#ifdef DECODE_OLD_FORMATS + if (Version() < 4) + { + // Safepoints are encoded with a -1 adjustment, adjust before searching. + codeOffset--; + } +#endif + size_t savedPos = m_Reader.GetCurrentPos(); UINT32 safePointIndex = FindSafePoint(codeOffset); m_Reader.SetCurrentPos(savedPos); @@ -462,7 +493,7 @@ UINT32 TGcInfoDecoder::NarrowSafePointSearch(size_t savedPos, UI INT32 low = 0; INT32 high = (INT32)m_NumSafePoints; - const UINT32 numBitsPerOffset = CeilOfLog2(GcInfoEncoding::NORMALIZE_CODE_OFFSET(m_CodeLength)); + const UINT32 numBitsPerOffset = CeilOfLog2(NormalizeCodeOffset(m_CodeLength)); while (high - low > MAX_LINEAR_SEARCH) { const INT32 mid = (low + high) / 2; @@ -486,9 +517,9 @@ template UINT32 TGcInfoDecoder::FindSa _ASSERTE(m_NumSafePoints > 0); UINT32 result = m_NumSafePoints; const size_t savedPos = m_Reader.GetCurrentPos(); - const UINT32 numBitsPerOffset = CeilOfLog2(GcInfoEncoding::NORMALIZE_CODE_OFFSET(m_CodeLength)); + const UINT32 numBitsPerOffset = CeilOfLog2(NormalizeCodeOffset(m_CodeLength)); - const UINT32 normBreakOffset = GcInfoEncoding::NORMALIZE_CODE_OFFSET(breakOffset); + const UINT32 normBreakOffset = NormalizeCodeOffset(breakOffset); UINT32 linearSearchStart = 0; UINT32 linearSearchEnd = m_NumSafePoints; if (linearSearchEnd - linearSearchStart > MAX_LINEAR_SEARCH) @@ -523,12 +554,21 @@ template void TGcInfoDecoder::Enumerat if(m_NumSafePoints == 0) return; - const UINT32 numBitsPerOffset = CeilOfLog2(GcInfoEncoding::NORMALIZE_CODE_OFFSET(m_CodeLength)); + const UINT32 numBitsPerOffset = CeilOfLog2(NormalizeCodeOffset(m_CodeLength)); for(UINT32 i = 0; i < m_NumSafePoints; i++) { UINT32 normOffset = (UINT32)m_Reader.Read(numBitsPerOffset); - UINT32 offset = GcInfoEncoding::DENORMALIZE_CODE_OFFSET(normOffset); + UINT32 offset = DenormalizeCodeOffset(normOffset); + +#ifdef DECODE_OLD_FORMATS + if (Version() < 4) + { + // Safepoints are encoded with a -1 adjustment, adjust before reporting + offset++; + } +#endif + pCallback(this, offset, hCallback); } } @@ -551,8 +591,8 @@ template void TGcInfoDecoder::Enumerat UINT32 rangeStartOffsetNormalized = lastInterruptibleRangeStopOffsetNormalized + normStartDelta; UINT32 rangeStopOffsetNormalized = rangeStartOffsetNormalized + normStopDelta; - UINT32 rangeStartOffset = GcInfoEncoding::DENORMALIZE_CODE_OFFSET(rangeStartOffsetNormalized); - UINT32 rangeStopOffset = GcInfoEncoding::DENORMALIZE_CODE_OFFSET(rangeStopOffsetNormalized); + UINT32 rangeStartOffset = DenormalizeCodeOffset(rangeStartOffsetNormalized); + UINT32 rangeStopOffset = DenormalizeCodeOffset(rangeStopOffsetNormalized); bool fStop = pCallback(rangeStartOffset, rangeStopOffset, hCallback); if (fStop) @@ -635,6 +675,13 @@ template UINT32 TGcInfoDecoder::GetCod return m_CodeLength; } +template ReturnKind TGcInfoDecoder::GetReturnKind() +{ + // SUPPORTS_DAC; + _ASSERTE(m_Flags & DECODE_RETURN_KIND); + return m_ReturnKind; +} + template UINT32 TGcInfoDecoder::GetStackBaseRegister() { return m_StackBaseRegister; @@ -698,7 +745,7 @@ template bool TGcInfoDecoder::Enumerat GcSlotDecoder slotDecoder; - UINT32 normBreakOffset = GcInfoEncoding::NORMALIZE_CODE_OFFSET(m_InstructionOffset); + UINT32 normBreakOffset = NormalizeCodeOffset(m_InstructionOffset); // Normalized break offset // Relative to interruptible ranges #if PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED