diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 5693ab205ce592..3e6fefa0790735 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -92,12 +92,51 @@ struct MethodDescHandle } ``` +```csharp +public enum ArrayFunctionType +{ + Get = 0, + Set = 1, + Address = 2, + Constructor = 3 +} +``` + ```csharp partial interface IRuntimeTypeSystem : IContract { public virtual MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer); public virtual TargetPointer GetMethodTable(MethodDescHandle methodDesc); + + // Return true for an uninstantiated generic method + public virtual bool IsGenericMethodDefinition(MethodDescHandle methodDesc); + + public virtual ReadOnlySpan GetGenericMethodInstantiation(MethodDescHandle methodDesc); + + // Return mdTokenNil (0x06000000) if the method doesn't have a token, otherwise return the token of the method + public virtual uint GetMethodToken(MethodDescHandle methodDesc); + + // Return true if a MethodDesc represents an array method + // An array method is also a StoredSigMethodDesc + public virtual bool IsArrayMethod(MethodDescHandle methodDesc, out ArrayFunctionType functionType); + + // Return true if a MethodDesc represents a method without metadata method, either an IL Stub dynamically + // generated by the runtime, or a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class + // Or something else similar. + // A no metadata method is also a StoredSigMethodDesc + public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out ReadOnlySpan methodName); + + // A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata. + public virtual bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan signature); + + // Return true for a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class + // A DynamicMethod is also a StoredSigMethodDesc, and a NoMetadataMethod + public virtual bool IsDynamicMethod(MethodDescHandle methodDesc); + + // Return true if a MethodDesc represents an IL Stub dynamically generated by the runtime + // A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod + public virtual bool IsILStub(MethodDescHandle methodDesc); } ``` @@ -563,7 +602,8 @@ The version 1 `MethodDesc` APIs depend on the `MethodDescAlignment` global and t | 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. +| `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. | +| `MethodDescTokenRemainderBitCount` | Number of bits in the token remainder in `MethodDesc` | 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 @@ -572,12 +612,211 @@ will typically have multiple chunks. There are subkinds of MethodDescs at runti 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. +| `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 | +| `MethodDesc` | `Flags3AndTokenRemainder` | More flags for the method, and the low bits of the method's token's RID | +| `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. | +| `MethodDescChunk` | `FlagsAndTokenRange` | `MethodDescChunk` flags, and the upper bits of the method token's RID | +| `InstantiatedMethodDesc` | `PerInstInfo` | The pointer to the method's type arguments | +| `InstantiatedMethodDesc` | `Flags2` | Flags for the `InstantiatedMethodDesc` | +| `InstantiatedMethodDesc` | `NumGenericArgs` | How many generic args the method has | +| `StoredSigMethodDesc` | `Sig` | Pointer to a metadata signature | +| `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature | +| `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` | +| `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc | + + +And the following enumeration definitions + +```csharp + internal enum MethodDescClassification + { + IL = 0, // IL + FCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor) + PInvoke = 2, // PInvoke method + EEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke) + Array = 4, // Array ECall + Instantiated = 5, // Instantiated generic methods, including descriptors + // for both shared and unshared code (see InstantiatedMethodDesc) + ComInterop = 6, + Dynamic = 7, // for method desc with no metadata behind + } + + [Flags] + internal enum MethodDescFlags : ushort + { + ClassificationMask = 0x7, + HasNonVtableSlot = 0x0008, + } + + internal enum InstantiatedMethodDescFlags2 : ushort + { + KindMask = 0x07, + GenericMethodDefinition = 0x01, + UnsharedMethodInstantiation = 0x02, + SharedMethodInstantiation = 0x03, + WrapperStubWithInstantiations = 0x04, + } + + [Flags] + internal enum DynamicMethodDescExtendedFlags : uint + { + IsLCGMethod = 0x00004000, + IsILStub = 0x00008000, + } +``` + + +And the various apis are implemented with the following algorithms + +```csharp + public bool IsGenericMethodDefinition(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodDescClassification.Instantiated) + return false; + + ushort Flags2 = // Read Flags2 field from InstantiatedMethodDesc contract using address methodDescHandle.Address + + return ((int)Flags2 & (int)InstantiatedMethodDescFlags2.KindMask) == (int)InstantiatedMethodDescFlags2.GenericMethodDefinition; + } + + public ReadOnlySpan GetGenericMethodInstantiation(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodDescClassification.Instantiated) + return default; + + TargetPointer dictionaryPointer = // Read PerInstInfo field from InstantiatedMethodDesc contract using address methodDescHandle.Address + if (dictionaryPointer == 0) + return default; + + int NumTypeArgs = // Read NumGenericArgs from methodDescHandle.Address using InstantiatedMethodDesc 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 uint GetMethodToken(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + TargetPointer methodDescChunk = // Using ChunkIndex from methodDesc, compute the wrapping MethodDescChunk + + ushort Flags3AndTokenRemainder = // Read Flags3AndTokenRemainder field from MethodDesc contract using address methodDescHandle.Address + + ushort FlagsAndTokenRange = // Read FlagsAndTokenRange field from MethodDescChunk contract using address methodDescChunk + int tokenRemainderBitCount = _target.ReadGlobal(Constants.Globals.MethodDescTokenRemainderBitCount); + int tokenRangeBitCount = 24 - tokenRemainderBitCount; + uint allRidBitsSet = 0xFFFFFF; + uint tokenRemainderMask = allRidBitsSet >> tokenRangeBitCount; + uint tokenRangeMask = allRidBitsSet >> tokenRemainderBitCount; + + uint tokenRemainder = (uint)(_desc.Flags3AndTokenRemainder & tokenRemainderMask); + uint tokenRange = ((uint)(_chunk.FlagsAndTokenRange & tokenRangeMask)) << tokenRemainderBitCount; + + return 0x06000000 | tokenRange | tokenRemainder; + } + + public bool IsArrayMethod(MethodDescHandle methodDescHandle, out ArrayFunctionType functionType) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodDescClassification.Array) + { + functionType = default; + return false; + } + + int arrayMethodIndex = methodDesc.Slot - GetNumVtableSlots(GetTypeHandle(methodDesc.MethodTable)); + + functionType = arrayMethodIndex switch + { + 0 => ArrayFunctionType.Get, + 1 => ArrayFunctionType.Set, + 2 => ArrayFunctionType.Address, + > 3 => ArrayFunctionType.Constructor, + _ => throw new InvalidOperationException() + }; + + return true; + } + + public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out ReadOnlySpan methodName) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodDescClassification.Dynamic) + { + methodName = default; + return false; + } + + TargetPointer methodNamePointer = // Read MethodName field from DynamicMethodDesc contract using address methodDescHandle.Address + + methodName = // ReadBuffer from target of a utf8 null terminated string, starting at address methodNamePointer + return true; + } + + public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + switch (methodDesc.Classification) + { + case MethodDescClassification.Dynamic: + case MethodDescClassification.EEImpl: + case MethodDescClassification.Array: + break; // These have stored sigs + + default: + signature = default; + return false; + } + + TargetPointer Sig = // Read Sig field from StoredSigMethodDesc contract using address methodDescHandle.Address + uint cSig = // Read cSig field from StoredSigMethodDesc contract using address methodDescHandle.Address + + TargetPointer methodNamePointer = // Read S field from DynamicMethodDesc contract using address methodDescHandle.Address + signature = // Read buffer from target memory starting at address Sig, with cSig bytes in it. + return true; + } + + public bool IsDynamicMethod(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodDescClassification.Dynamic) + { + return false; + } + + uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address + + return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod); + } + + public bool IsILStub(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodDescClassification.Dynamic) + { + return false; + } + + uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address + + return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsILStub); + } +``` **TODO(cdac)** diff --git a/src/coreclr/debug/daccess/dacfn.cpp b/src/coreclr/debug/daccess/dacfn.cpp index 2c9f28481d218e..07cb785ee4ae10 100644 --- a/src/coreclr/debug/daccess/dacfn.cpp +++ b/src/coreclr/debug/daccess/dacfn.cpp @@ -1386,6 +1386,15 @@ DacAllocHostOnlyInstance(ULONG32 size, bool throwEx) return inst + 1; } +thread_local bool t_DacAssertsUnconditionally = false; + +bool DacSetEnableDacAssertsUnconditionally(bool enable) +{ + bool oldValue = t_DacAssertsUnconditionally; + t_DacAssertsUnconditionally = enable; + return oldValue; +} + // // Queries whether ASSERTs should be raised when inconsistencies in the target are detected // @@ -1404,6 +1413,10 @@ bool DacTargetConsistencyAssertsEnabled() return true; } + // If asserts are unconditionally enabled via the thread local, simply return true. + if (t_DacAssertsUnconditionally) + return true; + return g_dacImpl->TargetConsistencyAssertsEnabled(); } diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h index 70e8845f6b9023..d09dab39dccc65 100644 --- a/src/coreclr/debug/daccess/dacimpl.h +++ b/src/coreclr/debug/daccess/dacimpl.h @@ -1245,6 +1245,7 @@ class ClrDataAccess HRESULT GetObjectStringDataImpl(CLRDATA_ADDRESS obj, unsigned int count, _Inout_updates_z_(count) WCHAR *stringData, unsigned int *pNeeded); HRESULT GetUsefulGlobalsImpl(struct DacpUsefulGlobalsData *globalsData); HRESULT GetMethodDescDataImpl(CLRDATA_ADDRESS methodDesc, CLRDATA_ADDRESS ip, struct DacpMethodDescData *data, ULONG cRevertedRejitVersions, DacpReJitData * rgRevertedRejitData, ULONG * pcNeededRevertedRejitData); + HRESULT GetMethodDescNameImpl(CLRDATA_ADDRESS methodDesc, unsigned int count, _Inout_updates_z_(count) WCHAR *name, unsigned int *pNeeded); BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord); #ifndef TARGET_UNIX diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 50a45522968c66..58fa58148f99ef 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -317,6 +317,7 @@ HRESULT ClrDataAccess::GetThreadStoreData(struct DacpThreadStoreData *threadStor // Assert that the data is the same as what we get from the DAC. DacpThreadStoreData threadStoreDataLocal; HRESULT hrLocal = GetThreadStoreDataImpl(&threadStoreDataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(threadStoreData->threadCount == threadStoreDataLocal.threadCount); _ASSERTE(threadStoreData->unstartedThreadCount == threadStoreDataLocal.unstartedThreadCount); @@ -895,6 +896,7 @@ HRESULT ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThre { DacpThreadData threadDataLocal; HRESULT hrLocal = GetThreadDataImpl(threadAddr, &threadDataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(threadData->corThreadId == threadDataLocal.corThreadId); _ASSERTE(threadData->osThreadId == threadDataLocal.osThreadId); @@ -1080,6 +1082,7 @@ HRESULT ClrDataAccess::GetMethodDescData( pcNeededRevertedRejitDataLocal = &cNeededRevertedRejitDataLocal; } HRESULT hrLocal = GetMethodDescDataImpl(methodDesc, ip,&mdDataLocal, cRevertedRejitVersions, rgRevertedRejitDataLocal, pcNeededRevertedRejitDataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(methodDescData->bHasNativeCode == mdDataLocal.bHasNativeCode); _ASSERTE(methodDescData->bIsDynamic == mdDataLocal.bIsDynamic); @@ -1581,6 +1584,51 @@ ClrDataAccess::GetMethodDescName(CLRDATA_ADDRESS methodDesc, unsigned int count, SOSDacEnter(); + if (m_cdacSos != NULL) + { + hr = m_cdacSos->GetMethodDescName(methodDesc, count, name, pNeeded); + if (FAILED(hr)) + { + hr = GetMethodDescNameImpl(methodDesc, count, name, pNeeded); + } +#ifdef _DEBUG + else + { + NewArrayHolder nameLocal = new WCHAR[count]; + unsigned int neededLocal = 0; + HRESULT hrLocal = GetMethodDescNameImpl(methodDesc, count, nameLocal, &neededLocal); + + DacAssertsEnabledHolder assertsEnabled; + _ASSERTE(hr == hrLocal); + + if (name != NULL) + { + _ASSERTE(0 == u16_strncmp(name, (WCHAR *)nameLocal, count)); + } + if (pNeeded != NULL) + { + _ASSERTE(*pNeeded == neededLocal); + } + } +#endif + } + else + { + hr = GetMethodDescNameImpl(methodDesc, count, name, pNeeded); + } + + SOSDacLeave(); + return hr; +} + +HRESULT +ClrDataAccess::GetMethodDescNameImpl(CLRDATA_ADDRESS methodDesc, unsigned int count, _Inout_updates_z_(count) WCHAR *name, unsigned int *pNeeded) +{ + if (methodDesc == 0) + return E_INVALIDARG; + + SOSDacEnter(); + MethodDesc* pMD = PTR_MethodDesc(TO_TADDR(methodDesc)); StackSString str; @@ -1701,6 +1749,7 @@ ClrDataAccess::GetObjectStringData(CLRDATA_ADDRESS obj, unsigned int count, _Ino unsigned int neededLocal; SString stringDataLocal; HRESULT hrLocal = GetObjectStringDataImpl(obj, count, stringDataLocal.OpenUnicodeBuffer(count), &neededLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(pNeeded == NULL || *pNeeded == neededLocal); _ASSERTE(u16_strncmp(stringData, stringDataLocal, count) == 0); @@ -1930,6 +1979,7 @@ ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData* module { DacpModuleData moduleDataLocal; HRESULT hrLocal = GetModuleDataImpl(addr, &moduleDataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(moduleData->Address == moduleDataLocal.Address); _ASSERTE(moduleData->PEAssembly == moduleDataLocal.PEAssembly); @@ -2047,6 +2097,7 @@ ClrDataAccess::GetMethodTableData(CLRDATA_ADDRESS mt, struct DacpMethodTableData // Assert that the data is the same as what we get from the DAC. DacpMethodTableData mtDataLocal; HRESULT hrLocal = GetMethodTableDataImpl(mt, &mtDataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(MTData->BaseSize == mtDataLocal.BaseSize); _ASSERTE(MTData->ComponentSize == mtDataLocal.ComponentSize); @@ -2136,6 +2187,7 @@ ClrDataAccess::GetMethodTableName(CLRDATA_ADDRESS mt, unsigned int count, _Inout NewArrayHolder pwszNameLocal(new WCHAR[count]); unsigned int neededLocal = 0; HRESULT hrLocal = GetMethodTableNameImpl(mt, count, mtName != NULL ? (WCHAR *)pwszNameLocal : NULL, pNeeded != NULL ? &neededLocal : NULL); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); if (mtName != NULL) @@ -2415,6 +2467,7 @@ ClrDataAccess::GetMethodTableForEEClass(CLRDATA_ADDRESS eeClassReallyCanonMT, CL // Assert that the data is the same as what we get from the DAC. CLRDATA_ADDRESS valueLocal; HRESULT hrLocal = GetMethodTableForEEClassImpl(eeClassReallyCanonMT, &valueLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(*value == valueLocal); } @@ -2578,6 +2631,7 @@ ClrDataAccess::GetObjectData(CLRDATA_ADDRESS addr, struct DacpObjectData* object { DacpObjectData objectDataLocal; HRESULT hrLocal = GetObjectDataImpl(addr, &objectDataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(objectData->MethodTable == objectDataLocal.MethodTable); _ASSERTE(objectData->ObjectType == objectDataLocal.ObjectType); @@ -3586,6 +3640,7 @@ ClrDataAccess::GetUsefulGlobals(struct DacpUsefulGlobalsData* globalsData) // Assert that the data is the same as what we get from the DAC. DacpUsefulGlobalsData globalsDataLocal; HRESULT hrLocal = GetUsefulGlobalsImpl(&globalsDataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(globalsData->ArrayMethodTable == globalsDataLocal.ArrayMethodTable); _ASSERTE(globalsData->StringMethodTable == globalsDataLocal.StringMethodTable); @@ -3644,6 +3699,7 @@ ClrDataAccess::GetNestedExceptionData(CLRDATA_ADDRESS exception, CLRDATA_ADDRESS CLRDATA_ADDRESS exceptionObjectLocal; CLRDATA_ADDRESS nextNestedExceptionLocal; HRESULT hrLocal = GetNestedExceptionDataImpl(exception, &exceptionObjectLocal, &nextNestedExceptionLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(*exceptionObject == exceptionObjectLocal); _ASSERTE(*nextNestedException == nextNestedExceptionLocal); @@ -4965,6 +5021,7 @@ HRESULT ClrDataAccess::GetObjectExceptionData(CLRDATA_ADDRESS objAddr, struct Da { DacpExceptionObjectData dataLocal; HRESULT hrLocal = GetObjectExceptionDataImpl(objAddr, &dataLocal); + DacAssertsEnabledHolder assertsEnabled; _ASSERTE(hr == hrLocal); _ASSERTE(data->Message == dataLocal.Message); _ASSERTE(data->InnerException == dataLocal.InnerException); diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index dc7c85c1f055c5..6eff38e5f4621a 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -301,6 +301,7 @@ CDAC_TYPE_INDETERMINATE(MethodDesc) CDAC_TYPE_FIELD(MethodDesc, /*uint8*/, ChunkIndex, cdac_data::ChunkIndex) CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Slot, cdac_data::Slot) CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags, cdac_data::Flags) +CDAC_TYPE_FIELD(MethodDesc, /*uint16*/, Flags3AndTokenRemainder, cdac_data::Flags3AndTokenRemainder) CDAC_TYPE_END(MethodDesc) CDAC_TYPE_BEGIN(MethodDescChunk) @@ -309,8 +310,28 @@ CDAC_TYPE_FIELD(MethodDescChunk, /*pointer*/, MethodTable, cdac_data::Next) CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Size, cdac_data::Size) CDAC_TYPE_FIELD(MethodDescChunk, /*uint8*/, Count, cdac_data::Count) +CDAC_TYPE_FIELD(MethodDescChunk, /*uint16*/, FlagsAndTokenRange, cdac_data::FlagsAndTokenRange) CDAC_TYPE_END(MethodDescChunk) +CDAC_TYPE_BEGIN(InstantiatedMethodDesc) +CDAC_TYPE_INDETERMINATE(InstantiatedMethodDesc) +CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*pointer*/, PerInstInfo, cdac_data::PerInstInfo) +CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*uint16*/, Flags2, cdac_data::Flags2) +CDAC_TYPE_FIELD(InstantiatedMethodDesc, /*uint16*/, NumGenericArgs, cdac_data::NumGenericArgs) +CDAC_TYPE_END(InstantiatedMethodDesc) + +CDAC_TYPE_BEGIN(StoredSigMethodDesc) +CDAC_TYPE_INDETERMINATE(StoredSigMethodDesc) +CDAC_TYPE_FIELD(StoredSigMethodDesc, /*pointer*/, Sig, cdac_data::Sig) +CDAC_TYPE_FIELD(StoredSigMethodDesc, /*uint32*/, cSig, cdac_data::cSig) +CDAC_TYPE_FIELD(StoredSigMethodDesc, /*uint32*/, ExtendedFlags, cdac_data::ExtendedFlags) +CDAC_TYPE_END(StoredSigMethodDesc) + +CDAC_TYPE_BEGIN(DynamicMethodDesc) +CDAC_TYPE_INDETERMINATE(DynamicMethodDesc) +CDAC_TYPE_FIELD(DynamicMethodDesc, /*pointer*/, MethodName, cdac_data::MethodName) +CDAC_TYPE_END(DynamicMethodDesc) + CDAC_TYPES_END() CDAC_GLOBALS_BEGIN() @@ -318,6 +339,7 @@ CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain) CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore) CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread) CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread) +CDAC_GLOBAL(MethodDescTokenRemainderBitCount, uint8, METHOD_TOKEN_REMAINDER_BIT_COUNT) #if FEATURE_EH_FUNCLETS CDAC_GLOBAL(FeatureEHFunclets, uint8, 1) #else @@ -335,6 +357,7 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1 | 1 << 2) CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1) #endif //TARGET_64BIT CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION) +CDAC_GLOBAL(DirectorySeparator, uint8, (uint8_t)DIRECTORY_SEPARATOR_CHAR_A) CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT) CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE) CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data::SyncBlockValue) diff --git a/src/coreclr/inc/daccess.h b/src/coreclr/inc/daccess.h index dc9b02daa1cda8..74aeed1c9bb067 100644 --- a/src/coreclr/inc/daccess.h +++ b/src/coreclr/inc/daccess.h @@ -733,6 +733,27 @@ PVOID DacAllocHostOnlyInstance(ULONG32 size, bool throwEx); // Determines whether ASSERTs should be raised when inconsistencies in the target are detected bool DacTargetConsistencyAssertsEnabled(); +// Sets whether ASSERTs should be raised when then fail. +// Returns the previous value +bool DacSetEnableDacAssertsUnconditionally(bool enable); + +class DacAssertsEnabledHolder +{ +#ifdef _DEBUG + bool m_fOldValue; +public: + DacAssertsEnabledHolder() + { + m_fOldValue = DacSetEnableDacAssertsUnconditionally(true); + } + + ~DacAssertsEnabledHolder() + { + DacSetEnableDacAssertsUnconditionally(m_fOldValue); + } +#endif // _DEBUG +}; + // Host instances can be marked as they are enumerated in // order to break cycles. This function returns true if // the instance is already marked, otherwise it marks the diff --git a/src/coreclr/vm/classcompat.cpp b/src/coreclr/vm/classcompat.cpp index ece9384703b2b8..f2aa54a9e10ca1 100644 --- a/src/coreclr/vm/classcompat.cpp +++ b/src/coreclr/vm/classcompat.cpp @@ -2705,13 +2705,13 @@ VOID MethodTableBuilder::EnumerateClassMethods() if (dwMethodRVA == 0) Classification = mcFCall; else - Classification = mcNDirect; + Classification = mcPInvoke; } // The NAT_L attribute is present, marking this method as NDirect else { CONSISTENCY_CHECK(hr == S_OK); - Classification = mcNDirect; + Classification = mcPInvoke; } } else if (IsMiRuntime(dwImplFlags)) @@ -2840,7 +2840,7 @@ VOID MethodTableBuilder::EnumerateClassMethods() } BYTE type; - if ((Classification & mdfClassification) == mcNDirect) + if ((Classification & mdfClassification) == mcPInvoke) { type = METHOD_TYPE_NDIRECT; } diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index d8313fdc965162..35c06ac2a79004 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -68,7 +68,7 @@ static_assert_no_msg((sizeof(DynamicMethodDesc) & MethodDesc::ALIGNMENT_MASK #define METHOD_DESC_SIZES(adjustment) \ adjustment + sizeof(MethodDesc), /* mcIL */ \ adjustment + sizeof(FCallMethodDesc), /* mcFCall */ \ - adjustment + sizeof(NDirectMethodDesc), /* mcNDirect */ \ + adjustment + sizeof(NDirectMethodDesc), /* mcPInvoke */ \ adjustment + sizeof(EEImplMethodDesc), /* mcEEImpl */ \ adjustment + sizeof(ArrayMethodDesc), /* mcArray */ \ adjustment + sizeof(InstantiatedMethodDesc), /* mcInstantiated */ \ @@ -2539,7 +2539,7 @@ BOOL MethodDesc::MayHaveNativeCode() break; case mcFCall: // FCalls do not have real native code. return FALSE; - case mcNDirect: // NDirect never have native code (note that the NDirect method + case mcPInvoke: // NDirect never have native code (note that the NDirect method return FALSE; // does not appear as having a native code even for stubs as IL) case mcEEImpl: // Runtime provided implementation. No native code. return FALSE; diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 628eb39864b7e7..2e275c2656dbd2 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -93,7 +93,7 @@ enum MethodClassification { mcIL = 0, // IL mcFCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor) - mcNDirect = 2, // N/Direct + mcPInvoke = 2, // PInvoke method also known as N/Direct in this codebase mcEEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke) mcArray = 4, // Array ECall mcInstantiated = 5, // Instantiated generic methods, including descriptors @@ -637,7 +637,7 @@ class MethodDesc inline DWORD IsNDirect() { LIMITED_METHOD_DAC_CONTRACT; - return mcNDirect == GetClassification(); + return mcPInvoke == GetClassification(); } inline DWORD IsInterface() @@ -1917,6 +1917,7 @@ template<> struct cdac_data 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); + static constexpr size_t Flags3AndTokenRemainder = offsetof(MethodDesc, m_wFlags3AndTokenRemainder); }; #ifndef DACCESS_COMPILE @@ -2348,6 +2349,7 @@ struct cdac_data 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); + static constexpr size_t FlagsAndTokenRange = offsetof(MethodDescChunk, m_flagsAndTokenRange); }; inline int MethodDesc::GetMethodDescChunkIndex() const @@ -2424,6 +2426,15 @@ class StoredSigMethodDesc : public MethodDesc #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); #endif + template friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t Sig = offsetof(StoredSigMethodDesc, m_pSig); + static constexpr size_t cSig = offsetof(StoredSigMethodDesc, m_cSig); + static constexpr size_t ExtendedFlags = offsetof(StoredSigMethodDesc, m_dwExtendedFlags); }; //----------------------------------------------------------------------- @@ -2665,8 +2676,14 @@ class DynamicMethodDesc : public StoredSigMethodDesc // following implementations defined in DynamicMethod.cpp // void Destroy(); + template friend struct ::cdac_data; }; +template<> +struct cdac_data +{ + static constexpr size_t MethodName = offsetof(DynamicMethodDesc, m_pszMethodName); +}; class ArrayMethodDesc : public StoredSigMethodDesc { @@ -3418,7 +3435,14 @@ class InstantiatedMethodDesc final : public MethodDesc MethodDesc* pSharedMDescForStub, Instantiation methodInst, BOOL getSharedNotStub); + template friend struct ::cdac_data; +}; +template<> struct cdac_data +{ + static constexpr size_t PerInstInfo = offsetof(InstantiatedMethodDesc, m_pPerInstInfo); + static constexpr size_t Flags2 = offsetof(InstantiatedMethodDesc, m_wFlags2); + static constexpr size_t NumGenericArgs = offsetof(InstantiatedMethodDesc, m_wNumGenericArgs); }; inline PTR_MethodTable MethodDesc::GetMethodTable() const diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index 70d4ea47affc52..bcc88d55b8cc30 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -3183,14 +3183,14 @@ MethodTableBuilder::EnumerateClassMethods() } else { - type = mcNDirect; + type = mcPInvoke; } } // The NAT_L attribute is present, marking this method as NDirect else { CONSISTENCY_CHECK(hr == S_OK); - type = mcNDirect; + type = mcPInvoke; } } else if (IsMiRuntime(dwImplFlags)) @@ -5093,7 +5093,7 @@ MethodTableBuilder::ValidateMethods() { BuildMethodTableThrowException(IDS_CLASSLOAD_BAD_UNMANAGED_RVA, it.Token()); } - if (it.MethodType() != mcNDirect) + if (it.MethodType() != mcPInvoke) { BuildMethodTableThrowException(BFA_BAD_UNMANAGED_ENTRY_POINT); } @@ -6099,7 +6099,7 @@ MethodTableBuilder::InitMethodDesc( switch (Classification) { - case mcNDirect: + case mcPInvoke: { // NDirect specific initialization. NDirectMethodDesc *pNewNMD = (NDirectMethodDesc*)pNewMD; diff --git a/src/coreclr/vm/typestring.h b/src/coreclr/vm/typestring.h index 869039ffe5d703..f3bd23bfc6707b 100644 --- a/src/coreclr/vm/typestring.h +++ b/src/coreclr/vm/typestring.h @@ -184,7 +184,7 @@ class TypeString // Append a representation of the method m to the string s // The following flags in the FormatFlags argument are significant: FormatNamespace FormatFullInst FormatAssembly FormatSignature FormatNoVersion - static void AppendMethodInternal(SString& s, MethodDesc *pMD, const DWORD format = FormatNamespace|FormatSignature|FormatStubInfo); + static void AppendMethodInternal(SString& s, MethodDesc *pMD, const DWORD format); // Append the field name and generic instantiation info. static void AppendField(SString& s, FieldDesc *pFD, Instantiation typeInstantiation, const DWORD format = FormatNamespace); diff --git a/src/native/managed/cdacreader/src/Constants.cs b/src/native/managed/cdacreader/src/Constants.cs index bd29ab0aeb8d92..5353fed7503444 100644 --- a/src/native/managed/cdacreader/src/Constants.cs +++ b/src/native/managed/cdacreader/src/Constants.cs @@ -35,5 +35,8 @@ internal static class Globals internal const string SyncTableEntries = nameof(SyncTableEntries); internal const string ArrayBoundsZero = nameof(ArrayBoundsZero); + + internal const string MethodDescTokenRemainderBitCount = nameof(MethodDescTokenRemainderBitCount); + internal const string DirectorySeparator = nameof(DirectorySeparator); } } diff --git a/src/native/managed/cdacreader/src/Contracts/Loader.cs b/src/native/managed/cdacreader/src/Contracts/Loader.cs index dbcd9bd37cceeb..4839e6d495c9b7 100644 --- a/src/native/managed/cdacreader/src/Contracts/Loader.cs +++ b/src/native/managed/cdacreader/src/Contracts/Loader.cs @@ -131,6 +131,8 @@ static IContract IContract.Create(Target target, int version) public virtual TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) => throw new NotImplementedException(); public virtual ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException(); + + public virtual string GetPath(ModuleHandle handle) => throw new NotImplementedException(); } internal readonly struct Loader : ILoader diff --git a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs index 8cb8a2c8675c57..5a87fe72f1535a 100644 --- a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs +++ b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem.cs @@ -65,6 +65,14 @@ internal MethodDescHandle(TargetPointer address) internal TargetPointer Address { get; } } +public enum ArrayFunctionType +{ + Get = 0, + Set = 1, + Address = 2, + Constructor = 3 +} + internal interface IRuntimeTypeSystem : IContract { static string IContract.Name => nameof(RuntimeTypeSystem); @@ -128,6 +136,34 @@ static IContract IContract.Create(Target target, int version) #region MethodDesc inspection APIs public virtual MethodDescHandle GetMethodDescHandle(TargetPointer targetPointer) => throw new NotImplementedException(); public virtual TargetPointer GetMethodTable(MethodDescHandle methodDesc) => throw new NotImplementedException(); + + // Return true for an uninstantiated generic method + public virtual bool IsGenericMethodDefinition(MethodDescHandle methodDesc) => throw new NotImplementedException(); + public virtual ReadOnlySpan GetGenericMethodInstantiation(MethodDescHandle methodDesc) => throw new NotImplementedException(); + + // Return mdTokenNil (0x06000000) if the method doesn't have a token, otherwise return the token of the method + public virtual uint GetMethodToken(MethodDescHandle methodDesc) => throw new NotImplementedException(); + + // Return true if a MethodDesc represents an array method + // An array method is also a StoredSigMethodDesc + public virtual bool IsArrayMethod(MethodDescHandle methodDesc, out ArrayFunctionType functionType) => throw new NotImplementedException(); + + // Return true if a MethodDesc represents a method without metadata method, either an IL Stub dynamically + // generated by the runtime, or a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class + // Or something else similar. + // A no metadata method is also a StoredSigMethodDesc + public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out ReadOnlySpan methodName) => throw new NotImplementedException(); + // A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata. + public virtual bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan signature) => throw new NotImplementedException(); + + // Return true for a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class + // A DynamicMethod is also a StoredSigMethodDesc, and a NoMetadataMethod + public virtual bool IsDynamicMethod(MethodDescHandle methodDesc) => throw new NotImplementedException(); + + // Return true if a MethodDesc represents an IL Stub dynamically generated by the runtime + // A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod + public virtual bool IsILStub(MethodDescHandle methodDesc) => throw new NotImplementedException(); + #endregion MethodDesc inspection APIs } diff --git a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs index 8a690f6a79cf36..cc15327ae16a71 100644 --- a/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_1.cs @@ -7,6 +7,8 @@ using Microsoft.Diagnostics.DataContractReader.Data; using Microsoft.Diagnostics.DataContractReader.Contracts.RuntimeTypeSystem_1_NS; using System.Diagnostics; +using System.Text; +using System.Reflection; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -71,26 +73,173 @@ internal enum TypeHandleBits ValidMask = 2, } + internal enum MethodClassification + { + IL = 0, // IL + FCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor) + PInvoke = 2, // PInvoke Method + EEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke) + Array = 4, // Array ECall + Instantiated = 5, // Instantiated generic methods, including descriptors + // for both shared and unshared code (see InstantiatedMethodDesc) + ComInterop = 6, + Dynamic = 7, // for method desc with no metadata behind + } + [Flags] internal enum MethodDescFlags : ushort { + ClassificationMask = 0x7, HasNonVtableSlot = 0x0008, } + internal enum InstantiatedMethodDescFlags2 : ushort + { + KindMask = 0x07, + GenericMethodDefinition = 0x01, + UnsharedMethodInstantiation = 0x02, + SharedMethodInstantiation = 0x03, + WrapperStubWithInstantiations = 0x04, + } + + [Flags] + internal enum DynamicMethodDescExtendedFlags : uint + { + IsLCGMethod = 0x00004000, + IsILStub = 0x00008000, + } + internal struct MethodDesc { private readonly Data.MethodDesc _desc; private readonly Data.MethodDescChunk _chunk; + private readonly Target _target; + internal TargetPointer Address { get; init; } - internal MethodDesc(TargetPointer methodDescPointer, Data.MethodDesc desc, Data.MethodDescChunk chunk) + internal MethodDesc(Target target, TargetPointer methodDescPointer, Data.MethodDesc desc, Data.MethodDescChunk chunk) { + _target = target; _desc = desc; _chunk = chunk; Address = methodDescPointer; + + Token = ComputeToken(target, desc, chunk); } public TargetPointer MethodTable => _chunk.MethodTable; public ushort Slot => _desc.Slot; + public uint Token { get; } + + private static uint ComputeToken(Target target, Data.MethodDesc desc, Data.MethodDescChunk chunk) + { + int tokenRemainderBitCount = target.ReadGlobal(Constants.Globals.MethodDescTokenRemainderBitCount); + int tokenRangeBitCount = 24 - tokenRemainderBitCount; + uint allRidBitsSet = 0xFFFFFF; + uint tokenRemainderMask = allRidBitsSet >> tokenRangeBitCount; + uint tokenRangeMask = allRidBitsSet >> tokenRemainderBitCount; + + uint tokenRemainder = (uint)(desc.Flags3AndTokenRemainder & tokenRemainderMask); + uint tokenRange = ((uint)(chunk.FlagsAndTokenRange & tokenRangeMask)) << tokenRemainderBitCount; + + return 0x06000000 | tokenRange | tokenRemainder; + } + + public MethodClassification Classification => (MethodClassification)((int)_desc.Flags & (int)MethodDescFlags.ClassificationMask); + } + + private class InstantiatedMethodDesc : IData + { + public static InstantiatedMethodDesc Create(Target target, TargetPointer address) => new InstantiatedMethodDesc(target, address); + + private readonly TargetPointer _address; + private readonly Data.InstantiatedMethodDesc _desc; + + private InstantiatedMethodDesc(Target target, TargetPointer methodDescPointer) + { + _address = methodDescPointer; + RuntimeTypeSystem_1 rts = (RuntimeTypeSystem_1)target.Contracts.RuntimeTypeSystem; + _desc = target.ProcessedData.GetOrAdd(methodDescPointer); + + int numGenericArgs = _desc.NumGenericArgs; + TargetPointer perInstInfo = _desc.PerInstInfo; + if ((perInstInfo == TargetPointer.Null) || (numGenericArgs == 0)) + { + Instantiation = System.Array.Empty(); + } + else + { + Instantiation = new TypeHandle[numGenericArgs]; + for (int i = 0; i < numGenericArgs; i++) + { + Instantiation[i] = rts.GetTypeHandle(target.ReadPointer(perInstInfo + (ulong)target.PointerSize * (ulong)i)); + } + } + } + + private bool HasFlags(InstantiatedMethodDescFlags2 mask, InstantiatedMethodDescFlags2 flags) => (_desc.Flags2 & (ushort)mask) == (ushort)flags; + internal bool IsWrapperStubWithInstantiations => HasFlags(InstantiatedMethodDescFlags2.KindMask, InstantiatedMethodDescFlags2.WrapperStubWithInstantiations); + internal bool IsGenericMethodDefinition => HasFlags(InstantiatedMethodDescFlags2.KindMask, InstantiatedMethodDescFlags2.GenericMethodDefinition); + internal bool HasPerInstInfo => _desc.PerInstInfo != TargetPointer.Null; + internal bool HasMethodInstantiation => IsGenericMethodDefinition || HasPerInstInfo; + public TypeHandle[] Instantiation { get; } + } + + private class DynamicMethodDesc : IData + { + public static DynamicMethodDesc Create(Target target, TargetPointer address) => new DynamicMethodDesc(target, address); + + private readonly TargetPointer _address; + private readonly Data.DynamicMethodDesc _desc; + private readonly Data.StoredSigMethodDesc _storedSigDesc; + + private DynamicMethodDesc(Target target, TargetPointer methodDescPointer) + { + _address = methodDescPointer; + List nameBytes = new(); + _desc = target.ProcessedData.GetOrAdd(methodDescPointer); + + if (_desc.MethodName != TargetPointer.Null) + { + TargetPointer currentNameAddress = _desc.MethodName; + do + { + byte nameByte = target.Read(currentNameAddress); + + if (nameByte == 0) + break; + + nameBytes.Add(nameByte); + currentNameAddress++; + } while (true); + + MethodName = nameBytes.ToArray(); + } + else + { + MethodName = System.Array.Empty(); + } + + _storedSigDesc = target.ProcessedData.GetOrAdd(methodDescPointer); + } + + public byte[] MethodName { get; } + public DynamicMethodDescExtendedFlags ExtendedFlags => (DynamicMethodDescExtendedFlags)_storedSigDesc.ExtendedFlags; + + public bool IsDynamicMethod => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod); + public bool IsILStub => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsILStub); + } + + private class StoredSigMethodDesc : IData + { + public static StoredSigMethodDesc Create(Target target, TargetPointer address) => new StoredSigMethodDesc(target, address); + + public byte[] Signature { get; } + private StoredSigMethodDesc(Target target, TargetPointer methodDescPointer) + { + Data.StoredSigMethodDesc storedSigMethodDesc = target.ProcessedData.GetOrAdd(methodDescPointer); + Signature = new byte[storedSigMethodDesc.cSig]; + target.ReadBuffer(storedSigMethodDesc.Sig, Signature.AsSpan()); + } } internal RuntimeTypeSystem_1(Target target, TargetPointer freeObjectMethodTablePointer, ulong methodDescAlignment) @@ -473,7 +622,7 @@ public MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer) { throw new InvalidOperationException("cached MethodDesc data but not its containing MethodDescChunk"); } - MethodDesc validatedMethodDesc = new MethodDesc(methodDescPointer, methodDescData, methodDescChunkData); + MethodDesc validatedMethodDesc = new MethodDesc(_target, methodDescPointer, methodDescData, methodDescChunkData); _ = _methodDescs.TryAdd(methodDescPointer, validatedMethodDesc); return new MethodDescHandle(methodDescPointer); } @@ -487,10 +636,137 @@ public MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer) Data.MethodDescChunk validatedMethodDescChunkData = _target.ProcessedData.GetOrAdd(methodDescChunkPointer); Data.MethodDesc validatedMethodDescData = _target.ProcessedData.GetOrAdd(methodDescPointer); - MethodDesc trustedMethodDescF = new MethodDesc(methodDescPointer, validatedMethodDescData, validatedMethodDescChunkData); + MethodDesc trustedMethodDescF = new MethodDesc(_target, methodDescPointer, validatedMethodDescData, validatedMethodDescChunkData); _ = _methodDescs.TryAdd(methodDescPointer, trustedMethodDescF); return new MethodDescHandle(methodDescPointer); } public TargetPointer GetMethodTable(MethodDescHandle methodDescHandle) => _methodDescs[methodDescHandle.Address].MethodTable; + + private InstantiatedMethodDesc AsInstantiatedMethodDesc(MethodDesc methodDesc) + { + Debug.Assert(methodDesc.Classification == MethodClassification.Instantiated); + return _target.ProcessedData.GetOrAdd(methodDesc.Address); + } + + private DynamicMethodDesc AsDynamicMethodDesc(MethodDesc methodDesc) + { + Debug.Assert(methodDesc.Classification == MethodClassification.Dynamic); + return _target.ProcessedData.GetOrAdd(methodDesc.Address); + } + + private StoredSigMethodDesc AsStoredSigMethodDesc(MethodDesc methodDesc) + { + Debug.Assert(methodDesc.Classification == MethodClassification.Dynamic || + methodDesc.Classification == MethodClassification.EEImpl || + methodDesc.Classification == MethodClassification.Array); + return _target.ProcessedData.GetOrAdd(methodDesc.Address); + } + + public bool IsGenericMethodDefinition(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodClassification.Instantiated) + return false; + return AsInstantiatedMethodDesc(methodDesc).IsGenericMethodDefinition; + } + + public ReadOnlySpan GetGenericMethodInstantiation(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodClassification.Instantiated) + return default; + + return AsInstantiatedMethodDesc(methodDesc).Instantiation; + } + + public uint GetMethodToken(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + return methodDesc.Token; + } + + public bool IsArrayMethod(MethodDescHandle methodDescHandle, out ArrayFunctionType functionType) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodClassification.Array) + { + functionType = default; + return false; + } + + int arrayMethodIndex = methodDesc.Slot - GetNumVtableSlots(GetTypeHandle(methodDesc.MethodTable)); + + functionType = arrayMethodIndex switch + { + 0 => ArrayFunctionType.Get, + 1 => ArrayFunctionType.Set, + 2 => ArrayFunctionType.Address, + > 3 => ArrayFunctionType.Constructor, + _ => throw new InvalidOperationException() + }; + + return true; + } + + public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out ReadOnlySpan methodName) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodClassification.Dynamic) + { + methodName = default; + return false; + } + + methodName = AsDynamicMethodDesc(methodDesc).MethodName; + return true; + } + + public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan signature) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + switch (methodDesc.Classification) + { + case MethodClassification.Dynamic: + case MethodClassification.EEImpl: + case MethodClassification.Array: + break; // These have stored sigs + + default: + signature = default; + return false; + } + + signature = AsStoredSigMethodDesc(methodDesc).Signature; + return true; + } + + public bool IsDynamicMethod(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodClassification.Dynamic) + { + return false; + } + + return AsDynamicMethodDesc(methodDesc).IsDynamicMethod; + } + + public bool IsILStub(MethodDescHandle methodDescHandle) + { + MethodDesc methodDesc = _methodDescs[methodDescHandle.Address]; + + if (methodDesc.Classification != MethodClassification.Dynamic) + { + return false; + } + + return AsDynamicMethodDesc(methodDesc).IsILStub; + } } diff --git a/src/native/managed/cdacreader/src/Data/MethodDesc.cs b/src/native/managed/cdacreader/src/Data/MethodDesc.cs index 7c4d6b9c8ebb37..d51153ab53e59d 100644 --- a/src/native/managed/cdacreader/src/Data/MethodDesc.cs +++ b/src/native/managed/cdacreader/src/Data/MethodDesc.cs @@ -15,9 +15,58 @@ public MethodDesc(Target target, TargetPointer address) ChunkIndex = target.Read(address + (ulong)type.Fields[nameof(ChunkIndex)].Offset); Slot = target.Read(address + (ulong)type.Fields[nameof(Slot)].Offset); Flags = target.Read(address + (ulong)type.Fields[nameof(Flags)].Offset); + Flags3AndTokenRemainder = target.Read(address + (ulong)type.Fields[nameof(Flags3AndTokenRemainder)].Offset); } public byte ChunkIndex { get; init; } public ushort Slot { get; init; } public ushort Flags { get; init; } + public ushort Flags3AndTokenRemainder { get; init; } +} + +internal sealed class InstantiatedMethodDesc : IData +{ + static InstantiatedMethodDesc IData.Create(Target target, TargetPointer address) => new InstantiatedMethodDesc(target, address); + public InstantiatedMethodDesc(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.InstantiatedMethodDesc); + + PerInstInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(PerInstInfo)].Offset); + NumGenericArgs = target.Read(address + (ulong)type.Fields[nameof(NumGenericArgs)].Offset); + Flags2 = target.Read(address + (ulong)type.Fields[nameof(Flags2)].Offset); + } + + public TargetPointer PerInstInfo { get; init; } + public ushort NumGenericArgs { get; init; } + public ushort Flags2 { get; init; } +} + +internal sealed class DynamicMethodDesc : IData +{ + static DynamicMethodDesc IData.Create(Target target, TargetPointer address) => new DynamicMethodDesc(target, address); + public DynamicMethodDesc(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.DynamicMethodDesc); + + MethodName = target.ReadPointer(address + (ulong)type.Fields[nameof(MethodName)].Offset); + } + + public TargetPointer MethodName { get; init; } +} + +internal sealed class StoredSigMethodDesc : IData +{ + static StoredSigMethodDesc IData.Create(Target target, TargetPointer address) => new StoredSigMethodDesc(target, address); + public StoredSigMethodDesc(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.StoredSigMethodDesc); + + Sig = target.ReadPointer(address + (ulong)type.Fields[nameof(Sig)].Offset); + cSig = target.Read(address + (ulong)type.Fields[nameof(cSig)].Offset); + ExtendedFlags = target.Read(address + (ulong)type.Fields[nameof(ExtendedFlags)].Offset); + } + + public TargetPointer Sig { get; init; } + public uint cSig { get; init; } + public uint ExtendedFlags { get; init; } } diff --git a/src/native/managed/cdacreader/src/Data/MethodDescChunk.cs b/src/native/managed/cdacreader/src/Data/MethodDescChunk.cs index 3d397a124df6d4..028050c40d9d42 100644 --- a/src/native/managed/cdacreader/src/Data/MethodDescChunk.cs +++ b/src/native/managed/cdacreader/src/Data/MethodDescChunk.cs @@ -16,10 +16,12 @@ public MethodDescChunk(Target target, TargetPointer address) Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset); Size = target.Read(address + (ulong)type.Fields[nameof(Size)].Offset); Count = target.Read(address + (ulong)type.Fields[nameof(Count)].Offset); + FlagsAndTokenRange = target.Read(address + (ulong)type.Fields[nameof(FlagsAndTokenRange)].Offset); } public TargetPointer MethodTable { get; init; } public TargetPointer Next { get; init; } public byte Size { get; init; } public byte Count { get; init; } + public ushort FlagsAndTokenRange { get; init; } } diff --git a/src/native/managed/cdacreader/src/DataType.cs b/src/native/managed/cdacreader/src/DataType.cs index a7efdf5b11f09a..3d9926eb4f505d 100644 --- a/src/native/managed/cdacreader/src/DataType.cs +++ b/src/native/managed/cdacreader/src/DataType.cs @@ -46,4 +46,7 @@ public enum DataType SyncBlock, SyncTableEntry, InteropSyncBlockInfo, + InstantiatedMethodDesc, + DynamicMethodDesc, + StoredSigMethodDesc, } diff --git a/src/native/managed/cdacreader/src/Helpers/EcmaMetadataReader.cs b/src/native/managed/cdacreader/src/Helpers/EcmaMetadataReader.cs index d358ccc15d5240..47a2a5d92ce639 100644 --- a/src/native/managed/cdacreader/src/Helpers/EcmaMetadataReader.cs +++ b/src/native/managed/cdacreader/src/Helpers/EcmaMetadataReader.cs @@ -278,7 +278,42 @@ public uint GetColumnAsConstant(EcmaMetadataCursor c, MetadataColumnIndex col_id public System.ReadOnlySpan GetColumnAsBlob(EcmaMetadataCursor c, MetadataColumnIndex col_idx) { - throw new NotImplementedException(); + if (columnTypes[(int)col_idx] != ColumnType.Blob) + throw new NotImplementedException(); + + uint rawResult = GetColumnRaw(c, col_idx); + if (rawResult == 0) + return default; + + checked + { + ReadOnlySpan blobHeap = _ecmaMetadata.BlobHeap.Span; + int curOffset = (int)rawResult; + + byte headerByte1 = blobHeap[curOffset]; + int size; + if ((headerByte1 & 0x80) == 0) + { + size = headerByte1; + } + else if ((headerByte1 & 0xC) == 0x80) + { + byte headerByte2 = blobHeap[++curOffset]; + size = headerByte1 & 0x3F << 8 + headerByte2; + } + else + { + byte headerByte2 = blobHeap[++curOffset]; + byte headerByte3 = blobHeap[++curOffset]; + byte headerByte4 = blobHeap[++curOffset]; + size = (headerByte1 & 0x3F << 24) + + (headerByte2 << 16) + + (headerByte3 << 8) + + headerByte4; + } + curOffset++; + return blobHeap.Slice(curOffset, size); + } } public uint GetColumnAsToken(EcmaMetadataCursor c, MetadataColumnIndex col_idx) diff --git a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs index 4bbef214ebb082..0e6062258de6a0 100644 --- a/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs @@ -3,8 +3,12 @@ using Microsoft.Diagnostics.DataContractReader.Contracts; using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; +using System.Text; namespace Microsoft.Diagnostics.DataContractReader.Legacy; @@ -112,7 +116,75 @@ public unsafe int GetMethodDescData(ulong methodDesc, ulong ip, DacpMethodDescDa } 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) + { + if (methodDesc == 0) + return HResults.E_INVALIDARG; + + int hr = HResults.S_OK; + if (pNeeded != null) + *pNeeded = 0; + try + { + StringBuilder stringBuilder = new StringBuilder(); + Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; + Contracts.MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(methodDesc); + try + { + TypeNameBuilder.AppendMethodInternal(_target, stringBuilder, methodDescHandle, TypeNameFormat.FormatSignature | TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst); + } + catch + { + hr = HResults.E_FAIL; + if (rtsContract.IsNoMetadataMethod(methodDescHandle, out _)) + { + // In heap dumps, trying to format the signature can fail + // in certain cases. + stringBuilder.Clear(); + TypeNameBuilder.AppendMethodInternal(_target, stringBuilder, methodDescHandle, TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst); + hr = HResults.S_OK; + } + else + { + string? fallbackNameString = _target.Contracts.DacStreams.StringFromEEAddress(methodDesc); + if (!string.IsNullOrEmpty(fallbackNameString)) + { + stringBuilder.Clear(); + stringBuilder.Append(fallbackNameString); + hr = HResults.S_OK; + } + else + { + TargetPointer modulePtr = rtsContract.GetModule(rtsContract.GetTypeHandle(rtsContract.GetMethodTable(methodDescHandle))); + Contracts.ModuleHandle module = _target.Contracts.Loader.GetModuleHandle(modulePtr); + string modulePath = _target.Contracts.Loader.GetPath(module); + ReadOnlySpan moduleSpan = modulePath.AsSpan(); + int pathNameSpanIndex = moduleSpan.LastIndexOf(_target.DirectorySeparator); + if (pathNameSpanIndex != -1) + { + moduleSpan = moduleSpan.Slice(pathNameSpanIndex + 1); + } + stringBuilder.Clear(); + stringBuilder.Append(moduleSpan); + stringBuilder.Append("!Unknown"); + hr = HResults.S_OK; + } + } + } + + if (hr == HResults.S_OK) + { + CopyStringToTargetBuffer(name, count, pNeeded, stringBuilder.ToString()); + } + } + catch (System.Exception ex) + { + return ex.HResult; + } + + return hr; + } + public unsafe int GetMethodDescPtrFromFrame(ulong frameAddr, ulong* ppMD) => HResults.E_NOTIMPL; public unsafe int GetMethodDescPtrFromIP(ulong ip, ulong* ppMD) => HResults.E_NOTIMPL; public unsafe int GetMethodDescTransparencyData(ulong methodDesc, void* data) => HResults.E_NOTIMPL; diff --git a/src/native/managed/cdacreader/src/Legacy/SigFormat.cs b/src/native/managed/cdacreader/src/Legacy/SigFormat.cs new file mode 100644 index 00000000000000..965bb7edf99e8a --- /dev/null +++ b/src/native/managed/cdacreader/src/Legacy/SigFormat.cs @@ -0,0 +1,469 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Microsoft.Diagnostics.DataContractReader.Contracts; +using Microsoft.Diagnostics.DataContractReader.Helpers; + +namespace Microsoft.Diagnostics.DataContractReader.Legacy +{ + internal static class SigFormat + { + private const uint IMAGE_CEE_CS_CALLCONV_MASK = 0xF; + private const uint IMAGE_CEE_CS_CALLCONV_VARARG = 0x5; + private const uint IMAGE_CEE_CS_CALLCONV_GENERIC = 0x10; + public static void AppendSigFormat(Target target, + StringBuilder stringBuilder, + ReadOnlySpan signature, + EcmaMetadataReader? metadata, + string? memberName, + string? className, + string? namespaceName, + ReadOnlySpan typeInstantiation, + ReadOnlySpan methodInstantiation, + bool CStringParmsOnly) + { + byte callConv = checked((byte)GetData(ref signature)); + + if ((callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) != 0) + { + GetData(ref signature); // Ignore generic parameter count + } + int cArgs = (int)GetData(ref signature); + bool isVarArg = (callConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_VARARG; + + if (!CStringParmsOnly) + { + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + stringBuilder.Append(' '); + if (!string.IsNullOrEmpty(namespaceName)) + { + stringBuilder.Append(namespaceName); + stringBuilder.Append('.'); + } + if (!string.IsNullOrEmpty(className)) + { + stringBuilder.Append(className); + stringBuilder.Append('.'); + } + if (!string.IsNullOrEmpty(memberName)) + { + stringBuilder.Append(memberName); + stringBuilder.Append('.'); + } + } + else + { + StringBuilder sbDummy = new StringBuilder(); + AddTypeString(target, sbDummy, ref signature, typeInstantiation, methodInstantiation, metadata); + } + + stringBuilder.Append('('); + for (int i = 0; i < cArgs; i++) + { + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + if (i != cArgs - 1) + stringBuilder.Append(", "); + } + + if (isVarArg) + { + if (cArgs > 0) + stringBuilder.Append(", "); + stringBuilder.Append("..."); + } + stringBuilder.Append(')'); + } + + private static Contracts.CorElementType GetElemType(ref ReadOnlySpan signature) + { + CorElementType result = (Contracts.CorElementType)signature[0]; + signature = signature.Slice(1); + return result; + } + + private static uint GetData(ref ReadOnlySpan signature) + { + byte headerByte = signature[0]; + if ((headerByte & 0x80) == 0) + { + signature = signature.Slice(1); + return headerByte; + } + else if ((headerByte & 0x40) == 0) + { + int result = ((headerByte & 0x3f) << 8) | signature[1]; + signature = signature.Slice(2); + return (uint)result; + } + else if ((headerByte & 0x20) == 0) + { + int result = ((headerByte & 0x1f) << 24) | (signature[1] << 16) | (signature[2] << 8) | signature[3]; + signature = signature.Slice(4); + return (uint)result; + } + throw new InvalidOperationException("Invalid signature format"); + } + + private static uint GetToken(ref ReadOnlySpan signature) + { + uint data = GetData(ref signature); + MetadataTable table; + switch (data & 3) + { + case 0: table = MetadataTable.TypeDef; break; + case 1: table = MetadataTable.TypeRef; break; + case 2: table = MetadataTable.TypeSpec; break; + default: throw new InvalidOperationException("Invalid signature format"); + } + + return EcmaMetadataReader.CreateToken(table, data >> 2); + } + + private static void AddTypeString(Target target, + StringBuilder stringBuilder, + ref ReadOnlySpan signature, + ReadOnlySpan typeInstantiation, + ReadOnlySpan methodInstantiation, + EcmaMetadataReader? metadata) + { + string _namespace; + string name; + EcmaMetadataCursor cursor; + + while (true) + { + switch (GetElemType(ref signature)) + { + case CorElementType.Void: stringBuilder.Append("Void"); return; + case CorElementType.Boolean: stringBuilder.Append("Boolean"); return; + case CorElementType.I: stringBuilder.Append("IntPtr"); return; + case CorElementType.U: stringBuilder.Append("UIntPtr"); return; + case CorElementType.I1: stringBuilder.Append("SByte"); return; + case CorElementType.U1: stringBuilder.Append("Byte"); return; + case CorElementType.I2: stringBuilder.Append("Int16"); return; + case CorElementType.U2: stringBuilder.Append("UInt16"); return; + case CorElementType.I4: stringBuilder.Append("Int32"); return; + case CorElementType.U4: stringBuilder.Append("UInt32"); return; + case CorElementType.I8: stringBuilder.Append("Int64"); return; + case CorElementType.U8: stringBuilder.Append("UInt64"); return; + case CorElementType.R4: stringBuilder.Append("Single"); return; + case CorElementType.R8: stringBuilder.Append("Double"); return; + case CorElementType.Char: stringBuilder.Append("Char"); return; + + case CorElementType.Object: stringBuilder.Append("System.Object"); return; + case CorElementType.String: stringBuilder.Append("System.String"); return; + + case CorElementType.ValueType: + case CorElementType.Class: + if (metadata == null) + throw new InvalidOperationException("Invalid signature without metadata reader"); + uint token = GetToken(ref signature); + cursor = metadata.GetCursor(token); + switch (EcmaMetadataReader.TokenToTable(token)) + { + case MetadataTable.TypeDef: + _namespace = metadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeNamespace); + name = metadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeNamespace); + break; + case MetadataTable.TypeRef: + _namespace = metadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeRef_TypeNamespace); + name = metadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeRef_TypeNamespace); + break; + default: + return; + } + + if (!string.IsNullOrEmpty(_namespace)) + { + stringBuilder.Append(_namespace); + stringBuilder.Append('.'); + } + stringBuilder.Append(name); + return; + + case CorElementType.Internal: + TargetPointer typeHandlePointer = target.ReadPointerFromSpan(signature); + IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem; + signature.Slice(target.PointerSize); + TypeHandle th = runtimeTypeSystem.GetTypeHandle(typeHandlePointer); + switch (runtimeTypeSystem.GetSignatureCorElementType(th)) + { + case CorElementType.FnPtr: + case CorElementType.Ptr: + stringBuilder.Append("System.UIntPtr"); + return; + case CorElementType.ValueType: + if (runtimeTypeSystem.HasTypeParam(th)) + { + th = runtimeTypeSystem.GetTypeParam(th); + } + break; + + case CorElementType.Byref: + case CorElementType.Array: + AddType(target, stringBuilder, th); + return; + } + + uint typeDefToken = runtimeTypeSystem.GetTypeDefToken(th); + TargetPointer modulePointer = target.Contracts.RuntimeTypeSystem.GetModule(th); + Contracts.ModuleHandle module = target.Contracts.Loader.GetModuleHandle(modulePointer); + EcmaMetadataReader internalTypeMetadata = target.Metadata.GetMetadata(module).EcmaMetadataReader; + cursor = internalTypeMetadata.GetCursor(typeDefToken); + _namespace = internalTypeMetadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeNamespace); + name = internalTypeMetadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeNamespace); + + if (!string.IsNullOrEmpty(_namespace)) + { + stringBuilder.Append(_namespace); + stringBuilder.Append('.'); + } + stringBuilder.Append(name); + return; + + case CorElementType.TypedByRef: + stringBuilder.Append("TypedReference"); + return; + + case CorElementType.Byref: + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + stringBuilder.Append(" ByRef"); + return; + + case CorElementType.Ptr: + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + stringBuilder.Append('*'); + return; + + case CorElementType.MVar: + uint mvarIndex = GetData(ref signature); + if (methodInstantiation.Length > (int)mvarIndex) + { + AddType(target, stringBuilder, methodInstantiation[(int)mvarIndex]); + } + else + { + stringBuilder.Append($"!!{mvarIndex}"); + } + return; + + case CorElementType.Var: + uint varIndex = GetData(ref signature); + if (methodInstantiation.Length > (int)varIndex) + { + AddType(target, stringBuilder, methodInstantiation[(int)varIndex]); + } + else + { + stringBuilder.Append($"!!{varIndex}"); + } + return; + + case CorElementType.GenericInst: + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + uint genericArgCount = GetData(ref signature); + stringBuilder.Append('<'); + for (uint i = 0; i < genericArgCount; i++) + { + if (i != 0) + stringBuilder.Append(','); + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + } + stringBuilder.Append('>'); + return; + + case CorElementType.SzArray: + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + stringBuilder.Append("[]"); + return; + + case CorElementType.Array: + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + stringBuilder.Append('['); + uint rank = GetData(ref signature); + for (uint i = 1; i < rank; i++) + { + stringBuilder.Append(','); + } + stringBuilder.Append(']'); + uint numSizes = GetData(ref signature); + for (uint i = 0; i < numSizes; i++) + { + GetData(ref signature); + } + uint numLoBounds = GetData(ref signature); + for (uint i = 0; i < numLoBounds; i++) + { + GetData(ref signature); + } + return; + + case CorElementType.FnPtr: + uint callConv = GetData(ref signature); + uint cArgs = GetData(ref signature); + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + stringBuilder.Append(" ("); + for (uint i = 0; i < cArgs; i++) + { + AddTypeString(target, stringBuilder, ref signature, typeInstantiation, methodInstantiation, metadata); + if (i != cArgs - 1) + stringBuilder.Append(", "); + } + if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_VARARG) + { + if (cArgs > 0) + stringBuilder.Append(", "); + stringBuilder.Append("..."); + } + stringBuilder.Append(')'); + return; + + case CorElementType.CModOpt: + case CorElementType.CModReqd: + GetToken(ref signature); + break; + + default: + stringBuilder.Append("**UNKNOWN TYPE**"); + return; + } + } + } + + private static void AddType(Target target, StringBuilder stringBuilder, TypeHandle typeHandle) + { + IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem; + + if (typeHandle.IsNull) + stringBuilder.Append("**UNKNOWN TYPE**"); + CorElementType corElementType = runtimeTypeSystem.GetSignatureCorElementType(typeHandle); + if (corElementType == CorElementType.ValueType && runtimeTypeSystem.HasTypeParam(typeHandle)) + { + typeHandle = runtimeTypeSystem.GetTypeParam(typeHandle); + } + + switch (corElementType) + { + case CorElementType.Void: stringBuilder.Append("Void"); return; + case CorElementType.Boolean: stringBuilder.Append("Boolean"); return; + case CorElementType.I: stringBuilder.Append("IntPtr"); return; + case CorElementType.U: stringBuilder.Append("UIntPtr"); return; + case CorElementType.I1: stringBuilder.Append("SByte"); return; + case CorElementType.U1: stringBuilder.Append("Byte"); return; + case CorElementType.I2: stringBuilder.Append("Int16"); return; + case CorElementType.U2: stringBuilder.Append("UInt16"); return; + case CorElementType.I4: stringBuilder.Append("Int32"); return; + case CorElementType.U4: stringBuilder.Append("UInt32"); return; + case CorElementType.I8: stringBuilder.Append("Int64"); return; + case CorElementType.U8: stringBuilder.Append("UInt64"); return; + case CorElementType.R4: stringBuilder.Append("Single"); return; + case CorElementType.R8: stringBuilder.Append("Double"); return; + case CorElementType.Char: stringBuilder.Append("Char"); return; + + case CorElementType.Object: stringBuilder.Append("System.Object"); return; + case CorElementType.String: stringBuilder.Append("System.String"); return; + + case CorElementType.ValueType: + case CorElementType.Class: + uint typeDefToken = runtimeTypeSystem.GetTypeDefToken(typeHandle); + TargetPointer modulePointer = target.Contracts.RuntimeTypeSystem.GetModule(typeHandle); + Contracts.ModuleHandle module = target.Contracts.Loader.GetModuleHandle(modulePointer); + EcmaMetadataReader metadata = target.Metadata.GetMetadata(module).EcmaMetadataReader; + EcmaMetadataCursor cursor = metadata.GetCursor(typeDefToken); + string _namespace = metadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeNamespace); + string name = metadata.GetColumnAsUtf8String(cursor, MetadataColumnIndex.TypeDef_TypeNamespace); + + if (!string.IsNullOrEmpty(_namespace)) + { + stringBuilder.Append(_namespace); + stringBuilder.Append('.'); + } + stringBuilder.Append(name); + + ReadOnlySpan instantiation = runtimeTypeSystem.GetInstantiation(typeHandle); + if (instantiation.Length > 0) + { + stringBuilder.Append('<'); + for (int i = 0; i < instantiation.Length; i++) + { + if (i != 0) + stringBuilder.Append(','); + AddType(target, stringBuilder, instantiation[i]); + } + stringBuilder.Append('>'); + } + + return; + + case CorElementType.TypedByRef: + stringBuilder.Append("TypedReference"); + return; + + case CorElementType.Byref: + AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle)); + stringBuilder.Append(" ByRef"); + return; + + case CorElementType.Ptr: + AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle)); + stringBuilder.Append('*'); + return; + + case CorElementType.MVar: + case CorElementType.Var: + runtimeTypeSystem.IsGenericVariable(typeHandle, out TargetPointer genericVariableModulePointer, out uint typeVarToken); + Contracts.ModuleHandle genericVariableModule = target.Contracts.Loader.GetModuleHandle(genericVariableModulePointer); + EcmaMetadataReader genericVariableMetadata = target.Metadata.GetMetadata(genericVariableModule).EcmaMetadataReader; + EcmaMetadataCursor genericVariableCursor = genericVariableMetadata.GetCursor(typeVarToken); + stringBuilder.Append(genericVariableMetadata.GetColumnAsUtf8String(genericVariableCursor, MetadataColumnIndex.GenericParam_Name)); + return; + + case CorElementType.SzArray: + AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle)); + stringBuilder.Append("[]"); + return; + + case CorElementType.Array: + AddType(target, stringBuilder, runtimeTypeSystem.GetTypeParam(typeHandle)); + stringBuilder.Append('['); + + runtimeTypeSystem.IsArray(typeHandle, out uint rank); + for (uint i = 1; i < rank; i++) + { + stringBuilder.Append(','); + } + stringBuilder.Append(']'); + return; + + case CorElementType.FnPtr: + runtimeTypeSystem.IsFunctionPointer(typeHandle, out ReadOnlySpan retAndArgTypes, out byte callConv); + AddType(target, stringBuilder, retAndArgTypes[0]); + stringBuilder.Append(" ("); + for (int i = 1; i < retAndArgTypes.Length; i++) + { + AddType(target, stringBuilder, retAndArgTypes[i]); + if (i != retAndArgTypes.Length - 1) + stringBuilder.Append(", "); + } + if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_VARARG) + { + if (retAndArgTypes.Length > 1) + stringBuilder.Append(", "); + stringBuilder.Append("..."); + } + stringBuilder.Append(')'); + return; + + default: + stringBuilder.Append("**UNKNOWN TYPE**"); + return; + } + } + } +} diff --git a/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs b/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs index 510367bd12480f..3e7dac6536494b 100644 --- a/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs +++ b/src/native/managed/cdacreader/src/Legacy/TypeNameBuilder.cs @@ -17,6 +17,7 @@ internal enum TypeNameFormat FormatAngleBrackets = 4, FormatAssembly = 8, FormatGenericParam = 16, + FormatSignature = 32, } internal struct TypeNameBuilder @@ -43,23 +44,150 @@ private enum ParseState private int InstNesting; private Stack? GenericStartsStack; - private TypeNameBuilder(StringBuilder typeString, Target target, TypeNameFormat format) + private TypeNameBuilder(StringBuilder typeString, Target target, TypeNameFormat format, bool initialStateIsName = false) { TypeString = typeString; Target = target; UseAngleBracketsForGenerics = format.HasFlag(TypeNameFormat.FormatAngleBrackets); - State = ParseState.Start; + if (initialStateIsName) + State = ParseState.Name; + else + State = ParseState.Start; + } + + public static void AppendMethodInternal(Target target, StringBuilder stringBuilder, Contracts.MethodDescHandle method, TypeNameFormat format) + { + AppendMethodImpl(target, stringBuilder, method, default, format); + } + + public static void AppendMethodImpl(Target target, StringBuilder stringBuilder, Contracts.MethodDescHandle method, ReadOnlySpan typeInstantiation, TypeNameFormat format) + { + IRuntimeTypeSystem runtimeTypeSystem = target.Contracts.RuntimeTypeSystem; + ILoader loader = target.Contracts.Loader; + ReadOnlySpan methodNameSpan; + TypeHandle th = default; + Contracts.ModuleHandle module = default; + + bool isNoMetadataMethod = runtimeTypeSystem.IsNoMetadataMethod(method, out methodNameSpan); + if (isNoMetadataMethod) + { + if (runtimeTypeSystem.IsDynamicMethod(method)) + { + stringBuilder.Append("DynamicClass"); + } + else if (runtimeTypeSystem.IsILStub(method)) + { + stringBuilder.Append("ILStubClass"); + } + } + else + { + th = runtimeTypeSystem.GetTypeHandle(runtimeTypeSystem.GetMethodTable(method)); + AppendType(target, stringBuilder, th, typeInstantiation, format); + } + + stringBuilder.Append('.'); + + if (isNoMetadataMethod) + { + stringBuilder.Append(Encoding.UTF8.GetString(methodNameSpan)); + } + else if (runtimeTypeSystem.IsArrayMethod(method, out ArrayFunctionType functionType)) + { + string arrayName; + + switch (functionType) + { + case ArrayFunctionType.Set: + arrayName = "Set"; + break; + case ArrayFunctionType.Get: + arrayName = "Get"; + break; + case ArrayFunctionType.Address: + arrayName = "Address"; + break; + case ArrayFunctionType.Constructor: + arrayName = ".ctor"; + break; + default: + throw new ArgumentException(nameof(method)); + } + + stringBuilder.Append(arrayName); + } + else + { + module = loader.GetModuleHandle(runtimeTypeSystem.GetModule(th)); + EcmaMetadataReader reader = target.Metadata.GetMetadata(module).EcmaMetadataReader; + var cursor = reader.GetCursor(runtimeTypeSystem.GetMethodToken(method)); + stringBuilder.Append(reader.GetColumnAsUtf8String(cursor, MetadataColumnIndex.MethodDef_Name)); + } + + ReadOnlySpan genericMethodInstantiation = runtimeTypeSystem.GetGenericMethodInstantiation(method); + if (genericMethodInstantiation.Length > 0 && !runtimeTypeSystem.IsGenericMethodDefinition(method)) + { + AppendInst(target, stringBuilder, genericMethodInstantiation, format); + } + + if (format.HasFlag(TypeNameFormat.FormatSignature)) + { + ReadOnlySpan signature; + EcmaMetadataReader? reader = default; + if (!runtimeTypeSystem.IsStoredSigMethodDesc(method, out signature)) + { + reader = target.Metadata.GetMetadata(module).EcmaMetadataReader; + var cursor = reader.GetCursor(runtimeTypeSystem.GetMethodToken(method)); + signature = reader.GetColumnAsBlob(cursor, MetadataColumnIndex.MethodDef_Signature); + } + + ReadOnlySpan typeInstantiationSigFormat = default; + if (!th.IsNull) + { + typeInstantiationSigFormat = runtimeTypeSystem.GetInstantiation(th); + } + + SigFormat.AppendSigFormat(target, stringBuilder, signature, reader, null, null, null, typeInstantiationSigFormat, runtimeTypeSystem.GetGenericMethodInstantiation(method), true); + } + } + + public static TypeHandle GetExactOwningType(IRuntimeTypeSystem runtimeTypeSystem, TypeHandle possiblyDerivedType, MethodDescHandle method) + { + TypeHandle approxOwner = runtimeTypeSystem.GetTypeHandle(runtimeTypeSystem.GetMethodTable(method)); + + uint typeDefTokenOfOwner = runtimeTypeSystem.GetTypeDefToken(approxOwner); + TargetPointer moduleOfOwner = runtimeTypeSystem.GetModule(approxOwner); + + do + { + uint typeDefTokenOfPossiblyDerivedType = runtimeTypeSystem.GetTypeDefToken(possiblyDerivedType); + TargetPointer moduleOfPossiblyDerivedType = runtimeTypeSystem.GetModule(possiblyDerivedType); + + if ((typeDefTokenOfOwner == typeDefTokenOfPossiblyDerivedType) && (moduleOfOwner == moduleOfPossiblyDerivedType)) + { + return possiblyDerivedType; + } + + TargetPointer parentTypePointer = runtimeTypeSystem.GetParentMethodTable(possiblyDerivedType); + if (parentTypePointer.Value == 0) + throw new InvalidOperationException("Invalid parent type"); + + // TODO(cdac) - Consider adding infinite loop detection here + possiblyDerivedType = runtimeTypeSystem.GetTypeHandle(parentTypePointer); + } while (true); } public static void AppendType(Target target, StringBuilder stringBuilder, Contracts.TypeHandle typeHandle, TypeNameFormat format) { AppendType(target, stringBuilder, typeHandle, default, format); } + public static void AppendType(Target target, StringBuilder stringBuilder, Contracts.TypeHandle typeHandle, ReadOnlySpan typeInstantiation, TypeNameFormat format) { TypeNameBuilder builder = new(stringBuilder, target, format); AppendTypeCore(ref builder, typeHandle, typeInstantiation, format); } + private static void AppendTypeCore(ref TypeNameBuilder tnb, Contracts.TypeHandle typeHandle, ReadOnlySpan instantiation, TypeNameFormat format) { bool toString = format.HasFlag(TypeNameFormat.FormatNamespace) && !format.HasFlag(TypeNameFormat.FormatFullInst) && !format.HasFlag(TypeNameFormat.FormatAssembly); @@ -184,6 +312,15 @@ private static void AppendTypeCore(ref TypeNameBuilder tnb, Contracts.TypeHandle } } + // Append a square-bracket-enclosed, comma-separated list of n type parameters in inst to the string s + // and enclose each parameter in square brackets to disambiguate the commas + // The following flags in the FormatFlags argument are significant: FormatNamespace FormatFullInst FormatAssembly FormatNoVersion + private static void AppendInst(Target target, StringBuilder stringBuilder, ReadOnlySpan inst, TypeNameFormat format) + { + TypeNameBuilder tnb = new (stringBuilder, target, format, initialStateIsName: true); + AppendInst(ref tnb, inst, format); + } + private static void AppendInst(ref TypeNameBuilder tnb, ReadOnlySpan inst, TypeNameFormat format) { tnb.OpenGenericArguments(); diff --git a/src/native/managed/cdacreader/src/Target.cs b/src/native/managed/cdacreader/src/Target.cs index dc5eec3fae31be..a091e2e4f960cd 100644 --- a/src/native/managed/cdacreader/src/Target.cs +++ b/src/native/managed/cdacreader/src/Target.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Net; using System.Numerics; using System.Runtime.CompilerServices; using Microsoft.Diagnostics.DataContractReader.Data; @@ -286,6 +287,23 @@ private static bool TryRead(ulong address, bool isLittleEndian, Reader reader : T.TryReadBigEndian(buffer, !IsSigned(), out value); } + private static T Read(ReadOnlySpan bytes, bool isLittleEndian) where T : unmanaged, IBinaryInteger, IMinMaxValue + { + if (sizeof(T) != bytes.Length) + throw new ArgumentException(nameof(bytes)); + + T value; + if (isLittleEndian) + { + T.TryReadLittleEndian(bytes, !IsSigned(), out value); + } + else + { + T.TryReadBigEndian(bytes, !IsSigned(), out value); + } + return value; + } + public void ReadBuffer(ulong address, Span buffer) { if (!TryReadBuffer(address, buffer)) @@ -311,6 +329,18 @@ public TargetPointer ReadPointer(ulong address) return pointer; } + public TargetPointer ReadPointerFromSpan(ReadOnlySpan bytes) + { + if (_config.PointerSize == sizeof(uint)) + { + return new TargetPointer(Read(bytes.Slice(0, sizeof(uint)), _config.IsLittleEndian)); + } + else + { + return new TargetPointer(Read(bytes.Slice(0, sizeof(ulong)), _config.IsLittleEndian)); + } + } + public void ReadPointers(ulong address, Span buffer) { // TODO(cdac) - This could do a single read, and then swizzle in place if it is useful for performance @@ -371,6 +401,8 @@ public bool IsAlignedToPointerSize(ulong value) public bool IsAlignedToPointerSize(TargetPointer pointer) => IsAligned(pointer.Value, _config.PointerSize); + public char DirectorySeparator => (char)ReadGlobal(Constants.Globals.DirectorySeparator); + public T ReadGlobal(string name) where T : struct, INumber => ReadGlobal(name, out _);