1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 09:34:49 +09:00

cDac work necessary to get the MethodTableName (#104759)

Add new details to the RuntimeTypeSystem and Loader contracts as needed to load metadata and examine and identify all of the kinds of type that the CoreCLR type system can represent

Add a type name generator based on a line by line copy from the CoreCLR codebase

Add an Ecma335 metadata parser which is pure safe managed code, and is capable of loading metadata that is not structured as a single array. This implementation is heavily based on the dnmd implementation. It is intended that this implementation is short-lived, and really ought to be replaced by dnmd or System.Reflection.Metadata when those apis become suitable for use in this scenario.

Provide implementations and documentation for all of the new contracts except for the RW metadata one. (Its rarely needed, so we can add it later)

Enhance the target infrastructure to better handle various forms of arrays, and contracts which do math based on target pointer sizes.

Contributes to https://github.com/dotnet/runtime/issues/99302
This commit is contained in:
David Wrighton 2024-07-17 13:19:36 -07:00 committed by GitHub
parent 57f870f909
commit 22630598e4
Signed by: github
GPG key ID: B5690EEEBB952194
44 changed files with 3977 additions and 178 deletions

View file

@ -0,0 +1,66 @@
# Contract DacStreams
This contract is for getting information from the streams embedded into a dump file as it crashes
## APIs of contract
``` csharp
// Return string corresponding to type system data structure if it exists, or null otherwise
string StringFromEEAddress(TargetPointer address);
```
## Version 1
Global variables used
| Global Name | Type | Purpose |
| --- | --- | --- |
| MiniMetaDataBuffAddress | TargetPointer | Identify where the mini metadata stream exists |
| MiniMetaDataBuffMaxSize | uint | Identify where the size of the mini metadata stream |
Magic numbers
| Name | Value |
| --- | --- |
| MiniMetadataSignature | 0x6d727473 |
| EENameStreamSignature | 0x614e4545 |
The format of the MiniMetadataStream begins with a Streams header, which has 3 fields
| Field | Type | Offset | Meaning |
| --- | --- | --- | --- |
| MiniMetadataSignature| uint | 0 | Magic value used to identify that there are streams |
| TotalSize | uint | 4 | Total size of the entire set of MiniMetadata streams including this header |
| Count of Streams | uint | 8 | Number of streams in the MiniMetadata |
The concept is that each stream simply follows the previous stream in the buffer.
There is no padding, so the data is not expected to be aligned within the buffer.
NOTE: At the moment there is only 1 supported stream type, so Count of Streams can only be 1.
The `EENameStream` is structured as a header, plus a series of null-terminated utf8 strings, and pointers.
The EENameStream header
| Field | Type | Offset | Meaning |
| --- | --- | --- | --- |
| EENameStreamSignature | uint | 0 | Magic value used to identify that the bytes immediately following are an `EENameStream` |
| CountOfNames | uint | 4 | Number of names encoded |
EENameStream entry
| Field | Type | Offset | Meaning |
| --- | --- | --- | --- |
| Pointer | pointer | 0 | Pointer to type system structure |
| String | null-terminated UTF-8 sting | 4 or 8 based on target pointer size | Pointer to type system structure |
Following the EENameStream header, there are CountOfNames entries. Each entry begins with a target pointer sized block which identifies a particular type system data structure, followed by a utf8 encoded null-terminated string.
``` csharp
string StringFromEEAddress(TargetPointer address)
{
TargetPointer miniMetaDataBuffAddress = _target.Read<uint>(_target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffAddress));
uint miniMetaDataBuffMaxSize = _target.Read<uint>(_target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffMaxSize));
// Parse MiniMetadataStream according the the format described above to produce a dictionary from pointer to string from the EENameStream.
// Then lookup in the dictionary, to produce a result if it was present in the table.
// In general, since this api is intended for fallback scenarios, implementations of this api should attempt
// to return null instead of producing errors.
// Since in normal execution of the runtime no stream is constructed, it is normal when examining full dumps and live process state without a stream encoded.
}
```

View file

@ -26,6 +26,75 @@ record struct ModuleLookupTables(
TargetPointer MethodDefToDesc,
TargetPointer TypeDefToMethodTable,
TargetPointer TypeRefToMethodTable);
internal struct EcmaMetadataSchema
{
public EcmaMetadataSchema(string metadataVersion, bool largeStringHeap, bool largeBlobHeap, bool largeGuidHeap, int[] rowCount, bool[] isSorted, bool variableSizedColumnsAre4BytesLong)
{
MetadataVersion = metadataVersion;
LargeStringHeap = largeStringHeap;
LargeBlobHeap = largeBlobHeap;
LargeGuidHeap = largeGuidHeap;
_rowCount = rowCount;
_isSorted = isSorted;
VariableSizedColumnsAreAll4BytesLong = variableSizedColumnsAre4BytesLong;
}
public readonly string MetadataVersion;
public readonly bool LargeStringHeap;
public readonly bool LargeBlobHeap;
public readonly bool LargeGuidHeap;
// Table data, these structures hold MetadataTable.Count entries
private readonly int[] _rowCount;
public readonly ReadOnlySpan<int> RowCount => _rowCount;
private readonly bool[] _isSorted;
public readonly ReadOnlySpan<bool> IsSorted => _isSorted;
// In certain scenarios the size of the tables is forced to be the maximum size
// Otherwise the size of columns should be computed based on RowSize/the various heap flags
public readonly bool VariableSizedColumnsAreAll4BytesLong;
}
internal class TargetEcmaMetadata
{
public TargetEcmaMetadata(EcmaMetadataSchema schema,
TargetSpan[] tables,
TargetSpan stringHeap,
TargetSpan userStringHeap,
TargetSpan blobHeap,
TargetSpan guidHeap)
{
Schema = schema;
_tables = tables;
StringHeap = stringHeap;
UserStringHeap = userStringHeap;
BlobHeap = blobHeap;
GuidHeap = guidHeap;
}
public EcmaMetadataSchema Schema { get; init; }
private TargetSpan[] _tables;
public ReadOnlySpan<TargetSpan> Tables => _tables;
public TargetSpan StringHeap { get; init; }
public TargetSpan UserStringHeap { get; init; }
public TargetSpan BlobHeap { get; init; }
public TargetSpan GuidHeap { get; init; }
}
[Flags]
internal enum AvailableMetadataType
{
None = 0,
ReadOnly = 1,
ReadWriteSavedCopy = 2,
ReadWrite = 4
}
```
``` csharp
@ -36,13 +105,31 @@ TargetPointer GetLoaderAllocator(ModuleHandle handle);
TargetPointer GetThunkHeap(ModuleHandle handle);
TargetPointer GetILBase(ModuleHandle handle);
TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size);
AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle);
TargetPointer GetReadWriteSavedMetadataAddress(ModuleHandle handle, out ulong size);
TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle);
ModuleLookupTables GetLookupTables(ModuleHandle handle);
```
## Version 1
Data descriptors used:
- `Module`
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `Module` | `Assembly` | Assembly of the Module |
| `Module` | `Base` | Pointer to start of PE file in memory |
| `Module` | `Flags` | Assembly of the Module |
| `Module` | `LoaderAllocator` | LoaderAllocator of the Module |
| `Module` | `ThunkHeap` | Pointer to the thunk heap |
| `Module` | `DynamicMetadata` | Pointer to saved metadata for reflection emit modules |
| `Module` | `FieldDefToDescMap` | Mapping table |
| `Module` | `ManifestModuleReferencesMap` | Mapping table |
| `Module` | `MemberRefToDescMap` | Mapping table |
| `Module` | `MethodDefToDescMap` | Mapping table |
| `Module` | `TypeDefToMethodTableMap` | Mapping table |
| `Module` | `TypeRefToMethodTableMap` | Mapping table |
| `DynamicMetadata` | `Size` | Size of the dynamic metadata blob (as a 32bit uint) |
| `DynamicMetadata` | `Data` | Start of dynamic metadata data array |
``` csharp
ModuleHandle GetModuleHandle(TargetPointer modulePointer)
@ -94,6 +181,32 @@ TargetPointer GetMetadataAddress(ModuleHandle handle, out ulong size)
return baseAddress + rva;
}
AvailableMetadataType ILoader.GetAvailableMetadataType(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
AvailableMetadataType flags = AvailableMetadataType.None;
TargetPointer dynamicMetadata = target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */);
if (dynamicMetadata != TargetPointer.Null)
flags |= AvailableMetadataType.ReadWriteSavedCopy;
else
flags |= AvailableMetadataType.ReadOnly;
return flags;
}
TargetPointer ILoader.GetReadWriteSavedMetadataAddress(ModuleHandle handle, out ulong size)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
TargetPointer dynamicMetadata = target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */);
size = target.Read<uint>(handle.Address + /* DynamicMetadata::Size offset */);
TargetPointer result = handle.Address + /* DynamicMetadata::Data offset */;
return result;
}
ModuleLookupTables GetLookupTables(ModuleHandle handle)
{
return new ModuleLookupTables(

View file

@ -4,46 +4,75 @@ This contract is for exploring the properties of the runtime types of values on
## APIs of contract
A `MethodTable` is the runtime representation of the type information about a value. Given a `TargetPointer` address, the `RuntimeTypeSystem` contract provides a `MethodTableHandle` for querying the `MethodTable`.
A `TypeHandle` is the runtime representation of the type information about a value which is represented as a TypeHandle.
Given a `TargetPointer` address, the `RuntimeTypeSystem` contract provides a `TypeHandle` for querying the details of the `TypeHandle`.
``` csharp
struct MethodTableHandle
struct TypeHandle
{
// no public properties or constructors
// no public constructors
internal TargetPointer Address { get; }
public TargetPointer Address { get; }
public bool IsNull => Address != 0;
}
internal enum CorElementType
{
// Values defined in ECMA-335 - II.23.1.16 Element types used in signatures
// +
Internal = 0x21, // Indicates that the next pointer sized number of bytes is the address of a TypeHandle. Signatures that contain the Internal CorElementType cannot exist in metadata that is saved into a serialized format.
}
```
``` csharp
#region MethodTable inspection APIs
public virtual MethodTableHandle GetMethodTableHandle(TargetPointer targetPointer);
A `TypeHandle` is the runtime representation of the type information about a value. This can be constructed from the address of a `TypeHandle` or a `MethodTable`.
public virtual TargetPointer GetModule(MethodTableHandle methodTable);
``` csharp
#region TypeHandle inspection APIs
public virtual TypeHandle GetTypeHandle(TargetPointer targetPointer);
public virtual TargetPointer GetModule(TypeHandle typeHandle);
// A canonical method table is either the MethodTable itself, or in the case of a generic instantiation, it is the
// MethodTable of the prototypical instance.
public virtual TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTable);
public virtual TargetPointer GetParentMethodTable(MethodTableHandle methodTable);
public virtual TargetPointer GetCanonicalMethodTable(TypeHandle typeHandle);
public virtual TargetPointer GetParentMethodTable(TypeHandle typeHandle);
public virtual uint GetBaseSize(MethodTableHandle methodTable);
public virtual uint GetBaseSize(TypeHandle typeHandle);
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
public virtual uint GetComponentSize(MethodTableHandle methodTable);
public virtual uint GetComponentSize(TypeHandle typeHandle);
// True if the MethodTable is the sentinel value associated with unallocated space in the managed heap
public virtual bool IsFreeObjectMethodTable(MethodTableHandle methodTable);
public virtual bool IsString(MethodTableHandle methodTable);
public virtual bool IsFreeObjectMethodTable(TypeHandle typeHandle);
public virtual bool IsString(TypeHandle typeHandle);
// True if the MethodTable represents a type that contains managed references
public virtual bool ContainsGCPointers(MethodTableHandle methodTable);
public virtual bool IsDynamicStatics(MethodTableHandle methodTable);
public virtual ushort GetNumMethods(MethodTableHandle methodTable);
public virtual ushort GetNumInterfaces(MethodTableHandle methodTable);
public virtual bool ContainsGCPointers(TypeHandle typeHandle);
public virtual bool IsDynamicStatics(TypeHandle typeHandle);
public virtual ushort GetNumMethods(TypeHandle typeHandle);
public virtual ushort GetNumInterfaces(TypeHandle typeHandle);
// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
public virtual uint GetTypeDefToken(MethodTableHandle methodTable);
public virtual uint GetTypeDefToken(TypeHandle typeHandle);
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
// or for its generic type definition if it is a generic instantiation
public virtual uint GetTypeDefTypeAttributes(MethodTableHandle methodTable);
#endregion MethodTable inspection APIs
public virtual uint GetTypeDefTypeAttributes(TypeHandle typeHandle);
public virtual ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle typeHandle);
public virtual bool IsGenericTypeDefinition(TypeHandle typeHandle);
public virtual TypeHandle TypeHandleFromAddress(TargetPointer address);
public virtual bool HasTypeParam(TypeHandle typeHandle);
// Element type of the type. NOTE: this drops the CorElementType.GenericInst, and CorElementType.String is returned as CorElementType.Class.
// NOTE: If this returns CorElementType.ValueType it may be a normal valuetype or a "NATIVE" valuetype used to represent an interop view of a structure
// HasTypeParam will return true for cases where this is the interop view, and false for normal valuetypes.
public virtual CorElementType GetSignatureCorElementType(TypeHandle typeHandle);
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
public virtual bool IsArray(TypeHandle typeHandle, out uint rank);
public virtual TypeHandle GetTypeParam(TypeHandle typeHandle);
public virtual bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token);
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
#endregion TypeHandle inspection APIs
```
## Version 1
@ -60,6 +89,7 @@ internal partial struct RuntimeTypeSystem_1
{
GenericsMask = 0x00000030,
GenericsMask_NonGeneric = 0x00000000, // no instantiation
GenericsMask_TypicalInstantiation = 0x00000030, // the type instantiated at its formal parameters, e.g. List<T>
StringArrayValues = GenericsMask_NonGeneric,
}
@ -69,9 +99,17 @@ internal partial struct RuntimeTypeSystem_1
internal enum WFLAGS_HIGH : uint
{
Category_Mask = 0x000F0000,
Category_Array = 0x00080000,
Category_ElementType_Mask = 0x000E0000,
Category_Array_Mask = 0x000C0000,
Category_IfArrayThenSzArray = 0x00020000,
Category_Array = 0x00080000,
Category_ValueType = 0x00040000,
Category_Nullable = 0x00050000,
Category_PrimitiveValueType = 0x00060000,
Category_TruePrimitive = 0x00070000,
Category_Interface = 0x000C0000,
ContainsGCPointers = 0x01000000,
HasComponentSize = 0x80000000, // This is set if lower 16 bits is used for the component size,
// otherwise the lower bits are used for WFLAGS_LOW
@ -118,6 +156,7 @@ internal partial struct RuntimeTypeSystem_1
public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric);
public bool ContainsGCPointers => GetFlag(WFLAGS_HIGH.ContainsGCPointers) != 0;
public bool IsDynamicStatics => GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0;
public bool IsGenericTypeDefinition => TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_TypicalInstantiation);
}
[Flags]
@ -127,6 +166,16 @@ internal partial struct RuntimeTypeSystem_1
CanonMT = 1,
Mask = 1,
}
// Low order bits of TypeHandle address.
// If the low bits contain a 2, then it is a TypeDesc
[Flags]
internal enum TypeHandleBits
{
MethodTable = 0,
TypeDesc = 2,
ValidMask = 2,
}
}
```
@ -141,6 +190,7 @@ internal struct MethodTable_1
internal TargetPointer ParentMethodTable { get; }
internal TargetPointer Module { get; }
internal TargetPointer EEClassOrCanonMT { get; }
internal TargetPointer PerInstInfo { get; }
internal MethodTable_1(Data.MethodTable data)
{
Flags = new RuntimeTypeSystem_1.MethodTableFlags
@ -154,24 +204,77 @@ internal struct MethodTable_1
EEClassOrCanonMT = data.EEClassOrCanonMT;
Module = data.Module;
ParentMethodTable = data.ParentMethodTable;
PerInstInfo = data.PerInstInfo;
}
}
```
Internally the contract uses extension methods on the `TypeHandle` api so that it can distinguish between `MethodTable` and `TypeDesc`
```csharp
static class RuntimeTypeSystem_1_Helpers
{
public static bool IsTypeDesc(this TypeHandle type)
{
return type.Address != 0 && (type.Address & TypeHandleBits.ValidMask) == TypeHandleBits.TypeDesc;
}
public static bool IsMethodTable(this TypeHandle type)
{
return type.Address != 0 && (type.Address & TypeHandleBits.ValidMask) == TypeHandleBits.MethodTable;
}
public static TargetPointer TypeDescAddress(this TypeHandle type)
{
if (!type.IsTypeDesc())
return 0;
return (ulong)type.Address & ~(ulong)TypeHandleBits.ValidMask;
}
}
```
The contract depends on the global pointer value `FreeObjectMethodTablePointer`.
The contract additionally depends on the `EEClass` data descriptor.
The contract additionally depends on these data descriptors
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `MethodTable` | `MTFlags` | One of the flags fields on `MethodTable` |
| `MethodTable` | `MTFlags2` | One of the flags fields on `MethodTable` |
| `MethodTable` | `BaseSize` | BaseSize of a `MethodTable` |
| `MethodTable` | `EEClassOrCanonMT` | Path to both EEClass and canonical MethodTable of a MethodTable |
| `MethodTable` | `Module` | Module for `MethodTable` |
| `MethodTable` | `ParentMethodTable` | Parent type pointer of `MethodTable` |
| `MethodTable` | `NumInterfaces` | Number of interfaces of `MethodTable` |
| `MethodTable` | `NumVirtuals` | Number of virtual methods in `MethodTable` |
| `MethodTable` | `PerInstInfo` | Either the array element type, or pointer to generic information for `MethodTable` |
| `EEClass` | `InternalCorElementType` | An InternalCorElementType uses the enum values of a CorElementType to indicate some of the information about the type of the type which uses the EEClass In particular, all reference types are CorElementType.Class, Enums are the element type of their underlying type and ValueTypes which can exactly be represented as an element type are represented as such, all other values types are represented as CorElementType.ValueType. |
| `EEClass` | `MethodTable` | Pointer to the canonical MethodTable of this type |
| `EEClass` | `NumMethods` | Count of methods attached to the EEClass |
| `EEClass` | `CorTypeAttr` | Various flags |
| `ArrayClass` | `Rank` | Rank of the associated array MethodTable |
| `TypeDesc` | `TypeAndFlags` | The lower 8 bits are the CorElementType of the `TypeDesc`, the upper 24 bits are reserved for flags |
| `ParamTypeDesc` | `TypeArg` | Associated type argument |
| `TypeVarTypeDesc` | `Module` | Pointer to module which defines the type variable |
| `TypeVarTypeDesc` | `Token` | Token of the type variable |
| `FnPtrTypeDesc` | `NumArgs` | Number of arguments to the function described by the `TypeDesc` |
| `FnPtrTypeDesc` | `CallConv` | Lower 8 bits is the calling convention bit as extracted by the signature that defines this `TypeDesc` |
| `FnPtrTypeDesc` | `RetAndArgTypes` | Pointer to an array of TypeHandle addresses. This length of this is 1 more than `NumArgs` |
| `GenericsDictInfo` | `NumTypeArgs` | Number of type arguments in the type or method instantiation described by this `GenericsDictInfo` |
```csharp
private readonly Dictionary<TargetPointer, MethodTable_1> _methodTables;
internal TargetPointer FreeObjectMethodTablePointer {get; }
public MethodTableHandle GetMethodTableHandle(TargetPointer methodTablePointer)
public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
{
... // validate that methodTablePointer points to something that looks like a MethodTable.
... // read Data.MethodTable from methodTablePointer.
... // create a MethodTable_1 and add it to _methodTables.
return MethodTableHandle { Address = methodTablePointer }
... // validate that typeHandlePointer points to something that looks like a MethodTable or a TypeDesc.
... // If this is a MethodTable
... // read Data.MethodTable from typeHandlePointer.
... // create a MethodTable_1 and add it to _methodTables.
return TypeHandle { Address = typeHandlePointer }
}
internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr)
@ -179,11 +282,11 @@ The contract additionally depends on the `EEClass` data descriptor.
return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask);
}
public uint GetBaseSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.BaseSize;
public uint GetBaseSize(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? (uint)0 : _methodTables[TypeHandle.Address].Flags.BaseSize;
public uint GetComponentSize(MethodTableHandle methodTableHandle) => GetComponentSize(_methodTables[methodTableHandle.Address]);
public uint GetComponentSize(TypeHandle TypeHandle) =>!typeHandle.IsMethodTable() ? (uint)0 : GetComponentSize(_methodTables[TypeHandle.Address]);
private TargetPointer GetClassPointer(MethodTableHandle methodTableHandle)
private TargetPointer GetClassPointer(TypeHandle TypeHandle)
{
... // if the MethodTable stores a pointer to the EEClass, return it
// otherwise the MethodTable stores a pointer to the canonical MethodTable
@ -191,34 +294,232 @@ The contract additionally depends on the `EEClass` data descriptor.
// Canonical MethodTables always store an EEClass pointer.
}
private Data.EEClass GetClassData(MethodTableHandle methodTableHandle)
private Data.EEClass GetClassData(TypeHandle TypeHandle)
{
TargetPointer eeClassPtr = GetClassPointer(methodTableHandle);
TargetPointer eeClassPtr = GetClassPointer(TypeHandle);
... // read Data.EEClass data from eeClassPtr
}
public TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).MethodTable;
public TargetPointer GetCanonicalMethodTable(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(TypeHandle).MethodTable;
public TargetPointer GetModule(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Module;
public TargetPointer GetParentMethodTable(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].ParentMethodTable;
public bool IsFreeObjectMethodTable(MethodTableHandle methodTableHandle) => FreeObjectMethodTablePointer == methodTableHandle.Address;
public bool IsString(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsString;
public bool ContainsGCPointers(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ContainsGCPointers;
public uint GetTypeDefToken(MethodTableHandle methodTableHandle)
public TargetPointer GetModule(TypeHandle TypeHandle)
{
MethodTable_1 methodTable = _methodTables[methodTableHandle.Address];
return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
if (typeHandle.IsMethodTable())
{
return _methodTables[TypeHandle.Address].Module;
}
else if (typeHandle.IsTypeDesc())
{
if (HasTypeParam(typeHandle))
{
return GetModule(GetTypeParam(typeHandle));
}
else if (IsGenericVariable(typeHandle, out TargetPointer genericParamModule, out _))
{
return genericParamModule;
}
}
return TargetPointer.Null;
}
public ushort GetNumMethods(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).NumMethods;
public TargetPointer GetParentMethodTable(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : _methodTables[TypeHandle.Address].ParentMethodTable;
public ushort GetNumInterfaces(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].NumInterfaces;
public bool IsFreeObjectMethodTable(TypeHandle TypeHandle) => FreeObjectMethodTablePointer == TypeHandle.Address;
public uint GetTypeDefTypeAttributes(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).CorTypeAttr;
public bool IsString(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsString;
public bool ContainsGCPointers(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.ContainsGCPointers;
public bool IsDynamicStatics(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsDynamicStatics;
public uint GetTypeDefToken(TypeHandle TypeHandle)
{
if (!typeHandle.IsMethodTable())
return 0;
MethodTable_1 typeHandle = _methodTables[TypeHandle.Address];
return (uint)(typeHandle.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
}
public ushort GetNumMethods(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).NumMethods;
public ushort GetNumInterfaces(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : _methodTables[TypeHandle.Address].NumInterfaces;
public uint GetTypeDefTypeAttributes(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? 0 : GetClassData(TypeHandle).CorTypeAttr;
public bool IsDynamicStatics(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsDynamicStatics;
public ReadOnlySpan<TypeHandle> GetInstantiation(TypeHandle TypeHandle)
{
if (!typeHandle.IsMethodTable())
return default;
MethodTable_1 typeHandle = _methodTables[TypeHandle.Address];
if (!typeHandle.Flags.HasInstantiation)
return default;
TargetPointer perInstInfo = typeHandle.PerInstInfo;
TargetPointer genericsDictInfo = perInstInfo - (ulong)_target.PointerSize;
TargetPointer dictionaryPointer = _target.ReadPointer(perInstInfo);
int NumTypeArgs = // Read NumTypeArgs from genericsDictInfo using GenericsDictInfo contract
TypeHandle[] instantiation = new TypeHandle[NumTypeArgs];
for (int i = 0; i < NumTypeArgs; i++)
instantiation[i] = GetTypeHandle(_target.ReadPointer(dictionaryPointer + _target.PointerSize * i));
return instantiation;
}
public bool IsDynamicStatics(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsDynamicStatics;
public bool HasTypeParam(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
{
MethodTable typeHandle = _methodTables[typeHandle.Address];
return typeHandle.Flags.IsArray;
}
else if (typeHandle.IsTypeDesc())
{
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
CorElementType elemType = (CorElementType)(TypeAndFlags & 0xFF);
switch (elemType)
{
case CorElementType.ValueType:
case CorElementType.Byref:
case CorElementType.Ptr:
return true;
}
}
return false;
}
public CorElementType GetSignatureCorElementType(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
{
MethodTable typeHandle = _methodTables[typeHandle.Address];
switch (typeHandle.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
{
case WFLAGS_HIGH.Category_Array:
return CorElementType.Array;
case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
return CorElementType.SzArray;
case WFLAGS_HIGH.Category_ValueType:
case WFLAGS_HIGH.Category_Nullable:
case WFLAGS_HIGH.Category_PrimitiveValueType:
return CorElementType.ValueType;
case WFLAGS_HIGH.Category_TruePrimitive:
return (CorElementType)GetClassData(typeHandle).InternalCorElementType;
default:
return CorElementType.Class;
}
}
else if (typeHandle.IsTypeDesc())
{
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
return (CorElementType)(TypeAndFlags & 0xFF);
}
return default(CorElementType);
}
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
public bool IsArray(TypeHandle typeHandle, out uint rank)
{
if (typeHandle.IsMethodTable())
{
MethodTable typeHandle = _methodTables[typeHandle.Address];
switch (typeHandle.Flags.GetFlag(WFLAGS_HIGH.Category_Mask))
{
case WFLAGS_HIGH.Category_Array:
TargetPointer clsPtr = GetClassPointer(typeHandle);
rank = // Read Rank field from ArrayClass contract using address clsPtr
return true;
case WFLAGS_HIGH.Category_Array | WFLAGS_HIGH.Category_IfArrayThenSzArray:
rank = 1;
return true;
}
}
rank = 0;
return false;
}
public TypeHandle GetTypeParam(TypeHandle typeHandle)
{
if (typeHandle.IsMethodTable())
{
MethodTable typeHandle = _methodTables[typeHandle.Address];
// Validate that this is an array
if (!typeHandle.Flags.IsArray)
throw new ArgumentException(nameof(typeHandle));
return TypeHandleFromAddress(typeHandle.PerInstInfo);
}
else if (typeHandle.IsTypeDesc())
{
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
switch (elemType)
{
case CorElementType.ValueType:
case CorElementType.Byref:
case CorElementType.Ptr:
TargetPointer typeArgPointer = // Read TypeArg field from ParamTypeDesc contract using address typeHandle.TypeDescAddress()
return TypeHandleFromAddress(typeArgPointer);
}
}
throw new ArgumentException(nameof(typeHandle));
}
public bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token)
{
module = TargetPointer.Null;
token = 0;
if (!typeHandle.IsTypeDesc())
return false;
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
switch (elemType)
{
case CorElementType.MVar:
case CorElementType.Var:
module = // Read Module field from TypeVarTypeDesc contract using address typeHandle.TypeDescAddress()
token = // Read Module field from TypeVarTypeDesc contract using address typeHandle.TypeDescAddress()
return true;
}
return false;
}
public bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv)
{
retAndArgTypes = default;
callConv = default;
if (!typeHandle.IsTypeDesc())
return false;
int TypeAndFlags = // Read TypeAndFlags field from TypeDesc contract using address typeHandle.TypeDescAddress()
CorElementType elemType = (CorElementType)(typeDesc.TypeAndFlags & 0xFF);
if (elemType != CorElementType.FnPtr)
return false;
int NumArgs = // Read NumArgs field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress()
TargetPointer RetAndArgTypes = // Read NumArgs field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress()
TypeHandle[] retAndArgTypesArray = new TypeHandle[NumTypeArgs + 1];
for (int i = 0; i <= NumTypeArgs; i++)
retAndArgTypesArray[i] = GetTypeHandle(_target.ReadPointer(RetAndArgTypes + _target.PointerSize * i));
retAndArgTypes = retAndArgTypesArray;
callConv = (byte) // Read CallConv field from FnPtrTypeDesc contract using address typeHandle.TypeDescAddress(), and then ignore all but the low 8 bits.
return true;
}
```