mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-09 17:44:48 +09:00
[cdac] Begin adding MethodDesc APIs to the RuntimeTypeSystem contract (#104811)
This is largely a placeholder just to get the MethodDesc/MethodDescChunk infrastructure in place. To further fill out ValidateMethodDesc and to implement the GetMethodDescData DAC API, I also need to add an executable code manager contract for mapping between native code pointer values and methods. I'd like to do that as a separate PR. Debugged WinDbg enough to verify that GetMethodDescData is implemented enough to extract the correct MethodTable pointer value from a MethodDesc. * start GetMethodDescDataImpl * add MethodDesc and MethodDescChunk * checkpoint: MethodDesc validation * update contract * fix RuntimeTypeSystem unit tests mock the additional data and globals * fix GetMethodDescChunkPointerMayThrow * add data descriptor description to the contract * Apply suggestions from code review Co-authored-by: Elinor Fung <elfung@microsoft.com> * MayThrow -> Throwing * Slot is ushort not byte * remove unused property * add TargetPointer 32-/64-bit max constants * use NewArrayHolder * spelling * add globals to RTS contract * remove unused usings * constexpr cdac_offsets, not const * Apply suggestions from code review Co-authored-by: Elinor Fung <elfung@microsoft.com> * make GetNumVtableSlots private --------- Co-authored-by: Elinor Fung <elfung@microsoft.com>
This commit is contained in:
parent
b92fbf67be
commit
27086b7125
19 changed files with 522 additions and 19 deletions
|
@ -4,10 +4,11 @@ This contract is for exploring the properties of the runtime types of values on
|
||||||
|
|
||||||
## APIs of contract
|
## APIs of contract
|
||||||
|
|
||||||
|
### TypeHandle
|
||||||
|
|
||||||
A `TypeHandle` is the runtime representation of the type information about a value which is represented as a TypeHandle.
|
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`.
|
Given a `TargetPointer` address, the `RuntimeTypeSystem` contract provides a `TypeHandle` for querying the details of the `TypeHandle`.
|
||||||
|
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
struct TypeHandle
|
struct TypeHandle
|
||||||
{
|
{
|
||||||
|
@ -28,6 +29,8 @@ internal enum CorElementType
|
||||||
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`.
|
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`.
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
|
partial interface IRuntimeTypeSystem : IContract
|
||||||
|
{
|
||||||
#region TypeHandle inspection APIs
|
#region TypeHandle inspection APIs
|
||||||
public virtual TypeHandle GetTypeHandle(TargetPointer targetPointer);
|
public virtual TypeHandle GetTypeHandle(TargetPointer targetPointer);
|
||||||
|
|
||||||
|
@ -73,10 +76,35 @@ A `TypeHandle` is the runtime representation of the type information about a val
|
||||||
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
|
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
|
||||||
|
|
||||||
#endregion TypeHandle inspection APIs
|
#endregion TypeHandle inspection APIs
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### MethodDesc
|
||||||
|
|
||||||
|
A `MethodDesc` is the runtime representation of a managed method (either from IL, from reflection emit, or generated by the runtime).
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
struct MethodDescHandle
|
||||||
|
{
|
||||||
|
// no public properties or constructors
|
||||||
|
|
||||||
|
internal TargetPointer Address { get; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
partial interface IRuntimeTypeSystem : IContract
|
||||||
|
{
|
||||||
|
public virtual MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer);
|
||||||
|
|
||||||
|
public virtual TargetPointer GetMethodTable(MethodDescHandle methodDesc);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Version 1
|
## Version 1
|
||||||
|
|
||||||
|
### TypeHandle
|
||||||
|
|
||||||
The `MethodTable` inspection APIs are implemented in terms of the following flags on the runtime `MethodTable` structure:
|
The `MethodTable` inspection APIs are implemented in terms of the following flags on the runtime `MethodTable` structure:
|
||||||
|
|
||||||
``` csharp
|
``` csharp
|
||||||
|
@ -233,7 +261,11 @@ static class RuntimeTypeSystem_1_Helpers
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The contract depends on the global pointer value `FreeObjectMethodTablePointer`.
|
The contract depends on the following globals
|
||||||
|
|
||||||
|
| Global name | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `FreeObjectMethodTablePointer` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory
|
||||||
|
|
||||||
The contract additionally depends on these data descriptors
|
The contract additionally depends on these data descriptors
|
||||||
|
|
||||||
|
@ -251,6 +283,7 @@ The contract additionally depends on these data descriptors
|
||||||
| `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` | `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` | `MethodTable` | Pointer to the canonical MethodTable of this type |
|
||||||
| `EEClass` | `NumMethods` | Count of methods attached to the EEClass |
|
| `EEClass` | `NumMethods` | Count of methods attached to the EEClass |
|
||||||
|
| `EEClass` | `NumNonVirtualSlots` | Count of non-virtual slots for the EEClass |
|
||||||
| `EEClass` | `CorTypeAttr` | Various flags |
|
| `EEClass` | `CorTypeAttr` | Various flags |
|
||||||
| `ArrayClass` | `Rank` | Rank of the associated array MethodTable |
|
| `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 |
|
| `TypeDesc` | `TypeAndFlags` | The lower 8 bits are the CorElementType of the `TypeDesc`, the upper 24 bits are reserved for flags |
|
||||||
|
@ -523,3 +556,28 @@ The contract additionally depends on these data descriptors
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### MethodDesc
|
||||||
|
|
||||||
|
The version 1 `MethodDesc` APIs depend on the `MethodDescAlignment` global and the `MethodDesc` and `MethodDescChunk` data descriptors.
|
||||||
|
|
||||||
|
| Global name | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `MethodDescAlignment` | `MethodDescChunk` trailing data is allocated in multiples of this constant. The size (in bytes) of each `MethodDesc` (or subclass) instance is a multiple of this constant.
|
||||||
|
|
||||||
|
|
||||||
|
In the runtime a `MethodDesc` implicitly belongs to a single `MethodDescChunk` and some common data is shared between method descriptors that belong to the same chunk. A single method table
|
||||||
|
will typically have multiple chunks. There are subkinds of MethodDescs at runtime of varying sizes (but the sizes must be mutliples of `MethodDescAlignment`) and each chunk contains method descriptors of the same size.
|
||||||
|
|
||||||
|
We depend on the following data descriptors:
|
||||||
|
| Data Descriptor Name | Field | Meaning |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `MethodDesc` | `ChunkIndex` | Offset of this `MethodDesc` relative to the end of its containing `MethodDescChunk` - in multiples of `MethodDescAlignment`
|
||||||
|
| `MethodDesc` | `Slot` | The method's slot
|
||||||
|
| `MethodDesc` | `Flags` | The method's flags
|
||||||
|
| `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to
|
||||||
|
| `MethodDescChunk` | `Next` | The next chunk of methods
|
||||||
|
| `MethodDescChunk` | `Size` | The size of this `MethodDescChunk` following this `MethodDescChunk` header, minus 1. In multiples of `MethodDescAlignment`
|
||||||
|
| `MethodDescChunk` | `Count` | The number of `MethodDesc` entries in this chunk, minus 1.
|
||||||
|
|
||||||
|
**TODO(cdac)**
|
||||||
|
|
|
@ -1243,6 +1243,7 @@ public:
|
||||||
HRESULT GetObjectExceptionDataImpl(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data);
|
HRESULT GetObjectExceptionDataImpl(CLRDATA_ADDRESS objAddr, struct DacpExceptionObjectData *data);
|
||||||
HRESULT GetObjectStringDataImpl(CLRDATA_ADDRESS obj, unsigned int count, _Inout_updates_z_(count) WCHAR *stringData, unsigned int *pNeeded);
|
HRESULT GetObjectStringDataImpl(CLRDATA_ADDRESS obj, unsigned int count, _Inout_updates_z_(count) WCHAR *stringData, unsigned int *pNeeded);
|
||||||
HRESULT GetUsefulGlobalsImpl(struct DacpUsefulGlobalsData *globalsData);
|
HRESULT GetUsefulGlobalsImpl(struct DacpUsefulGlobalsData *globalsData);
|
||||||
|
HRESULT GetMethodDescDataImpl(CLRDATA_ADDRESS methodDesc, CLRDATA_ADDRESS ip, struct DacpMethodDescData *data, ULONG cRevertedRejitVersions, DacpReJitData * rgRevertedRejitData, ULONG * pcNeededRevertedRejitData);
|
||||||
|
|
||||||
BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
|
BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
|
||||||
#ifndef TARGET_UNIX
|
#ifndef TARGET_UNIX
|
||||||
|
|
|
@ -1041,6 +1041,70 @@ HRESULT ClrDataAccess::GetMethodDescData(
|
||||||
}
|
}
|
||||||
|
|
||||||
SOSDacEnter();
|
SOSDacEnter();
|
||||||
|
if (m_cdacSos != NULL)
|
||||||
|
{
|
||||||
|
// Try the cDAC first - it will return E_NOTIMPL if it doesn't support this method yet. Fall back to the DAC.
|
||||||
|
hr = m_cdacSos->GetMethodDescData(methodDesc, ip, methodDescData, cRevertedRejitVersions, rgRevertedRejitData, pcNeededRevertedRejitData);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
hr = GetMethodDescDataImpl(methodDesc, ip, methodDescData, cRevertedRejitVersions, rgRevertedRejitData, pcNeededRevertedRejitData);
|
||||||
|
}
|
||||||
|
#ifdef _DEBUG
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Assert that the data is the same as what we get from the DAC.
|
||||||
|
DacpMethodDescData mdDataLocal;
|
||||||
|
NewArrayHolder<DacpReJitData> rgRevertedRejitDataLocal{};
|
||||||
|
if (rgRevertedRejitData != nullptr)
|
||||||
|
{
|
||||||
|
rgRevertedRejitDataLocal = new DacpReJitData[cRevertedRejitVersions];
|
||||||
|
}
|
||||||
|
ULONG cNeededRevertedRejitDataLocal = 0;
|
||||||
|
ULONG *pcNeededRevertedRejitDataLocal = NULL;
|
||||||
|
if (pcNeededRevertedRejitData != NULL)
|
||||||
|
{
|
||||||
|
pcNeededRevertedRejitDataLocal = &cNeededRevertedRejitDataLocal;
|
||||||
|
}
|
||||||
|
HRESULT hrLocal = GetMethodDescDataImpl(methodDesc, ip,&mdDataLocal, cRevertedRejitVersions, rgRevertedRejitDataLocal, pcNeededRevertedRejitDataLocal);
|
||||||
|
_ASSERTE(hr == hrLocal);
|
||||||
|
_ASSERTE(methodDescData->bHasNativeCode == mdDataLocal.bHasNativeCode);
|
||||||
|
_ASSERTE(methodDescData->bIsDynamic == mdDataLocal.bIsDynamic);
|
||||||
|
_ASSERTE(methodDescData->wSlotNumber == mdDataLocal.wSlotNumber);
|
||||||
|
_ASSERTE(methodDescData->NativeCodeAddr == mdDataLocal.NativeCodeAddr);
|
||||||
|
_ASSERTE(methodDescData->AddressOfNativeCodeSlot == mdDataLocal.AddressOfNativeCodeSlot);
|
||||||
|
//TODO[cdac]: assert the rest of mdDataLocal contains the same info as methodDescData
|
||||||
|
if (rgRevertedRejitData != NULL)
|
||||||
|
{
|
||||||
|
_ASSERTE (cNeededRevertedRejitDataLocal == *pcNeededRevertedRejitData);
|
||||||
|
for (ULONG i = 0; i < cNeededRevertedRejitDataLocal; i++)
|
||||||
|
{
|
||||||
|
_ASSERTE(rgRevertedRejitData[i].rejitID == rgRevertedRejitDataLocal[i].rejitID);
|
||||||
|
_ASSERTE(rgRevertedRejitData[i].NativeCodeAddr == rgRevertedRejitDataLocal[i].NativeCodeAddr);
|
||||||
|
_ASSERTE(rgRevertedRejitData[i].flags == rgRevertedRejitDataLocal[i].flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hr = GetMethodDescDataImpl(methodDesc, ip, methodDescData, cRevertedRejitVersions, rgRevertedRejitData, pcNeededRevertedRejitData);
|
||||||
|
}
|
||||||
|
|
||||||
|
SOSDacLeave();
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT ClrDataAccess::GetMethodDescDataImpl(
|
||||||
|
CLRDATA_ADDRESS methodDesc,
|
||||||
|
CLRDATA_ADDRESS ip,
|
||||||
|
struct DacpMethodDescData *methodDescData,
|
||||||
|
ULONG cRevertedRejitVersions,
|
||||||
|
DacpReJitData * rgRevertedRejitData,
|
||||||
|
ULONG * pcNeededRevertedRejitData)
|
||||||
|
{
|
||||||
|
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
PTR_MethodDesc pMD = PTR_MethodDesc(TO_TADDR(methodDesc));
|
PTR_MethodDesc pMD = PTR_MethodDesc(TO_TADDR(methodDesc));
|
||||||
|
|
||||||
|
@ -1236,7 +1300,6 @@ HRESULT ClrDataAccess::GetMethodDescData(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SOSDacLeave();
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -202,7 +202,7 @@ CDAC_TYPE_FIELD(Module, /*pointer*/, TypeDefToMethodTableMap, cdac_offsets<Modul
|
||||||
CDAC_TYPE_FIELD(Module, /*pointer*/, TypeRefToMethodTableMap, cdac_offsets<Module>::TypeRefToMethodTableMap)
|
CDAC_TYPE_FIELD(Module, /*pointer*/, TypeRefToMethodTableMap, cdac_offsets<Module>::TypeRefToMethodTableMap)
|
||||||
CDAC_TYPE_END(Module)
|
CDAC_TYPE_END(Module)
|
||||||
|
|
||||||
// Metadata
|
// RuntimeTypeSystem
|
||||||
|
|
||||||
CDAC_TYPE_BEGIN(MethodTable)
|
CDAC_TYPE_BEGIN(MethodTable)
|
||||||
CDAC_TYPE_INDETERMINATE(MethodTable)
|
CDAC_TYPE_INDETERMINATE(MethodTable)
|
||||||
|
@ -223,6 +223,7 @@ CDAC_TYPE_FIELD(EEClass, /*pointer*/, MethodTable, cdac_offsets<EEClass>::Method
|
||||||
CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumMethods, cdac_offsets<EEClass>::NumMethods)
|
CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumMethods, cdac_offsets<EEClass>::NumMethods)
|
||||||
CDAC_TYPE_FIELD(EEClass, /*uint32*/, CorTypeAttr, cdac_offsets<EEClass>::CorTypeAttr)
|
CDAC_TYPE_FIELD(EEClass, /*uint32*/, CorTypeAttr, cdac_offsets<EEClass>::CorTypeAttr)
|
||||||
CDAC_TYPE_FIELD(EEClass, /*uint8*/, InternalCorElementType, cdac_offsets<EEClass>::InternalCorElementType)
|
CDAC_TYPE_FIELD(EEClass, /*uint8*/, InternalCorElementType, cdac_offsets<EEClass>::InternalCorElementType)
|
||||||
|
CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumNonVirtualSlots, cdac_offsets<EEClass>::NumNonVirtualSlots)
|
||||||
CDAC_TYPE_END(EEClass)
|
CDAC_TYPE_END(EEClass)
|
||||||
|
|
||||||
CDAC_TYPE_BEGIN(ArrayClass)
|
CDAC_TYPE_BEGIN(ArrayClass)
|
||||||
|
@ -263,6 +264,21 @@ CDAC_TYPE_FIELD(DynamicMetadata, /*uint32*/, Size, cdac_offsets<DynamicMetadata>
|
||||||
CDAC_TYPE_FIELD(DynamicMetadata, /*inline byte array*/, Data, cdac_offsets<DynamicMetadata>::Data)
|
CDAC_TYPE_FIELD(DynamicMetadata, /*inline byte array*/, Data, cdac_offsets<DynamicMetadata>::Data)
|
||||||
CDAC_TYPE_END(DynamicMetadata)
|
CDAC_TYPE_END(DynamicMetadata)
|
||||||
|
|
||||||
|
CDAC_TYPE_BEGIN(MethodDesc)
|
||||||
|
CDAC_TYPE_INDETERMINATE(MethodDesc)
|
||||||
|
CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, ChunkIndex, cdac_offsets<MethodDesc>::ChunkIndex)
|
||||||
|
CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Slot, cdac_offsets<MethodDesc>::Slot)
|
||||||
|
CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags, cdac_offsets<MethodDesc>::Flags)
|
||||||
|
CDAC_TYPE_END(MethodDesc)
|
||||||
|
|
||||||
|
CDAC_TYPE_BEGIN(MethodDescChunk)
|
||||||
|
CDAC_TYPE_SIZE(sizeof(MethodDescChunk))
|
||||||
|
CDAC_TYPE_FIELD(MethodDescChunk, /*pointer*/, MethodTable, cdac_offsets<MethodDescChunk>::MethodTable)
|
||||||
|
CDAC_TYPE_FIELD(MethodDescChunk, /*pointer*/, Next, cdac_offsets<MethodDescChunk>::Next)
|
||||||
|
CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Size, cdac_offsets<MethodDescChunk>::Size)
|
||||||
|
CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Count, cdac_offsets<MethodDescChunk>::Count)
|
||||||
|
CDAC_TYPE_END(MethodDescChunk)
|
||||||
|
|
||||||
CDAC_TYPES_END()
|
CDAC_TYPES_END()
|
||||||
|
|
||||||
CDAC_GLOBALS_BEGIN()
|
CDAC_GLOBALS_BEGIN()
|
||||||
|
@ -282,6 +298,7 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1 | 1 << 2)
|
||||||
CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1)
|
CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1)
|
||||||
#endif //TARGET_64BIT
|
#endif //TARGET_64BIT
|
||||||
CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION)
|
CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION)
|
||||||
|
CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT)
|
||||||
CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass)
|
CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass)
|
||||||
CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable)
|
CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable)
|
||||||
CDAC_GLOBAL_POINTER(ObjectMethodTable, &::g_pObjectClass)
|
CDAC_GLOBAL_POINTER(ObjectMethodTable, &::g_pObjectClass)
|
||||||
|
|
|
@ -1807,6 +1807,7 @@ template<> struct cdac_offsets<EEClass>
|
||||||
static constexpr size_t MethodTable = offsetof(EEClass, m_pMethodTable);
|
static constexpr size_t MethodTable = offsetof(EEClass, m_pMethodTable);
|
||||||
static constexpr size_t NumMethods = offsetof(EEClass, m_NumMethods);
|
static constexpr size_t NumMethods = offsetof(EEClass, m_NumMethods);
|
||||||
static constexpr size_t CorTypeAttr = offsetof(EEClass, m_dwAttrClass);
|
static constexpr size_t CorTypeAttr = offsetof(EEClass, m_dwAttrClass);
|
||||||
|
static constexpr size_t NumNonVirtualSlots = offsetof(EEClass, m_NumNonVirtualSlots);
|
||||||
};
|
};
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1908,6 +1908,15 @@ private:
|
||||||
public:
|
public:
|
||||||
static void Init();
|
static void Init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template<typename T> friend struct ::cdac_offsets;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct cdac_offsets<MethodDesc>
|
||||||
|
{
|
||||||
|
static constexpr size_t ChunkIndex = offsetof(MethodDesc, m_chunkIndex);
|
||||||
|
static constexpr size_t Slot = offsetof(MethodDesc, m_wSlotNumber);
|
||||||
|
static constexpr size_t Flags = offsetof(MethodDesc, m_wFlags);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef DACCESS_COMPILE
|
#ifndef DACCESS_COMPILE
|
||||||
|
@ -2328,6 +2337,17 @@ private:
|
||||||
UINT16 m_flagsAndTokenRange;
|
UINT16 m_flagsAndTokenRange;
|
||||||
|
|
||||||
// Followed by array of method descs...
|
// Followed by array of method descs...
|
||||||
|
|
||||||
|
template<typename T> friend struct ::cdac_offsets;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct cdac_offsets<MethodDescChunk>
|
||||||
|
{
|
||||||
|
static constexpr size_t MethodTable = offsetof(MethodDescChunk, m_methodTable);
|
||||||
|
static constexpr size_t Next = offsetof(MethodDescChunk, m_next);
|
||||||
|
static constexpr size_t Size = offsetof(MethodDescChunk, m_size);
|
||||||
|
static constexpr size_t Count = offsetof(MethodDescChunk, m_count);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline int MethodDesc::GetMethodDescChunkIndex() const
|
inline int MethodDesc::GetMethodDescChunkIndex() const
|
||||||
|
|
|
@ -25,5 +25,7 @@ internal static class Constants
|
||||||
|
|
||||||
internal const string MiniMetaDataBuffAddress = nameof(MiniMetaDataBuffAddress);
|
internal const string MiniMetaDataBuffAddress = nameof(MiniMetaDataBuffAddress);
|
||||||
internal const string MiniMetaDataBuffMaxSize = nameof(MiniMetaDataBuffMaxSize);
|
internal const string MiniMetaDataBuffMaxSize = nameof(MiniMetaDataBuffMaxSize);
|
||||||
|
|
||||||
|
internal const string MethodDescAlignment = nameof(MethodDescAlignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,16 @@ internal enum CorElementType
|
||||||
Sentinel = 0x41,
|
Sentinel = 0x41,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal readonly struct MethodDescHandle
|
||||||
|
{
|
||||||
|
internal MethodDescHandle(TargetPointer address)
|
||||||
|
{
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal TargetPointer Address { get; }
|
||||||
|
}
|
||||||
|
|
||||||
internal interface IRuntimeTypeSystem : IContract
|
internal interface IRuntimeTypeSystem : IContract
|
||||||
{
|
{
|
||||||
static string IContract.Name => nameof(RuntimeTypeSystem);
|
static string IContract.Name => nameof(RuntimeTypeSystem);
|
||||||
|
@ -62,9 +72,10 @@ internal interface IRuntimeTypeSystem : IContract
|
||||||
{
|
{
|
||||||
TargetPointer targetPointer = target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable);
|
TargetPointer targetPointer = target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable);
|
||||||
TargetPointer freeObjectMethodTable = target.ReadPointer(targetPointer);
|
TargetPointer freeObjectMethodTable = target.ReadPointer(targetPointer);
|
||||||
|
ulong methodDescAlignment = target.ReadGlobal<ulong>(Constants.Globals.MethodDescAlignment);
|
||||||
return version switch
|
return version switch
|
||||||
{
|
{
|
||||||
1 => new RuntimeTypeSystem_1(target, freeObjectMethodTable),
|
1 => new RuntimeTypeSystem_1(target, freeObjectMethodTable, methodDescAlignment),
|
||||||
_ => default(RuntimeTypeSystem),
|
_ => default(RuntimeTypeSystem),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -113,6 +124,11 @@ internal interface IRuntimeTypeSystem : IContract
|
||||||
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv) => throw new NotImplementedException();
|
public virtual bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv) => throw new NotImplementedException();
|
||||||
// Returns null if the TypeHandle is not a class/struct/generic variable
|
// Returns null if the TypeHandle is not a class/struct/generic variable
|
||||||
#endregion TypeHandle inspection APIs
|
#endregion TypeHandle inspection APIs
|
||||||
|
|
||||||
|
#region MethodDesc inspection APIs
|
||||||
|
public virtual MethodDescHandle GetMethodDescHandle(TargetPointer targetPointer) => throw new NotImplementedException();
|
||||||
|
public virtual TargetPointer GetMethodTable(MethodDescHandle methodDesc) => throw new NotImplementedException();
|
||||||
|
#endregion MethodDesc inspection APIs
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct RuntimeTypeSystem : IRuntimeTypeSystem
|
internal struct RuntimeTypeSystem : IRuntimeTypeSystem
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||||
|
|
||||||
|
@ -85,6 +86,26 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
||||||
internal TargetPointer MethodTable => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(MethodTable)].Offset);
|
internal TargetPointer MethodTable => _target.ReadPointer(Address + (ulong)_type.Fields[nameof(MethodTable)].Offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal struct MethodDesc
|
||||||
|
{
|
||||||
|
private readonly Target _target;
|
||||||
|
private readonly Data.MethodDesc _desc;
|
||||||
|
private readonly Data.MethodDescChunk _chunk;
|
||||||
|
internal MethodDesc(Target target, Data.MethodDesc desc, Data.MethodDescChunk chunk)
|
||||||
|
{
|
||||||
|
_target = target;
|
||||||
|
_desc = desc;
|
||||||
|
_chunk = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasFlag(MethodDescFlags flag) => (_desc.Flags & (ushort)flag) != 0;
|
||||||
|
|
||||||
|
internal byte ChunkIndex => _desc.ChunkIndex;
|
||||||
|
internal TargetPointer MethodTable => _chunk.MethodTable;
|
||||||
|
internal ushort Slot => _desc.Slot;
|
||||||
|
internal bool HasNonVtableSlot => HasFlag(MethodDescFlags.HasNonVtableSlot);
|
||||||
|
}
|
||||||
|
|
||||||
internal static MethodTable GetMethodTableData(Target target, TargetPointer methodTablePointer)
|
internal static MethodTable GetMethodTableData(Target target, TargetPointer methodTablePointer)
|
||||||
{
|
{
|
||||||
return new MethodTable(target, methodTablePointer);
|
return new MethodTable(target, methodTablePointer);
|
||||||
|
@ -194,5 +215,99 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TargetPointer GetMethodDescChunkPointerThrowing(TargetPointer methodDescPointer, Data.MethodDesc umd)
|
||||||
|
{
|
||||||
|
ulong? methodDescChunkSize = _target.GetTypeInfo(DataType.MethodDescChunk).Size;
|
||||||
|
if (!methodDescChunkSize.HasValue)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Target has no definite MethodDescChunk size");
|
||||||
|
}
|
||||||
|
// The runtime allocates a contiguous block of memory for a MethodDescChunk followed by MethodDescAlignment * Size bytes of space
|
||||||
|
// that is filled with MethodDesc (or its subclasses) instances. Each MethodDesc has a ChunkIndex that indicates its
|
||||||
|
// offset from the end of the MethodDescChunk.
|
||||||
|
ulong chunkAddress = (ulong)methodDescPointer - methodDescChunkSize.Value - umd.ChunkIndex * MethodDescAlignment;
|
||||||
|
return new TargetPointer(chunkAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Data.MethodDescChunk GetMethodDescChunkThrowing(TargetPointer methodDescPointer, Data.MethodDesc md, out TargetPointer methodDescChunkPointer)
|
||||||
|
{
|
||||||
|
methodDescChunkPointer = GetMethodDescChunkPointerThrowing(methodDescPointer, md);
|
||||||
|
return new Data.MethodDescChunk(_target, methodDescChunkPointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NonValidated.MethodDesc GetMethodDescThrowing(TargetPointer methodDescPointer, out TargetPointer methodDescChunkPointer)
|
||||||
|
{
|
||||||
|
// may throw if the method desc at methodDescPointer is corrupted
|
||||||
|
// we bypass the target data cache here because we don't want to cache non-validated data
|
||||||
|
Data.MethodDesc desc = new Data.MethodDesc(_target, methodDescPointer);
|
||||||
|
Data.MethodDescChunk chunk = GetMethodDescChunkThrowing(methodDescPointer, desc, out methodDescChunkPointer);
|
||||||
|
return new NonValidated.MethodDesc(_target, desc, chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateMethodDescPointer(TargetPointer methodDescPointer, [NotNullWhen(true)] out TargetPointer methodDescChunkPointer)
|
||||||
|
{
|
||||||
|
methodDescChunkPointer = TargetPointer.Null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NonValidated.MethodDesc umd = GetMethodDescThrowing(methodDescPointer, out methodDescChunkPointer);
|
||||||
|
TargetPointer methodTablePointer = umd.MethodTable;
|
||||||
|
if (methodTablePointer == TargetPointer.Null
|
||||||
|
|| methodTablePointer == TargetPointer.Max64Bit
|
||||||
|
|| methodTablePointer == TargetPointer.Max32Bit)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TypeHandle typeHandle = GetTypeHandle(methodTablePointer);
|
||||||
|
|
||||||
|
if (umd.Slot >= GetNumVtableSlots(typeHandle) && !umd.HasNonVtableSlot)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: request.cpp
|
||||||
|
// TODO[cdac]: this needs a Precode lookup
|
||||||
|
// see MethodDescChunk::GetTemporaryEntryPoint
|
||||||
|
#if false
|
||||||
|
MethodDesc *pMDCheck = MethodDesc::GetMethodDescFromStubAddr(pMD->GetTemporaryEntryPoint(), TRUE);
|
||||||
|
|
||||||
|
if (PTR_HOST_TO_TADDR(pMD) != PTR_HOST_TO_TADDR(pMDCheck))
|
||||||
|
{
|
||||||
|
retval = FALSE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: request.cpp
|
||||||
|
// TODO[cdac]: needs MethodDesc::GetNativeCode and MethodDesc::GetMethodEntryPoint()
|
||||||
|
#if false
|
||||||
|
if (retval && pMD->HasNativeCode() && !pMD->IsFCall())
|
||||||
|
{
|
||||||
|
PCODE jitCodeAddr = pMD->GetNativeCode();
|
||||||
|
|
||||||
|
MethodDesc *pMDCheck = ExecutionManager::GetCodeMethodDesc(jitCodeAddr);
|
||||||
|
if (pMDCheck)
|
||||||
|
{
|
||||||
|
// Check that the given MethodDesc matches the MethodDesc from
|
||||||
|
// the CodeHeader
|
||||||
|
if (PTR_HOST_TO_TADDR(pMD) != PTR_HOST_TO_TADDR(pMDCheck))
|
||||||
|
{
|
||||||
|
retval = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retval = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (System.Exception)
|
||||||
|
{
|
||||||
|
// TODO(cdac): maybe don't swallow all exceptions? We could consider a richer contract that
|
||||||
|
// helps to track down what sort of memory corruption caused the validation to fail.
|
||||||
|
// TODO(cdac): we could also consider a more fine-grained exception type so we don't mask
|
||||||
|
// programmer mistakes in cdacreader.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
||||||
using System.Reflection.Metadata.Ecma335;
|
using System.Reflection.Metadata.Ecma335;
|
||||||
using Microsoft.Diagnostics.DataContractReader.Data;
|
using Microsoft.Diagnostics.DataContractReader.Data;
|
||||||
using Microsoft.Diagnostics.DataContractReader.Contracts.RuntimeTypeSystem_1_NS;
|
using Microsoft.Diagnostics.DataContractReader.Contracts.RuntimeTypeSystem_1_NS;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||||
|
@ -15,10 +14,12 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
||||||
{
|
{
|
||||||
private readonly Target _target;
|
private readonly Target _target;
|
||||||
private readonly TargetPointer _freeObjectMethodTablePointer;
|
private readonly TargetPointer _freeObjectMethodTablePointer;
|
||||||
|
private readonly ulong _methodDescAlignment;
|
||||||
|
|
||||||
// TODO(cdac): we mutate this dictionary - copies of the RuntimeTypeSystem_1 struct share this instance.
|
// TODO(cdac): we mutate this dictionary - copies of the RuntimeTypeSystem_1 struct share this instance.
|
||||||
// If we need to invalidate our view of memory, we should clear this dictionary.
|
// If we need to invalidate our view of memory, we should clear this dictionary.
|
||||||
private readonly Dictionary<TargetPointer, MethodTable> _methodTables = new();
|
private readonly Dictionary<TargetPointer, MethodTable> _methodTables = new();
|
||||||
|
private readonly Dictionary<TargetPointer, MethodDesc> _methodDescs = new();
|
||||||
|
|
||||||
|
|
||||||
internal struct MethodTable
|
internal struct MethodTable
|
||||||
|
@ -45,6 +46,9 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
||||||
ParentMethodTable = data.ParentMethodTable;
|
ParentMethodTable = data.ParentMethodTable;
|
||||||
PerInstInfo = data.PerInstInfo;
|
PerInstInfo = data.PerInstInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this MethodTable is a canonical MethodTable if its EEClassOrCanonMT is an EEClass
|
||||||
|
internal bool IsCanonMT => GetEEClassOrCanonMTBits(EEClassOrCanonMT) == EEClassOrCanonMTBits.EEClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Low order bit of EEClassOrCanonMT.
|
// Low order bit of EEClassOrCanonMT.
|
||||||
|
@ -67,14 +71,39 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
||||||
ValidMask = 2,
|
ValidMask = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
internal RuntimeTypeSystem_1(Target target, TargetPointer freeObjectMethodTablePointer)
|
[Flags]
|
||||||
|
internal enum MethodDescFlags : ushort
|
||||||
|
{
|
||||||
|
HasNonVtableSlot = 0x0008,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal struct MethodDesc
|
||||||
|
{
|
||||||
|
private readonly Data.MethodDesc _desc;
|
||||||
|
private readonly Data.MethodDescChunk _chunk;
|
||||||
|
internal TargetPointer Address { get; init; }
|
||||||
|
internal MethodDesc(TargetPointer methodDescPointer, Data.MethodDesc desc, Data.MethodDescChunk chunk)
|
||||||
|
{
|
||||||
|
_desc = desc;
|
||||||
|
_chunk = chunk;
|
||||||
|
Address = methodDescPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TargetPointer MethodTable => _chunk.MethodTable;
|
||||||
|
public ushort Slot => _desc.Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal RuntimeTypeSystem_1(Target target, TargetPointer freeObjectMethodTablePointer, ulong methodDescAlignment)
|
||||||
{
|
{
|
||||||
_target = target;
|
_target = target;
|
||||||
_freeObjectMethodTablePointer = freeObjectMethodTablePointer;
|
_freeObjectMethodTablePointer = freeObjectMethodTablePointer;
|
||||||
|
_methodDescAlignment = methodDescAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer;
|
internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer;
|
||||||
|
|
||||||
|
internal ulong MethodDescAlignment => _methodDescAlignment;
|
||||||
|
|
||||||
public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
|
public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer)
|
||||||
{
|
{
|
||||||
TypeHandleBits addressLowBits = (TypeHandleBits)((ulong)typeHandlePointer & ((ulong)_target.PointerSize - 1));
|
TypeHandleBits addressLowBits = (TypeHandleBits)((ulong)typeHandlePointer & ((ulong)_target.PointerSize - 1));
|
||||||
|
@ -417,4 +446,51 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ushort GetNumVtableSlots(TypeHandle typeHandle)
|
||||||
|
{
|
||||||
|
if (!typeHandle.IsMethodTable())
|
||||||
|
return 0;
|
||||||
|
MethodTable methodTable = _methodTables[typeHandle.Address];
|
||||||
|
ushort numNonVirtualSlots = methodTable.IsCanonMT ? GetClassData(typeHandle).NumNonVirtualSlots : (ushort)0;
|
||||||
|
return checked((ushort)(methodTable.NumVirtuals + numNonVirtualSlots));
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer)
|
||||||
|
{
|
||||||
|
// if we already validated this address, return a handle
|
||||||
|
if (_methodDescs.ContainsKey(methodDescPointer))
|
||||||
|
{
|
||||||
|
return new MethodDescHandle(methodDescPointer);
|
||||||
|
}
|
||||||
|
// Check if we cached the underlying data already
|
||||||
|
if (_target.ProcessedData.TryGet(methodDescPointer, out Data.MethodDesc? methodDescData))
|
||||||
|
{
|
||||||
|
// we already cached the data, we must have validated the address, create the representation struct for our use
|
||||||
|
TargetPointer mdescChunkPtr = GetMethodDescChunkPointerThrowing(methodDescPointer, methodDescData);
|
||||||
|
// FIXME[cdac]: this isn't threadsafe
|
||||||
|
if (!_target.ProcessedData.TryGet(mdescChunkPtr, out Data.MethodDescChunk? methodDescChunkData))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("cached MethodDesc data but not its containing MethodDescChunk");
|
||||||
|
}
|
||||||
|
MethodDesc validatedMethodDesc = new MethodDesc(methodDescPointer, methodDescData, methodDescChunkData);
|
||||||
|
_ = _methodDescs.TryAdd(methodDescPointer, validatedMethodDesc);
|
||||||
|
return new MethodDescHandle(methodDescPointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateMethodDescPointer(methodDescPointer, out TargetPointer methodDescChunkPointer))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Invalid method desc pointer");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok, we validated it, cache the data and add the MethodDesc struct to the dictionary
|
||||||
|
Data.MethodDescChunk validatedMethodDescChunkData = _target.ProcessedData.GetOrAdd<Data.MethodDescChunk>(methodDescChunkPointer);
|
||||||
|
Data.MethodDesc validatedMethodDescData = _target.ProcessedData.GetOrAdd<Data.MethodDesc>(methodDescPointer);
|
||||||
|
|
||||||
|
MethodDesc trustedMethodDescF = new MethodDesc(methodDescPointer, validatedMethodDescData, validatedMethodDescChunkData);
|
||||||
|
_ = _methodDescs.TryAdd(methodDescPointer, trustedMethodDescF);
|
||||||
|
return new MethodDescHandle(methodDescPointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TargetPointer GetMethodTable(MethodDescHandle methodDescHandle) => _methodDescs[methodDescHandle.Address].MethodTable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ public sealed class EEClass : IData<EEClass>
|
||||||
NumMethods = target.Read<ushort>(address + (ulong)type.Fields[nameof(NumMethods)].Offset);
|
NumMethods = target.Read<ushort>(address + (ulong)type.Fields[nameof(NumMethods)].Offset);
|
||||||
CorTypeAttr = target.Read<uint>(address + (ulong)type.Fields[nameof(CorTypeAttr)].Offset);
|
CorTypeAttr = target.Read<uint>(address + (ulong)type.Fields[nameof(CorTypeAttr)].Offset);
|
||||||
InternalCorElementType = target.Read<byte>(address + (ulong)type.Fields[nameof(InternalCorElementType)].Offset);
|
InternalCorElementType = target.Read<byte>(address + (ulong)type.Fields[nameof(InternalCorElementType)].Offset);
|
||||||
|
NumNonVirtualSlots = target.Read<ushort>(address + (ulong)type.Fields[nameof(NumNonVirtualSlots)].Offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TargetPointer MethodTable { get; init; }
|
public TargetPointer MethodTable { get; init; }
|
||||||
|
@ -28,6 +29,8 @@ public sealed class EEClass : IData<EEClass>
|
||||||
// Enums are the element type of their underlying type
|
// Enums are the element type of their underlying type
|
||||||
// ValueTypes which can exactly be represented as an element type are represented as such
|
// ValueTypes which can exactly be represented as an element type are represented as such
|
||||||
public byte InternalCorElementType { get; init; }
|
public byte InternalCorElementType { get; init; }
|
||||||
|
|
||||||
|
public ushort NumNonVirtualSlots { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ArrayClass : IData<ArrayClass>
|
public sealed class ArrayClass : IData<ArrayClass>
|
||||||
|
|
23
src/native/managed/cdacreader/src/Data/MethodDesc.cs
Normal file
23
src/native/managed/cdacreader/src/Data/MethodDesc.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||||
|
|
||||||
|
internal sealed class MethodDesc : IData<MethodDesc>
|
||||||
|
{
|
||||||
|
static MethodDesc IData<MethodDesc>.Create(Target target, TargetPointer address) => new MethodDesc(target, address);
|
||||||
|
public MethodDesc(Target target, TargetPointer address)
|
||||||
|
{
|
||||||
|
Target.TypeInfo type = target.GetTypeInfo(DataType.MethodDesc);
|
||||||
|
|
||||||
|
ChunkIndex = target.Read<byte>(address + (ulong)type.Fields[nameof(ChunkIndex)].Offset);
|
||||||
|
Slot = target.Read<ushort>(address + (ulong)type.Fields[nameof(Slot)].Offset);
|
||||||
|
Flags = target.Read<ushort>(address + (ulong)type.Fields[nameof(Flags)].Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte ChunkIndex { get; init; }
|
||||||
|
public ushort Slot { get; init; }
|
||||||
|
public ushort Flags { get; init; }
|
||||||
|
}
|
25
src/native/managed/cdacreader/src/Data/MethodDescChunk.cs
Normal file
25
src/native/managed/cdacreader/src/Data/MethodDescChunk.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.Diagnostics.DataContractReader.Data;
|
||||||
|
|
||||||
|
internal sealed class MethodDescChunk : IData<MethodDescChunk>
|
||||||
|
{
|
||||||
|
static MethodDescChunk IData<MethodDescChunk>.Create(Target target, TargetPointer address) => new MethodDescChunk(target, address);
|
||||||
|
public MethodDescChunk(Target target, TargetPointer address)
|
||||||
|
{
|
||||||
|
Target.TypeInfo type = target.GetTypeInfo(DataType.MethodDescChunk);
|
||||||
|
|
||||||
|
MethodTable = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodTable)].Offset);
|
||||||
|
Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset);
|
||||||
|
Size = target.Read<byte>(address + (ulong)type.Fields[nameof(Size)].Offset);
|
||||||
|
Count = target.Read<byte>(address + (ulong)type.Fields[nameof(Count)].Offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TargetPointer MethodTable { get; init; }
|
||||||
|
public TargetPointer Next { get; init; }
|
||||||
|
public byte Size { get; init; }
|
||||||
|
public byte Count { get; init; }
|
||||||
|
}
|
|
@ -39,4 +39,6 @@ public enum DataType
|
||||||
DynamicMetadata,
|
DynamicMetadata,
|
||||||
Object,
|
Object,
|
||||||
String,
|
String,
|
||||||
|
MethodDesc,
|
||||||
|
MethodDescChunk,
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,55 @@ internal struct DacpUsefulGlobalsData
|
||||||
}
|
}
|
||||||
#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
|
#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
|
||||||
|
|
||||||
|
internal struct DacpReJitData
|
||||||
|
{
|
||||||
|
// FIXME[cdac]: the C++ definition enum doesn't have an explicit underlying type or constant values for the flags
|
||||||
|
public enum Flags : uint
|
||||||
|
{
|
||||||
|
kUnknown = 0,
|
||||||
|
kRequested = 1,
|
||||||
|
kActive = 2,
|
||||||
|
kReverted = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ rejitID;
|
||||||
|
public Flags flags; /* = Flags::kUnknown*/
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ NativeCodeAddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
internal struct DacpMethodDescData
|
||||||
|
{
|
||||||
|
public int bHasNativeCode;
|
||||||
|
public int bIsDynamic;
|
||||||
|
public ushort wSlotNumber;
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ NativeCodeAddr;
|
||||||
|
// Useful for breaking when a method is jitted.
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ AddressOfNativeCodeSlot;
|
||||||
|
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ MethodDescPtr;
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ MethodTablePtr;
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ ModulePtr;
|
||||||
|
|
||||||
|
public uint /*mdToken*/ MDToken;
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ GCInfo;
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ GCStressCodeCopy;
|
||||||
|
|
||||||
|
// This is only valid if bIsDynamic is true
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ managedDynamicMethodObject;
|
||||||
|
|
||||||
|
public ulong /*CLRDATA_ADDRESS*/ requestedIP;
|
||||||
|
|
||||||
|
// Gives info for the single currently active version of a method
|
||||||
|
public DacpReJitData rejitDataCurrent;
|
||||||
|
|
||||||
|
// Gives info corresponding to requestedIP (for !ip2md)
|
||||||
|
public DacpReJitData rejitDataRequested;
|
||||||
|
|
||||||
|
// Total number of rejit versions that have been jitted
|
||||||
|
public uint /*ULONG*/ cJittedRejitVersions;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
[GeneratedComInterface]
|
[GeneratedComInterface]
|
||||||
[Guid("436f00f2-b42a-4b9f-870c-e73db66ae930")]
|
[Guid("436f00f2-b42a-4b9f-870c-e73db66ae930")]
|
||||||
internal unsafe partial interface ISOSDacInterface
|
internal unsafe partial interface ISOSDacInterface
|
||||||
|
@ -155,7 +204,7 @@ internal unsafe partial interface ISOSDacInterface
|
||||||
|
|
||||||
// MethodDescs
|
// MethodDescs
|
||||||
[PreserveSig]
|
[PreserveSig]
|
||||||
int GetMethodDescData(ulong methodDesc, ulong ip, /*struct DacpMethodDescData*/ void* data, uint cRevertedRejitVersions, /*struct DacpReJitData*/ void* rgRevertedRejitData, uint* pcNeededRevertedRejitData);
|
int GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDescData* data, uint cRevertedRejitVersions, DacpReJitData* rgRevertedRejitData, uint* pcNeededRevertedRejitData);
|
||||||
[PreserveSig]
|
[PreserveSig]
|
||||||
int GetMethodDescPtrFromIP(ulong ip, ulong* ppMD);
|
int GetMethodDescPtrFromIP(ulong ip, ulong* ppMD);
|
||||||
[PreserveSig]
|
[PreserveSig]
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
|
|
||||||
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
using Microsoft.Diagnostics.DataContractReader.Contracts;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.Contracts;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.InteropServices.Marshalling;
|
using System.Runtime.InteropServices.Marshalling;
|
||||||
|
|
||||||
|
@ -79,7 +76,37 @@ internal sealed partial class SOSDacImpl : ISOSDacInterface, ISOSDacInterface2,
|
||||||
public unsafe int GetJitHelperFunctionName(ulong ip, uint count, byte* name, uint* pNeeded) => HResults.E_NOTIMPL;
|
public unsafe int GetJitHelperFunctionName(ulong ip, uint count, byte* name, uint* pNeeded) => HResults.E_NOTIMPL;
|
||||||
public unsafe int GetJitManagerList(uint count, void* managers, uint* pNeeded) => HResults.E_NOTIMPL;
|
public unsafe int GetJitManagerList(uint count, void* managers, uint* pNeeded) => HResults.E_NOTIMPL;
|
||||||
public unsafe int GetJumpThunkTarget(void* ctx, ulong* targetIP, ulong* targetMD) => HResults.E_NOTIMPL;
|
public unsafe int GetJumpThunkTarget(void* ctx, ulong* targetIP, ulong* targetMD) => HResults.E_NOTIMPL;
|
||||||
public unsafe int GetMethodDescData(ulong methodDesc, ulong ip, void* data, uint cRevertedRejitVersions, void* rgRevertedRejitData, uint* pcNeededRevertedRejitData) => HResults.E_NOTIMPL;
|
public unsafe int GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDescData* data, uint cRevertedRejitVersions, DacpReJitData* rgRevertedRejitData, uint* pcNeededRevertedRejitData)
|
||||||
|
{
|
||||||
|
if (methodDesc == 0)
|
||||||
|
{
|
||||||
|
return HResults.E_INVALIDARG;
|
||||||
|
}
|
||||||
|
if (cRevertedRejitVersions != 0 && rgRevertedRejitData == null)
|
||||||
|
{
|
||||||
|
return HResults.E_INVALIDARG;
|
||||||
|
}
|
||||||
|
if (rgRevertedRejitData != null && pcNeededRevertedRejitData == null)
|
||||||
|
{
|
||||||
|
// If you're asking for reverted rejit data, you'd better ask for the number of
|
||||||
|
// elements we return
|
||||||
|
return HResults.E_INVALIDARG;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
|
||||||
|
Contracts.MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(methodDesc);
|
||||||
|
|
||||||
|
data->MethodTablePtr = rtsContract.GetMethodTable(methodDescHandle);
|
||||||
|
|
||||||
|
return HResults.E_NOTIMPL;
|
||||||
|
}
|
||||||
|
catch (global::System.Exception ex)
|
||||||
|
{
|
||||||
|
return ex.HResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe int GetMethodDescFromToken(ulong moduleAddr, uint token, ulong* methodDesc) => HResults.E_NOTIMPL;
|
public unsafe int GetMethodDescFromToken(ulong moduleAddr, uint token, ulong* methodDesc) => HResults.E_NOTIMPL;
|
||||||
public unsafe int GetMethodDescName(ulong methodDesc, uint count, char* name, uint* pNeeded) => HResults.E_NOTIMPL;
|
public unsafe int GetMethodDescName(ulong methodDesc, uint count, char* name, uint* pNeeded) => HResults.E_NOTIMPL;
|
||||||
public unsafe int GetMethodDescPtrFromFrame(ulong frameAddr, ulong* ppMD) => HResults.E_NOTIMPL;
|
public unsafe int GetMethodDescPtrFromFrame(ulong frameAddr, ulong* ppMD) => HResults.E_NOTIMPL;
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace Microsoft.Diagnostics.DataContractReader;
|
||||||
public readonly struct TargetPointer : IEquatable<TargetPointer>
|
public readonly struct TargetPointer : IEquatable<TargetPointer>
|
||||||
{
|
{
|
||||||
public static TargetPointer Null = new(0);
|
public static TargetPointer Null = new(0);
|
||||||
|
public static TargetPointer Max32Bit = new(uint.MaxValue);
|
||||||
|
public static TargetPointer Max64Bit = new(ulong.MaxValue);
|
||||||
|
|
||||||
public readonly ulong Value;
|
public readonly ulong Value;
|
||||||
public TargetPointer(ulong value) => Value = value;
|
public TargetPointer(ulong value) => Value = value;
|
||||||
|
|
|
@ -35,6 +35,7 @@ public unsafe class MethodTableTests
|
||||||
{ nameof (Data.EEClass.CorTypeAttr), new () { Offset = 16, Type = DataType.uint32}},
|
{ nameof (Data.EEClass.CorTypeAttr), new () { Offset = 16, Type = DataType.uint32}},
|
||||||
{ nameof (Data.EEClass.NumMethods), new () { Offset = 20, Type = DataType.uint16}},
|
{ nameof (Data.EEClass.NumMethods), new () { Offset = 20, Type = DataType.uint16}},
|
||||||
{ nameof (Data.EEClass.InternalCorElementType), new () { Offset = 22, Type = DataType.uint8}},
|
{ nameof (Data.EEClass.InternalCorElementType), new () { Offset = 22, Type = DataType.uint8}},
|
||||||
|
{ nameof (Data.EEClass.NumNonVirtualSlots), new () { Offset = 24, Type = DataType.uint16}},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,6 +49,7 @@ public unsafe class MethodTableTests
|
||||||
private static readonly (string Name, ulong Value, string? Type)[] RTSGlobals =
|
private static readonly (string Name, ulong Value, string? Type)[] RTSGlobals =
|
||||||
[
|
[
|
||||||
(nameof(Constants.Globals.FreeObjectMethodTable), TestFreeObjectMethodTableGlobalAddress, null),
|
(nameof(Constants.Globals.FreeObjectMethodTable), TestFreeObjectMethodTableGlobalAddress, null),
|
||||||
|
(nameof(Constants.Globals.MethodDescAlignment), 8, nameof(DataType.uint64)),
|
||||||
];
|
];
|
||||||
|
|
||||||
private static MockMemorySpace.Builder AddFreeObjectMethodTable(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder)
|
private static MockMemorySpace.Builder AddFreeObjectMethodTable(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder)
|
||||||
|
@ -60,13 +62,14 @@ public unsafe class MethodTableTests
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MockMemorySpace.Builder AddEEClass(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods)
|
private static MockMemorySpace.Builder AddEEClass(TargetTestHelpers targetTestHelpers, MockMemorySpace.Builder builder, TargetPointer eeClassPtr, string name, TargetPointer canonMTPtr, uint attr, ushort numMethods, ushort numNonVirtualSlots)
|
||||||
{
|
{
|
||||||
MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"EEClass '{name}'", Address = eeClassPtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(EEClassTypeInfo)] };
|
MockMemorySpace.HeapFragment eeClassFragment = new() { Name = $"EEClass '{name}'", Address = eeClassPtr, Data = new byte[targetTestHelpers.SizeOfTypeInfo(EEClassTypeInfo)] };
|
||||||
Span<byte> dest = eeClassFragment.Data;
|
Span<byte> dest = eeClassFragment.Data;
|
||||||
targetTestHelpers.WritePointer(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr);
|
targetTestHelpers.WritePointer(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.MethodTable)].Offset), canonMTPtr);
|
||||||
targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr);
|
targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.CorTypeAttr)].Offset), attr);
|
||||||
targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods);
|
targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumMethods)].Offset), numMethods);
|
||||||
|
targetTestHelpers.Write(dest.Slice(EEClassTypeInfo.Fields[nameof(Data.EEClass.NumNonVirtualSlots)].Offset), numNonVirtualSlots);
|
||||||
return builder.AddHeapFragment(eeClassFragment);
|
return builder.AddHeapFragment(eeClassFragment);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -163,7 +166,7 @@ public unsafe class MethodTableTests
|
||||||
System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class;
|
System.Reflection.TypeAttributes typeAttributes = System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class;
|
||||||
const int numMethods = 8; // System.Object has 8 methods
|
const int numMethods = 8; // System.Object has 8 methods
|
||||||
const int numVirtuals = 3; // System.Object has 3 virtual methods
|
const int numVirtuals = 3; // System.Object has 3 virtual methods
|
||||||
builder = AddEEClass(targetTestHelpers, builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods);
|
builder = AddEEClass(targetTestHelpers, builder, systemObjectEEClassPtr, "System.Object", systemObjectMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0);
|
||||||
builder = AddMethodTable(targetTestHelpers, builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr,
|
builder = AddMethodTable(targetTestHelpers, builder, systemObjectMethodTablePtr, "System.Object", systemObjectEEClassPtr,
|
||||||
mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
||||||
module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals);
|
module: TargetPointer.Null, parentMethodTable: TargetPointer.Null, numInterfaces: 0, numVirtuals: numVirtuals);
|
||||||
|
@ -218,7 +221,7 @@ public unsafe class MethodTableTests
|
||||||
const int numInterfaces = 8; // Arbitrary
|
const int numInterfaces = 8; // Arbitrary
|
||||||
const int numVirtuals = 3; // at least as many as System.Object
|
const int numVirtuals = 3; // at least as many as System.Object
|
||||||
uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2;
|
uint mtflags = (uint)RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | /*componentSize: */2;
|
||||||
builder = AddEEClass(targetTestHelpers, builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods);
|
builder = AddEEClass(targetTestHelpers, builder, systemStringEEClassPtr, "System.String", systemStringMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0);
|
||||||
builder = AddMethodTable(targetTestHelpers, builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr,
|
builder = AddMethodTable(targetTestHelpers, builder, systemStringMethodTablePtr, "System.String", systemStringEEClassPtr,
|
||||||
mtflags: mtflags, mtflags2: default, baseSize: targetTestHelpers.StringBaseSize,
|
mtflags: mtflags, mtflags2: default, baseSize: targetTestHelpers.StringBaseSize,
|
||||||
module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals);
|
module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals);
|
||||||
|
@ -293,7 +296,7 @@ public unsafe class MethodTableTests
|
||||||
const int numInterfaces = 0;
|
const int numInterfaces = 0;
|
||||||
const int numVirtuals = 3;
|
const int numVirtuals = 3;
|
||||||
const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst
|
const uint gtd_mtflags = 0x00000030; // TODO: GenericsMask_TypicalInst
|
||||||
builder = AddEEClass(targetTestHelpers, builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods);
|
builder = AddEEClass(targetTestHelpers, builder, genericDefinitionEEClassPtr, "EEClass GenericDefinition", genericDefinitionMethodTablePtr, attr: (uint)typeAttributes, numMethods: numMethods, numNonVirtualSlots: 0);
|
||||||
builder = AddMethodTable(targetTestHelpers, builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr,
|
builder = AddMethodTable(targetTestHelpers, builder, genericDefinitionMethodTablePtr, "MethodTable GenericDefinition", genericDefinitionEEClassPtr,
|
||||||
mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
mtflags: gtd_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
||||||
module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals);
|
module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: numInterfaces, numVirtuals: numVirtuals);
|
||||||
|
@ -348,7 +351,7 @@ public unsafe class MethodTableTests
|
||||||
const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array
|
const ushort systemArrayNumMethods = 37; // Arbitrary. Not trying to exactly match the real System.Array
|
||||||
const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class);
|
const uint systemArrayCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class);
|
||||||
|
|
||||||
builder = AddEEClass(targetTestHelpers, builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods);
|
builder = AddEEClass(targetTestHelpers, builder, systemArrayEEClassPtr, "EEClass System.Array", systemArrayMethodTablePtr, attr: systemArrayCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0);
|
||||||
builder = AddMethodTable(targetTestHelpers, builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr,
|
builder = AddMethodTable(targetTestHelpers, builder, systemArrayMethodTablePtr, "MethodTable System.Array", systemArrayEEClassPtr,
|
||||||
mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
mtflags: default, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
||||||
module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3);
|
module: TargetPointer.Null, parentMethodTable: systemObjectMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3);
|
||||||
|
@ -356,7 +359,7 @@ public unsafe class MethodTableTests
|
||||||
const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize;
|
const uint arrayInst_mtflags = (uint)(RuntimeTypeSystem_1.WFLAGS_HIGH.HasComponentSize | RuntimeTypeSystem_1.WFLAGS_HIGH.Category_Array) | arrayInstanceComponentSize;
|
||||||
const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed);
|
const uint arrayInstCorTypeAttr = (uint)(System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Sealed);
|
||||||
|
|
||||||
builder = AddEEClass(targetTestHelpers, builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods);
|
builder = AddEEClass(targetTestHelpers, builder, arrayInstanceEEClassPtr, "EEClass ArrayInstance", arrayInstanceMethodTablePtr, attr: arrayInstCorTypeAttr, numMethods: systemArrayNumMethods, numNonVirtualSlots: 0);
|
||||||
builder = AddMethodTable(targetTestHelpers, builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr,
|
builder = AddMethodTable(targetTestHelpers, builder, arrayInstanceMethodTablePtr, "MethodTable ArrayInstance", arrayInstanceEEClassPtr,
|
||||||
mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
mtflags: arrayInst_mtflags, mtflags2: default, baseSize: targetTestHelpers.ObjectBaseSize,
|
||||||
module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3);
|
module: TargetPointer.Null, parentMethodTable: systemArrayMethodTablePtr, numInterfaces: systemArrayNumInterfaces, numVirtuals: 3);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue