diff --git a/docs/design/datacontracts/Signature.md b/docs/design/datacontracts/Signature.md new file mode 100644 index 00000000000000..e0eb96ab807a17 --- /dev/null +++ b/docs/design/datacontracts/Signature.md @@ -0,0 +1,71 @@ +# Contract Signature + +This contract describes the format of method, field, and local-variable signatures stored in target memory. Signatures use the ECMA-335 §II.23.2 format with CoreCLR-internal element types added by the runtime. + +## Internal element types + +The runtime extends the standard ECMA-335 element type encoding with values that may appear in signatures stored in target memory: + +| Encoding | Value | Layout following the tag | +| --- | --- | --- | +| `ELEMENT_TYPE_INTERNAL` | `0x21` | a target-sized pointer to a runtime `TypeHandle` | +| `ELEMENT_TYPE_CMOD_INTERNAL` | `0x22` | one byte (`1` = required, `0` = optional), then a target-sized pointer to a runtime `TypeHandle` | + +These tags are used in signatures generated internally by the runtime that are not persisted to a managed image. They are defined alongside the standard ECMA-335 element types in `src/coreclr/inc/corhdr.h`. Their literal values are part of this contract -- changing them is a breaking change. + +## APIs of contract + +```csharp +TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx); +``` + +## Version 1 + +Data descriptors used: +| Data Descriptor Name | Field | Meaning | +| --- | --- | --- | +| _none_ | | | + +Global variables used: +| Global Name | Type | Purpose | +| --- | --- | --- | +| _none_ | | | + +Contracts used: +| Contract Name | +| --- | +| RuntimeTypeSystem | +| Loader | +| EcmaMetadata | + +Constants: +| Constant Name | Meaning | Value | +| --- | --- | --- | +| `ELEMENT_TYPE_INTERNAL` | runtime-internal element type tag for an internal `TypeHandle` | `0x21` | +| `ELEMENT_TYPE_CMOD_INTERNAL` | runtime-internal element type tag for an internal modified type | `0x22` | + +Decoding a signature follows the ECMA-335 §II.23.2 grammar. For all standard element types, decoding behaves identically to `System.Reflection.Metadata.SignatureDecoder`. When the decoder encounters one of the runtime-internal tags above, it reads the target-sized pointer (and optional `required` byte for `ELEMENT_TYPE_CMOD_INTERNAL`) from the signature blob and resolves it to a runtime `TypeHandle`. + +The decoder is implemented as `RuntimeSignatureDecoder` -- a clone of SRM's `SignatureDecoder` with added support for the runtime-internal element types. The clone takes an additional `Target` so internal-type pointers can be sized for the target architecture. Provider implementations implement `IRuntimeSignatureTypeProvider` -- a superset of `System.Reflection.Metadata.ISignatureTypeProvider` -- adding methods for the runtime-internal element types: + +```csharp +TType GetInternalType(TargetPointer typeHandlePointer); +TType GetInternalModifiedType(TargetPointer typeHandlePointer, TType unmodifiedType, bool isRequired); +``` + +The contract's provider resolves these pointers through `RuntimeTypeSystem.GetTypeHandle`. Standard ECMA-335 element types resolve through `RuntimeTypeSystem.GetPrimitiveType` and `RuntimeTypeSystem.GetConstructedType`. Generic type parameters (`VAR`) and generic method parameters (`MVAR`) resolve via `RuntimeTypeSystem.GetInstantiation` and `RuntimeTypeSystem.GetGenericMethodInstantiation` respectively, using a `TypeHandle` (for generic types) or `MethodDescHandle` (for generic methods) generic context. `GetTypeFromDefinition` and `GetTypeFromReference` resolve tokens via the module's `TypeDefToMethodTableMap` / `TypeRefToMethodTableMap`; cross-module references and `GetTypeFromSpecification` are not currently implemented. + +```csharp +TypeHandle ISignature.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx) +{ + SignatureTypeProvider provider = new(_target, moduleHandle); + MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!; + BlobReader blobReader = mdReader.GetBlobReader(blobHandle); + RuntimeSignatureDecoder decoder = new(provider, _target, mdReader, ctx); + return decoder.DecodeFieldSignature(ref blobReader); +} +``` + +### Other consumers + +`RuntimeSignatureDecoder` is shared infrastructure within the cDAC. Other contracts construct their own decoder and provider directly when they need to decode method or local signatures rather than going through this contract. For example, the [StackWalk](./StackWalk.md) contract uses `RuntimeSignatureDecoder` with a GC-specific provider to classify method parameters during signature-based GC reference scanning. diff --git a/docs/design/datacontracts/SignatureDecoder.md b/docs/design/datacontracts/SignatureDecoder.md deleted file mode 100644 index 08f41f49917dd3..00000000000000 --- a/docs/design/datacontracts/SignatureDecoder.md +++ /dev/null @@ -1,70 +0,0 @@ -# Contract SignatureDecoder - -This contract encapsulates signature decoding in the cDAC. - -## APIs of contract - -```csharp -TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx); -``` - -## Version 1 - -In version 1 of the SignatureDecoder contract we take advantage of the System.Reflection.Metadata signature decoding. We implement a SignatureTypeProvider that inherits from System.Reflection.Metadata ISignatureTypeProvider. - -Data descriptors used: -| Data Descriptor Name | Field | Meaning | -| --- | --- | --- | - - -Global variables used: -| Global Name | Type | Purpose | -| --- | --- | --- | - - -Contracts used: -| Contract Name | -| --- | -| RuntimeTypeSystem | -| Loader | -| EcmaMetadata | - -### SignatureTypeProvider -The cDAC implements the ISignatureTypeProvider with TType=TypeHandle. TGenericContext can either be a MethodDescHandle or TypeHandle; MethodDescHandle context is used to look up generic method parameters, and TypeHandle context is used to look up generic type parameters. - -A cDAC SignatureTypeProvider is instantiated over a Module which is used to lookup types. - -The following ISignatureTypeProvider APIs are trivially implemented using RuntimeTypeSystem.GetPrimitiveType and RuntimeTypeSystem.GetConstructedType: - -* GetArrayType - GetConstructedType -* GetByReferenceType - GetConstructedType -* GetFunctionPointerType - Implemented as primitive IntPtr type -* GetGenericInstantiation - GetConstructedType -* GetModifiedType - Returns unmodified type -* GetPinnedType - Returns unpinned type -* GetPointerType - GetConstructedType -* GetPrimitiveType - GetConstructedType -* GetSZArrayType - GetConstructedType - -GetGenericMethodParameter is only supported when TGenericContext=MethodDescHandle and looks up the method parameters from the context using RuntimeTypeSystem.GetGenericMethodInstantiation. - -GetGenericTypeParameter is only supported when TGenericContext=TypeHandle and looks up the type parameters from the context using RuntimeTypeSystem.GetInstantiation. - -GetTypeFromDefinition uses the SignatureTypeProvider's ModuleHandle to lookup the given Token in the Module's TypeDefToMethodTableMap. If a value is not found return null. - -GetTypeFromReference uses the SignatureTypeProvider's ModuleHandle to lookup the given Token in the Module's TypeRefToMethodTableMap. If a value is not found return null.The implementation when the type exists in a different module is incomplete. - -GetTypeFromSpecification is not currently implemented. - - -### APIs -```csharp -TypeHandle ISignatureDecoder.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx) -{ - SignatureTypeProvider provider = new(_target, moduleHandle); - MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!; - BlobReader blobReader = mdReader.GetBlobReader(blobHandle); - SignatureDecoder decoder = new(provider, mdReader, ctx); - return decoder.DecodeFieldSignature(ref blobReader); -} -``` diff --git a/docs/design/datacontracts/StackWalk.md b/docs/design/datacontracts/StackWalk.md index 35c8a33e00c2c7..e8d88ed9957bca 100644 --- a/docs/design/datacontracts/StackWalk.md +++ b/docs/design/datacontracts/StackWalk.md @@ -471,7 +471,64 @@ At each frame yielded by `Filter`, the walk determines whether to scan for GC re - **PrestubMethodFrame / CallCountingHelperFrame**: Use signature-based scanning. - Other frame types: No GC roots to report. -See [GCRefMap Format and Resolution](#gcrefmap-format-and-resolution) for the GCRefMap scanning path details. +See [GCRefMap Format and Resolution](#gcrefmap-format-and-resolution) for the GCRefMap scanning path and [Signature-Based Scanning](#signature-based-scanning) for the signature decoding path. + +### Signature-Based Scanning + +When a transition frame's calling convention is not described by a precomputed GCRefMap (`PrestubMethodFrame`, `CallCountingHelperFrame`, and the fallback path for `StubDispatchFrame`/`ExternalMethodFrame`), the GC reference walk classifies caller-stack arguments by decoding the callee's method signature. This corresponds to native `TransitionFrame::PromoteCallerStack` (`src/coreclr/vm/frames.cpp`). + +#### GcSignatureTypeProvider + +`GcSignatureTypeProvider` is an `IRuntimeSignatureTypeProvider` that classifies each parameter type into one of: + +```csharp +internal enum GcTypeKind +{ + None, // Non-GC primitive that fits in a single slot + Ref, // Object reference (TYPE_GC_REF) + Interior, // Managed pointer / byref (TYPE_GC_BYREF) + Other, // Value type that may contain GC refs, or any type larger than a slot +} +``` + +The provider is scoped to the method's containing module (captured at construction) so that `TypeDef` and `TypeRef` tokens can be resolved to a loaded `MethodTable` via the module's `TypeDefToMethodTable` / `TypeRefToMethodTable` lookup tables. The decoder's generic context is a `GcSignatureContext(TypeHandle classContext, MethodDescHandle methodContext)` carrying the method's class and method instantiations. + +The provider classifies primitives directly (`String`/`Object` -> `Ref`, `TypedReference` -> `Other`, others -> `None`). For `TypeDef`/`TypeRef` it resolves the loaded `TypeHandle` and classifies via `RuntimeTypeSystem.GetSignatureCorElementType`, treating enums (`IsEnum`) as their underlying primitive (`None`). When the type cannot be resolved (e.g., not yet loaded), classification falls back to the signature's `rawTypeKind` (`ValueType` -> `Other`, otherwise `Ref`). Arrays are `Ref`, byrefs are `Interior`, raw pointers are `None`. Generic parameters (`!T`, `!!T`) are resolved against the `GcSignatureContext` (via `GetInstantiation` / `GetGenericMethodInstantiation`) and classified by their actual instantiation -- matching native `SigTypeContext`-driven `PeekElemTypeNormalized` behavior. `ELEMENT_TYPE_INTERNAL` resolves the `TypeHandle` via `RuntimeTypeSystem.GetSignatureCorElementType` and maps the `CorElementType` to a `GcTypeKind`. + +#### PromoteCallerStack Algorithm + +1. Read the `MethodDesc` pointer from the `FramedMethodFrame` and obtain a `MethodDescHandle` from `RuntimeTypeSystem`. +2. Resolve the method's `MetadataReader` via `Loader.GetModuleHandleFromModulePtr` and `EcmaMetadata.GetMetadata`. If metadata is unavailable, no caller-stack refs are reported (matches native fallback behavior). +3. Obtain the method's signature blob, matching native `MethodDesc::GetSig`: + - If `RuntimeTypeSystem.IsStoredSigMethodDesc` is true (dynamic, EEImpl, and array method descs), pin the stored signature span and pass a `BlobReader` over it to `RuntimeSignatureDecoder.DecodeMethodSignature`. + - Otherwise, look up the signature via the metadata token (`mdMethodDef`), skipping methods with a nil token (`0x06000000`). +4. Decode the signature with `RuntimeSignatureDecoder` and a `GcSignatureTypeProvider` constructed for the method's module. The `GcSignatureContext` passes the method's class and method instantiations so that `VAR`/`MVAR` placeholders resolve to their actual types. See [Signature contract](./Signature.md) for the decoder. +5. Skip varargs methods (the caller-stack layout is not described by the callee signature alone). +6. Compute the number of reserved register slots in the `TransitionBlock`: + + | Reserved Slot | Condition | + |---|---| + | `this` pointer | `MethodSignature.Header.IsInstance` | + | Return buffer | Return type is `GcTypeKind.Other` | + | Generic instantiation arg | `RuntimeTypeSystem.RequiresInstArg(methodDesc)` | + | Async continuation | `RuntimeTypeSystem.IsAsyncMethod(methodDesc)` | + | ARM64 indirect-result register (`x8`) | Target architecture is ARM64 | + +7. If `IsInstance`, report the `this` slot at position `0` (or `1` on ARM64 to skip `x8`). The slot is reported as `GC_CALL_INTERIOR` for value-type `this`, otherwise as a normal reference. +8. Walk `MethodSignature.ParameterTypes` starting at slot index = reserved slot count, advancing one slot per parameter: + - `GcTypeKind.Ref` -> report as a reference. + - `GcTypeKind.Interior` -> report with `GC_CALL_INTERIOR`. + - `GcTypeKind.Other` / `GcTypeKind.None` -> not reported (large value types are reported via the GCRefMap path when one is available; otherwise their interior refs are not visible to this scan). + +The slot address is computed using the same formula as the GCRefMap path: + +```csharp +slotAddress = transitionBlockPtr + FirstGCRefMapSlot + (position * pointerSize); +``` + +#### Limitations vs. Native + +This signature-based scan has known gaps relative to native see [dotnet/runtime#127765](https://github.com/dotnet/runtime/issues/127765) for tracking. ### GCRefMap Format and Resolution diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index 466e1e8307fddf..7c6336a6571c5b 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -913,7 +913,7 @@ typedef enum CorElementType ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT // This is for signatures generated internally (which will not be persisted in any way). - // [cDAC] [RuntimeTypeSystem]: Contract depends on the values of ELEMENT_TYPE_INTERNAL and ELEMENT_TYPE_CMOD_INTERNAL. + // [cDAC] [Signature][RuntimeTypeSystem]: Contract depends on the values of ELEMENT_TYPE_INTERNAL and ELEMENT_TYPE_CMOD_INTERNAL. ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL ELEMENT_TYPE_CMOD_INTERNAL = 0x22, // CMOD_INTERNAL diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index ff8738e9860743..bbeafe501f5850 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -1574,7 +1574,7 @@ CDAC_GLOBAL_CONTRACT(ReJIT, c1) CDAC_GLOBAL_CONTRACT(RuntimeInfo, c1) CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, c1) CDAC_GLOBAL_CONTRACT(SHash, c1) -CDAC_GLOBAL_CONTRACT(SignatureDecoder, c1) +CDAC_GLOBAL_CONTRACT(Signature, c1) CDAC_GLOBAL_CONTRACT(StackWalk, c1) CDAC_GLOBAL_CONTRACT(StressLog, c2) CDAC_GLOBAL_CONTRACT(SyncBlock, c1) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs index 9d4088ee27b15c..ae50ec7fae054b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/ContractRegistry.cs @@ -97,9 +97,9 @@ public abstract class ContractRegistry /// public virtual ICodeNotifications CodeNotifications => GetContract(); /// - /// Gets an instance of the SignatureDecoder contract for the target. + /// Gets an instance of the Signature contract for the target. /// - public virtual ISignatureDecoder SignatureDecoder => GetContract(); + public virtual ISignature Signature => GetContract(); /// /// Gets an instance of the SyncBlock contract for the target. /// diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs similarity index 71% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs index 5977d736b74d43..f53847ea4e3b55 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignatureDecoder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ISignature.cs @@ -6,13 +6,13 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; -public interface ISignatureDecoder : IContract +public interface ISignature : IContract { - static string IContract.Name { get; } = nameof(SignatureDecoder); + static string IContract.Name { get; } = nameof(Signature); TypeHandle DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx) => throw new NotImplementedException(); } -public readonly struct SignatureDecoder : ISignatureDecoder +public readonly struct Signature : ISignature { // Everything throws NotImplementedException } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/IRuntimeSignatureTypeProvider.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/IRuntimeSignatureTypeProvider.cs new file mode 100644 index 00000000000000..a8d9b15251dd40 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/IRuntimeSignatureTypeProvider.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection.Metadata; + +namespace Microsoft.Diagnostics.DataContractReader.SignatureHelpers; + +/// +/// Superset of SRM's +/// that adds support for runtime-internal type codes +/// (ELEMENT_TYPE_INTERNAL 0x21 and ELEMENT_TYPE_CMOD_INTERNAL 0x22). +/// +/// +/// Providers implementing this interface automatically satisfy SRM's +/// and can be used +/// with both SRM's SignatureDecoder and our +/// . +/// +public interface IRuntimeSignatureTypeProvider + : ISignatureTypeProvider +{ + /// + /// Classify an ELEMENT_TYPE_INTERNAL (0x21) type by resolving the + /// embedded TypeHandle pointer via the target's runtime type system. + /// + TType GetInternalType(TargetPointer typeHandlePointer); + + /// + /// Classify an ELEMENT_TYPE_CMOD_INTERNAL (0x22) custom modifier by + /// resolving the embedded TypeHandle pointer via the target's runtime type system. + /// + TType GetInternalModifiedType(TargetPointer typeHandlePointer, TType unmodifiedType, bool isRequired); +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.cs new file mode 100644 index 00000000000000..aa1700383b8944 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/RuntimeSignatureDecoder.cs @@ -0,0 +1,326 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Immutable; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +namespace Microsoft.Diagnostics.DataContractReader.SignatureHelpers; + +/// +/// Decodes signature blobs. Behaves identically to SRM's +/// for standard ECMA-335 type codes, +/// with added support for runtime-internal types +/// (ELEMENT_TYPE_INTERNAL 0x21 and ELEMENT_TYPE_CMOD_INTERNAL 0x22). +/// +internal readonly struct RuntimeSignatureDecoder +{ + private const int ELEMENT_TYPE_CMOD_INTERNAL = 0x22; + private const int ELEMENT_TYPE_INTERNAL = 0x21; + + private readonly IRuntimeSignatureTypeProvider _provider; + private readonly MetadataReader _metadataReader; + private readonly TGenericContext _genericContext; + private readonly int _pointerSize; + + public RuntimeSignatureDecoder( + IRuntimeSignatureTypeProvider provider, + Target target, + MetadataReader metadataReader, + TGenericContext genericContext) + { + _provider = provider; + _metadataReader = metadataReader; + _genericContext = genericContext; + _pointerSize = target.PointerSize; + } + + /// + /// Decodes a type embedded in a signature and advances the reader past the type. + /// + public TType DecodeType(ref BlobReader blobReader, bool allowTypeSpecifications = false) + { + return DecodeType(ref blobReader, allowTypeSpecifications, blobReader.ReadCompressedInteger()); + } + + private TType DecodeType(ref BlobReader blobReader, bool allowTypeSpecifications, int typeCode) + { + TType elementType; + int index; + + switch (typeCode) + { + case (int)SignatureTypeCode.Boolean: + case (int)SignatureTypeCode.Char: + case (int)SignatureTypeCode.SByte: + case (int)SignatureTypeCode.Byte: + case (int)SignatureTypeCode.Int16: + case (int)SignatureTypeCode.UInt16: + case (int)SignatureTypeCode.Int32: + case (int)SignatureTypeCode.UInt32: + case (int)SignatureTypeCode.Int64: + case (int)SignatureTypeCode.UInt64: + case (int)SignatureTypeCode.Single: + case (int)SignatureTypeCode.Double: + case (int)SignatureTypeCode.IntPtr: + case (int)SignatureTypeCode.UIntPtr: + case (int)SignatureTypeCode.Object: + case (int)SignatureTypeCode.String: + case (int)SignatureTypeCode.Void: + case (int)SignatureTypeCode.TypedReference: + return _provider.GetPrimitiveType((PrimitiveTypeCode)typeCode); + + case (int)SignatureTypeCode.Pointer: + elementType = DecodeType(ref blobReader); + return _provider.GetPointerType(elementType); + + case (int)SignatureTypeCode.ByReference: + elementType = DecodeType(ref blobReader); + return _provider.GetByReferenceType(elementType); + + case (int)SignatureTypeCode.Pinned: + elementType = DecodeType(ref blobReader); + return _provider.GetPinnedType(elementType); + + case (int)SignatureTypeCode.SZArray: + elementType = DecodeType(ref blobReader); + return _provider.GetSZArrayType(elementType); + + case (int)SignatureTypeCode.FunctionPointer: + MethodSignature methodSignature = DecodeMethodSignature(ref blobReader); + return _provider.GetFunctionPointerType(methodSignature); + + case (int)SignatureTypeCode.Array: + return DecodeArrayType(ref blobReader); + + case (int)SignatureTypeCode.RequiredModifier: + return DecodeModifiedType(ref blobReader, isRequired: true); + + case (int)SignatureTypeCode.OptionalModifier: + return DecodeModifiedType(ref blobReader, isRequired: false); + + case (int)SignatureTypeCode.GenericTypeInstance: + return DecodeGenericTypeInstance(ref blobReader); + + case (int)SignatureTypeCode.GenericTypeParameter: + index = blobReader.ReadCompressedInteger(); + return _provider.GetGenericTypeParameter(_genericContext, index); + + case (int)SignatureTypeCode.GenericMethodParameter: + index = blobReader.ReadCompressedInteger(); + return _provider.GetGenericMethodParameter(_genericContext, index); + + case (int)SignatureTypeKind.Class: + case (int)SignatureTypeKind.ValueType: + return DecodeTypeHandle(ref blobReader, (byte)typeCode, allowTypeSpecifications); + + case ELEMENT_TYPE_INTERNAL: + return DecodeInternalType(ref blobReader); + + case ELEMENT_TYPE_CMOD_INTERNAL: + return DecodeInternalModifiedType(ref blobReader); + + default: + throw new BadImageFormatException($"Unexpected signature type code: 0x{typeCode:X2}"); + } + } + + /// + /// Decodes a list of types, with at least one instance that is preceded by its count as a compressed integer. + /// + private ImmutableArray DecodeTypeSequence(ref BlobReader blobReader) + { + int count = blobReader.ReadCompressedInteger(); + if (count == 0) + { + throw new BadImageFormatException("Signature type sequence must have at least one element"); + } + + var types = ImmutableArray.CreateBuilder(count); + for (int i = 0; i < count; i++) + { + types.Add(DecodeType(ref blobReader)); + } + return types.MoveToImmutable(); + } + + /// + /// Decodes a method (definition, reference, or standalone) or property signature blob. + /// + public MethodSignature DecodeMethodSignature(ref BlobReader blobReader) + { + SignatureHeader header = blobReader.ReadSignatureHeader(); + CheckMethodOrPropertyHeader(header); + + int genericParameterCount = 0; + if (header.IsGeneric) + { + genericParameterCount = blobReader.ReadCompressedInteger(); + } + + int parameterCount = blobReader.ReadCompressedInteger(); + TType returnType = DecodeType(ref blobReader); + ImmutableArray parameterTypes; + int requiredParameterCount; + + if (parameterCount == 0) + { + requiredParameterCount = 0; + parameterTypes = ImmutableArray.Empty; + } + else + { + var parameterBuilder = ImmutableArray.CreateBuilder(parameterCount); + int parameterIndex; + + for (parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) + { + int typeCode = blobReader.ReadCompressedInteger(); + if (typeCode == (int)SignatureTypeCode.Sentinel) + { + break; + } + parameterBuilder.Add(DecodeType(ref blobReader, allowTypeSpecifications: false, typeCode: typeCode)); + } + + requiredParameterCount = parameterIndex; + for (; parameterIndex < parameterCount; parameterIndex++) + { + parameterBuilder.Add(DecodeType(ref blobReader)); + } + parameterTypes = parameterBuilder.MoveToImmutable(); + } + + return new MethodSignature(header, returnType, requiredParameterCount, genericParameterCount, parameterTypes); + } + + /// + /// Decodes a local variable signature blob and advances the reader past the signature. + /// + public ImmutableArray DecodeLocalSignature(ref BlobReader blobReader) + { + SignatureHeader header = blobReader.ReadSignatureHeader(); + CheckHeader(header, SignatureKind.LocalVariables); + return DecodeTypeSequence(ref blobReader); + } + + /// + /// Decodes a field signature blob and advances the reader past the signature. + /// + public TType DecodeFieldSignature(ref BlobReader blobReader) + { + SignatureHeader header = blobReader.ReadSignatureHeader(); + CheckHeader(header, SignatureKind.Field); + return DecodeType(ref blobReader); + } + + private TType DecodeArrayType(ref BlobReader blobReader) + { + TType elementType = DecodeType(ref blobReader); + int rank = blobReader.ReadCompressedInteger(); + var sizes = ImmutableArray.Empty; + var lowerBounds = ImmutableArray.Empty; + + int sizesCount = blobReader.ReadCompressedInteger(); + if (sizesCount > 0) + { + var builder = ImmutableArray.CreateBuilder(sizesCount); + for (int i = 0; i < sizesCount; i++) + { + builder.Add(blobReader.ReadCompressedInteger()); + } + sizes = builder.MoveToImmutable(); + } + + int lowerBoundsCount = blobReader.ReadCompressedInteger(); + if (lowerBoundsCount > 0) + { + var builder = ImmutableArray.CreateBuilder(lowerBoundsCount); + for (int i = 0; i < lowerBoundsCount; i++) + { + builder.Add(blobReader.ReadCompressedSignedInteger()); + } + lowerBounds = builder.MoveToImmutable(); + } + + return _provider.GetArrayType(elementType, new ArrayShape(rank, sizes, lowerBounds)); + } + + private TType DecodeGenericTypeInstance(ref BlobReader blobReader) + { + TType genericType = DecodeType(ref blobReader); + ImmutableArray types = DecodeTypeSequence(ref blobReader); + return _provider.GetGenericInstantiation(genericType, types); + } + + private TType DecodeModifiedType(ref BlobReader blobReader, bool isRequired) + { + // A standard modifier may be followed by an internal modifier; allow type specifications + // for the modifier handle (matches SRM behavior). + TType modifier = DecodeTypeHandle(ref blobReader, 0, allowTypeSpecifications: true); + TType unmodifiedType = DecodeType(ref blobReader); + return _provider.GetModifiedType(modifier, unmodifiedType, isRequired); + } + + private TType DecodeInternalType(ref BlobReader blobReader) + { + ulong val = ReadPointerSized(ref blobReader); + return _provider.GetInternalType(new TargetPointer(val)); + } + + private TType DecodeInternalModifiedType(ref BlobReader blobReader) + { + bool isRequired = blobReader.ReadByte() != 0; + ulong val = ReadPointerSized(ref blobReader); + TType unmodifiedType = DecodeType(ref blobReader); + return _provider.GetInternalModifiedType(new TargetPointer(val), unmodifiedType, isRequired); + } + + private TType DecodeTypeHandle(ref BlobReader blobReader, byte rawTypeKind, bool allowTypeSpecifications) + { + EntityHandle handle = blobReader.ReadTypeHandle(); + if (!handle.IsNil) + { + switch (handle.Kind) + { + case HandleKind.TypeDefinition: + return _provider.GetTypeFromDefinition(_metadataReader, (TypeDefinitionHandle)handle, rawTypeKind); + + case HandleKind.TypeReference: + return _provider.GetTypeFromReference(_metadataReader, (TypeReferenceHandle)handle, rawTypeKind); + + case HandleKind.TypeSpecification: + if (!allowTypeSpecifications) + { + throw new BadImageFormatException("TypeSpecification handle not allowed in this context"); + } + return _provider.GetTypeFromSpecification(_metadataReader, _genericContext, (TypeSpecificationHandle)handle, rawTypeKind); + } + } + + throw new BadImageFormatException("Expected TypeDef, TypeRef, or TypeSpec handle"); + } + + private ulong ReadPointerSized(ref BlobReader blobReader) + { + return _pointerSize == 8 ? blobReader.ReadUInt64() : blobReader.ReadUInt32(); + } + + private static void CheckHeader(SignatureHeader header, SignatureKind expectedKind) + { + if (header.Kind != expectedKind) + { + throw new BadImageFormatException($"Expected signature header {expectedKind}, got {header.Kind} (raw 0x{header.RawValue:X2})"); + } + } + + private static void CheckMethodOrPropertyHeader(SignatureHeader header) + { + SignatureKind kind = header.Kind; + if (kind != SignatureKind.Method && kind != SignatureKind.Property) + { + throw new BadImageFormatException($"Expected Method or Property signature header, got {kind} (raw 0x{header.RawValue:X2})"); + } + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs index 82672504975f49..c6cb2bfb47fbd5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureTypeProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.Diagnostics.DataContractReader.SignatureHelpers; -public class SignatureTypeProvider : ISignatureTypeProvider +public class SignatureTypeProvider : IRuntimeSignatureTypeProvider { private readonly Target _target; private readonly Contracts.ModuleHandle _moduleHandle; @@ -89,4 +89,12 @@ public TypeHandle GetTypeFromReference(MetadataReader reader, TypeReferenceHandl public TypeHandle GetTypeFromSpecification(MetadataReader reader, T context, TypeSpecificationHandle handle, byte rawTypeKind) => throw new NotImplementedException(); + + public TypeHandle GetInternalType(TargetPointer typeHandlePointer) + => typeHandlePointer == TargetPointer.Null + ? new TypeHandle(TargetPointer.Null) + : _runtimeTypeSystem.GetTypeHandle(typeHandlePointer); + + public TypeHandle GetInternalModifiedType(TargetPointer typeHandlePointer, TypeHandle unmodifiedType, bool isRequired) + => unmodifiedType; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs similarity index 50% rename from src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.cs rename to src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs index adfdbeca340134..8517cf674bccdb 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/SignatureDecoder_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Signature/Signature_1.cs @@ -1,29 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; using Microsoft.Diagnostics.DataContractReader.SignatureHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts; -/* NOTE: some elements of SignatureTypeProvider remain unimplemented or minimally implemented - * as they are not needed for the current usage of ISignatureDecoder. - * GetModifiedType and GetPinnedType ignore pinning and custom modifiers. - * GetTypeFromReference does not look up the type in another module. - * GetTypeFromSpecification is unimplemented. - * These can be completed as needed. - */ - -internal sealed class SignatureDecoder_1 : ISignatureDecoder +// NOTE: some elements of SignatureTypeProvider remain unimplemented or minimally implemented +// as they are not needed for the current usage of ISignature. +// GetModifiedType and GetPinnedType ignore pinning and custom modifiers. +// GetTypeFromReference does not look up the type in another module. +// GetTypeFromSpecification is unimplemented. +// These can be completed as needed. +internal sealed class Signature_1 : ISignature { private readonly Target _target; private readonly Dictionary> _thProviders = []; - private readonly Dictionary> _mdhProviders = []; - internal SignatureDecoder_1(Target target) + internal Signature_1(Target target) { _target = target; } @@ -31,7 +26,6 @@ internal SignatureDecoder_1(Target target) public void Flush() { _thProviders.Clear(); - _mdhProviders.Clear(); } private SignatureTypeProvider GetTypeHandleProvider(ModuleHandle moduleHandle) @@ -46,23 +40,13 @@ private SignatureTypeProvider GetTypeHandleProvider(ModuleHandle mod return newProvider; } - private SignatureTypeProvider GetMethodDescHandleProvider(ModuleHandle moduleHandle) - { - if (_mdhProviders.TryGetValue(moduleHandle, out SignatureTypeProvider? mdhProvider)) - { - return mdhProvider; - } - SignatureTypeProvider newProvider = new(_target, moduleHandle); - _mdhProviders[moduleHandle] = newProvider; - return newProvider; - } - - TypeHandle ISignatureDecoder.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx) + TypeHandle ISignature.DecodeFieldSignature(BlobHandle blobHandle, ModuleHandle moduleHandle, TypeHandle ctx) { SignatureTypeProvider provider = GetTypeHandleProvider(moduleHandle); MetadataReader mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!; + BlobReader blobReader = mdReader.GetBlobReader(blobHandle); - SignatureDecoder decoder = new(provider, mdReader, ctx); + RuntimeSignatureDecoder decoder = new(provider, _target, mdReader, ctx); return decoder.DecodeFieldSignature(ref blobReader); } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs index d050adb8617be2..807c66ae8bacaf 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcScanner.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using Microsoft.Diagnostics.DataContractReader.SignatureHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; @@ -323,32 +324,51 @@ private void PromoteCallerStack( if (methodDescPtr == TargetPointer.Null) return; - ReadOnlySpan signature; - try - { - signature = GetMethodSignatureBytes(methodDescPtr); - } - catch (System.Exception) - { - return; - } - - if (signature.IsEmpty) - return; + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + MethodDescHandle mdh = rts.GetMethodDescHandle(methodDescPtr); MethodSignature methodSig; try { - unsafe + TargetPointer methodTablePtr = rts.GetMethodTable(mdh); + TypeHandle typeHandle = rts.GetTypeHandle(methodTablePtr); + TargetPointer modulePtr = rts.GetModule(typeHandle); + + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr); + MetadataReader? mdReader = _target.Contracts.EcmaMetadata.GetMetadata(moduleHandle); + if (mdReader is null) + return; + + GcSignatureTypeProvider provider = new(_target, moduleHandle); + GcSignatureContext genericContext = new(typeHandle, mdh); + RuntimeSignatureDecoder decoder = new( + provider, _target, mdReader, genericContext); + + // Match native MethodDesc::GetSig: prefer stored signature (dynamic, EEImpl, + // and array method descs) before falling back to a metadata token lookup. + if (rts.IsStoredSigMethodDesc(mdh, out ReadOnlySpan storedSig)) { - fixed (byte* pSig = signature) + unsafe { - BlobReader blobReader = new(pSig, signature.Length); - SignatureDecoder decoder = new( - GcSignatureTypeProvider.Instance, metadataReader: null!, genericContext: null); - methodSig = decoder.DecodeMethodSignature(ref blobReader); + fixed (byte* pStoredSig = storedSig) + { + BlobReader blobReader = new BlobReader(pStoredSig, storedSig.Length); + methodSig = decoder.DecodeMethodSignature(ref blobReader); + } } } + else + { + uint methodToken = rts.GetMethodToken(mdh); + if (methodToken == (uint)EcmaMetadataUtils.TokenType.mdtMethodDef) + return; + + MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)EcmaMetadataUtils.GetRowId(methodToken)); + MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); + + BlobReader blobReader = mdReader.GetBlobReader(methodDef.Signature); + methodSig = decoder.DecodeMethodSignature(ref blobReader); + } } catch (System.Exception) { @@ -358,9 +378,6 @@ private void PromoteCallerStack( if (methodSig.Header.CallingConvention is SignatureCallingConvention.VarArgs) return; - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle mdh = rts.GetMethodDescHandle(methodDescPtr); - bool hasThis = methodSig.Header.IsInstance; bool hasRetBuf = methodSig.ReturnType is GcTypeKind.Other; bool requiresInstArg = false; @@ -440,36 +457,6 @@ private void PromoteCallerStackHelper( } } - private ReadOnlySpan GetMethodSignatureBytes(TargetPointer methodDescPtr) - { - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; - MethodDescHandle mdh = rts.GetMethodDescHandle(methodDescPtr); - - if (rts.IsStoredSigMethodDesc(mdh, out ReadOnlySpan storedSig)) - return storedSig; - - uint methodToken = rts.GetMethodToken(mdh); - if (methodToken == 0x06000000) - return default; - - TargetPointer methodTablePtr = rts.GetMethodTable(mdh); - TypeHandle typeHandle = rts.GetTypeHandle(methodTablePtr); - TargetPointer modulePtr = rts.GetModule(typeHandle); - - ILoader loader = _target.Contracts.Loader; - ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); - - IEcmaMetadata ecmaMetadata = _target.Contracts.EcmaMetadata; - MetadataReader? mdReader = ecmaMetadata.GetMetadata(moduleHandle); - if (mdReader is null) - return default; - - MethodDefinitionHandle methodDefHandle = MetadataTokens.MethodDefinitionHandle((int)(methodToken & 0x00FFFFFF)); - MethodDefinition methodDef = mdReader.GetMethodDefinition(methodDefHandle); - BlobReader blobReader = mdReader.GetBlobReader(methodDef.Signature); - return blobReader.ReadBytes(blobReader.Length); - } - private TargetPointer AddressFromGCRefMapPos(Data.TransitionBlock tb, int pos) { return new TargetPointer(tb.FirstGCRefMapSlot.Value + (ulong)(pos * _target.PointerSize)); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcSignatureTypeProvider.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcSignatureTypeProvider.cs index 46e6c8af2de24c..8852f733df6a97 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcSignatureTypeProvider.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/GC/GcSignatureTypeProvider.cs @@ -1,8 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Immutable; +using System.Diagnostics; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using Microsoft.Diagnostics.DataContractReader.SignatureHelpers; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; @@ -21,15 +25,38 @@ internal enum GcTypeKind Other, } +/// +/// Generic context used to resolve ELEMENT_TYPE_VAR and ELEMENT_TYPE_MVAR +/// while decoding a method signature for GC scanning. is the +/// owning type's (used for VAR), and +/// is the owning method's (used for MVAR). +/// +internal readonly record struct GcSignatureContext(TypeHandle ClassContext, MethodDescHandle MethodContext); + /// /// Classifies signature types for GC scanning purposes. -/// Implements for use -/// with SRM's . +/// Implements which +/// is a superset of SRM's , +/// adding support for ELEMENT_TYPE_INTERNAL. /// +/// +/// The provider is scoped to a single module: GetTypeFromDefinition and +/// GetTypeFromReference resolve TypeDef/TypeRef tokens via the module's +/// lookup tables so enums (and other runtime-normalized value types) are classified +/// using the actual , matching native +/// SigPointer::PeekElemTypeNormalized. +/// internal sealed class GcSignatureTypeProvider - : ISignatureTypeProvider + : IRuntimeSignatureTypeProvider { - public static readonly GcSignatureTypeProvider Instance = new(); + private readonly Target _target; + private readonly ModuleHandle _moduleHandle; + + public GcSignatureTypeProvider(Target target, ModuleHandle moduleHandle) + { + _target = target; + _moduleHandle = moduleHandle; + } public GcTypeKind GetPrimitiveType(PrimitiveTypeCode typeCode) => typeCode switch @@ -40,12 +67,12 @@ public GcTypeKind GetPrimitiveType(PrimitiveTypeCode typeCode) }; public GcTypeKind GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) - => rawTypeKind == (byte)SignatureTypeKind.ValueType ? GcTypeKind.Other : GcTypeKind.Ref; + => ClassifyTokenLookup(_target.Contracts.Loader.GetLookupTables(_moduleHandle).TypeDefToMethodTable, MetadataTokens.GetToken(handle), rawTypeKind); public GcTypeKind GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) - => rawTypeKind == (byte)SignatureTypeKind.ValueType ? GcTypeKind.Other : GcTypeKind.Ref; + => ClassifyTokenLookup(_target.Contracts.Loader.GetLookupTables(_moduleHandle).TypeRefToMethodTable, MetadataTokens.GetToken(handle), rawTypeKind); - public GcTypeKind GetTypeFromSpecification(MetadataReader reader, object? genericContext, TypeSpecificationHandle handle, byte rawTypeKind) + public GcTypeKind GetTypeFromSpecification(MetadataReader reader, GcSignatureContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind) => rawTypeKind == (byte)SignatureTypeKind.ValueType ? GcTypeKind.Other : GcTypeKind.Ref; public GcTypeKind GetSZArrayType(GcTypeKind elementType) => GcTypeKind.Ref; @@ -56,9 +83,137 @@ public GcTypeKind GetTypeFromSpecification(MetadataReader reader, object? generi public GcTypeKind GetGenericInstantiation(GcTypeKind genericType, ImmutableArray typeArguments) => genericType; - public GcTypeKind GetGenericMethodParameter(object? genericContext, int index) => GcTypeKind.Ref; - public GcTypeKind GetGenericTypeParameter(object? genericContext, int index) => GcTypeKind.Ref; + public GcTypeKind GetGenericMethodParameter(GcSignatureContext genericContext, int index) + { + try + { + ReadOnlySpan instantiation = _target.Contracts.RuntimeTypeSystem.GetGenericMethodInstantiation(genericContext.MethodContext); + if ((uint)index >= (uint)instantiation.Length) + return GcTypeKind.Ref; + return ClassifyTypeHandle(instantiation[index]); + } + catch + { + return GcTypeKind.Ref; + } + } + + public GcTypeKind GetGenericTypeParameter(GcSignatureContext genericContext, int index) + { + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TypeHandle classCtx = genericContext.ClassContext; + + if (rts.IsArray(classCtx, out _)) + { + // Match native SigTypeContext::InitTypeContext (typectxt.cpp): arrays use + // the element type as their class instantiation. RuntimeTypeSystem.GetInstantiation + // returns an empty span for arrays, so consult GetTypeParam directly (the + // managed equivalent of MethodTable::GetArrayInstantiation). + Debug.Assert(index == 0, "Array class context has a 1-element instantiation; index > 0 indicates a malformed signature."); + if (index != 0) + return GcTypeKind.Ref; + return ClassifyTypeHandle(rts.GetTypeParam(classCtx)); + } + + ReadOnlySpan instantiation = rts.GetInstantiation(classCtx); + if ((uint)index >= (uint)instantiation.Length) + return GcTypeKind.Ref; + return ClassifyTypeHandle(instantiation[index]); + } + catch + { + return GcTypeKind.Ref; + } + } + public GcTypeKind GetFunctionPointerType(MethodSignature signature) => GcTypeKind.None; public GcTypeKind GetModifiedType(GcTypeKind modifier, GcTypeKind unmodifiedType, bool isRequired) => unmodifiedType; + public GcTypeKind GetInternalModifiedType(TargetPointer typeHandlePointer, GcTypeKind unmodifiedType, bool isRequired) => unmodifiedType; public GcTypeKind GetPinnedType(GcTypeKind elementType) => elementType; + + public GcTypeKind GetInternalType(TargetPointer typeHandlePointer) + { + if (typeHandlePointer == TargetPointer.Null) + return GcTypeKind.None; + + try + { + return ClassifyTypeHandle(_target.Contracts.RuntimeTypeSystem.GetTypeHandle(typeHandlePointer)); + } + catch + { + return GcTypeKind.Ref; + } + } + + /// + /// Resolve a TypeDef/TypeRef token via the module's lookup tables and classify the + /// resulting . Falls back to a -based + /// classification when the type has not been loaded. + /// + private GcTypeKind ClassifyTokenLookup(TargetPointer lookupTable, int token, byte rawTypeKind) + { + try + { + TargetPointer typeHandlePtr = _target.Contracts.Loader.GetModuleLookupMapElement(lookupTable, (uint)token, out _); + if (typeHandlePtr == TargetPointer.Null) + return rawTypeKind == (byte)SignatureTypeKind.ValueType ? GcTypeKind.Other : GcTypeKind.Ref; + + return ClassifyTypeHandle(_target.Contracts.RuntimeTypeSystem.GetTypeHandle(typeHandlePtr)); + } + catch + { + return rawTypeKind == (byte)SignatureTypeKind.ValueType ? GcTypeKind.Other : GcTypeKind.Ref; + } + } + + /// + /// Classify a resolved . Mirrors native + /// SigPointer::PeekElemTypeNormalized + gElementTypeInfo[etype].m_gc: + /// enums collapse to their underlying primitive () so + /// they are skipped during stack scanning, matching native behavior. + /// + private GcTypeKind ClassifyTypeHandle(TypeHandle typeHandle) + { + if (typeHandle.Address == TargetPointer.Null) + return GcTypeKind.Ref; + + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + CorElementType corType = rts.GetSignatureCorElementType(typeHandle); + + switch (corType) + { + case CorElementType.Void: + case CorElementType.Boolean: + case CorElementType.Char: + case CorElementType.I1: + case CorElementType.U1: + case CorElementType.I2: + case CorElementType.U2: + case CorElementType.I4: + case CorElementType.U4: + case CorElementType.I8: + case CorElementType.U8: + case CorElementType.R4: + case CorElementType.R8: + case CorElementType.I: + case CorElementType.U: + case CorElementType.FnPtr: + case CorElementType.Ptr: + return GcTypeKind.None; + + case CorElementType.Byref: + return GcTypeKind.Interior; + + case CorElementType.ValueType: + // Native PeekElemTypeNormalized resolves enums to their underlying primitive + // CorElementType, which classifies as TYPE_GC_NONE in gElementTypeInfo. + return rts.IsEnum(typeHandle) ? GcTypeKind.None : GcTypeKind.Other; + + default: + return GcTypeKind.Ref; + } + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/CoreCLRContracts.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/CoreCLRContracts.cs index f84ae929637aaa..7ddc39936340d2 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/CoreCLRContracts.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/CoreCLRContracts.cs @@ -25,7 +25,7 @@ public static void Register(ContractRegistry registry) registry.Register("c1", static t => new SHash_1(t)); registry.Register("c1", static t => new Notifications_1(t)); registry.Register("c1", static t => new CodeNotifications_1(t)); - registry.Register("c1", static t => new SignatureDecoder_1(t)); + registry.Register("c1", static t => new Signature_1(t)); registry.Register("c1", static t => new BuiltInCOM_1(t)); registry.Register("c1", static t => new ConditionalWeakTable_1(t)); registry.Register("c1", static t => new AuxiliarySymbols_1(t)); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 8a909f25f5ebc7..116d74eb604e61 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -1071,7 +1071,7 @@ int ISOSDacInterface.GetFieldDescData(ClrDataAddress fieldDesc, DacpFieldDescDat IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; IEcmaMetadata ecmaMetadataContract = _target.Contracts.EcmaMetadata; - ISignatureDecoder signatureDecoder = _target.Contracts.SignatureDecoder; + ISignature signatureContract = _target.Contracts.Signature; TargetPointer fieldDescTargetPtr = fieldDesc.ToTargetPointer(_target); CorElementType fieldDescType = rtsContract.GetFieldDescType(fieldDescTargetPtr); @@ -1090,7 +1090,7 @@ int ISOSDacInterface.GetFieldDescData(ClrDataAddress fieldDesc, DacpFieldDescDat try { // try to completely decode the signature - TypeHandle foundTypeHandle = signatureDecoder.DecodeFieldSignature(fieldDef.Signature, moduleHandle, ctx); + TypeHandle foundTypeHandle = signatureContract.DecodeFieldSignature(fieldDef.Signature, moduleHandle, ctx); // get the MT of the type // This is an implementation detail of the DAC that we replicate here to get method tables for non-MT types