From 005b792abb95f9e782f986adfbca435762b9f5e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 May 2026 02:15:30 +0000 Subject: [PATCH 01/10] Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> add fill expanded type data apis --- .../design/datacontracts/RuntimeTypeSystem.md | 6 + src/coreclr/debug/daccess/dacdbiimpl.cpp | 57 +--- src/coreclr/debug/daccess/dacdbiimpl.h | 5 +- src/coreclr/debug/di/process.cpp | 2 +- src/coreclr/debug/di/rstype.cpp | 4 +- src/coreclr/debug/inc/dacdbiinterface.h | 5 +- src/coreclr/debug/inc/dacdbistructures.h | 7 +- src/coreclr/inc/dacdbi.idl | 3 +- .../Contracts/IRuntimeTypeSystem.cs | 2 + .../Contracts/RuntimeTypeSystem_1.cs | 6 + .../Dbi/DacDbiImpl.cs | 311 +++++++++++++++++- .../Dbi/IDacDbiInterface.cs | 48 ++- .../DumpTests/RuntimeTypeSystemDumpTests.cs | 3 + .../managed/cdac/tests/MethodTableTests.cs | 3 + .../MockDescriptors.RuntimeTypeSystem.cs | 13 + 15 files changed, 406 insertions(+), 69 deletions(-) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 28605efe3c70b3..f93ba4613dd913 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -51,6 +51,8 @@ partial interface IRuntimeTypeSystem : IContract // True if the MethodTable is the sentinel value associated with unallocated space in the managed heap public virtual bool IsFreeObjectMethodTable(TypeHandle typeHandle); + // True if the MethodTable is the System.Object MethodTable (g_pObjectClass) + public virtual bool IsObject(TypeHandle typeHandle); public virtual bool IsString(TypeHandle typeHandle); // True if the MethodTable represents a type that contains managed references public virtual bool ContainsGCPointers(TypeHandle typeHandle); @@ -433,6 +435,7 @@ The contract depends on the following globals | --- | --- | | `ContinuationMethodTable` | A pointer to the address of the base `Continuation` `MethodTable`, or null if no continuations have been created | `FreeObjectMethodTablePointer` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory +| `ObjectMethodTable` | A pointer to the address of the `System.Object` `MethodTable` (`g_pObjectClass`) | `StaticsPointerMask` | For masking out a bit of DynamicStaticsInfo pointer fields | `ArrayBaseSize` | The base size of an array object; used to compute multidimensional array rank from `MethodTable::BaseSize` @@ -505,6 +508,7 @@ Contracts used: private readonly Dictionary _methodTables; internal TargetPointer FreeObjectMethodTablePointer {get; } + internal TargetPointer ObjectMethodTablePointer {get; } internal TargetPointer ContinuationMethodTablePointer {get; } public TypeHandle GetTypeHandle(TargetPointer typeHandlePointer) @@ -565,6 +569,8 @@ Contracts used: public bool IsFreeObjectMethodTable(TypeHandle TypeHandle) => FreeObjectMethodTablePointer == TypeHandle.Address; + public bool IsObject(TypeHandle TypeHandle) => ObjectMethodTablePointer != TargetPointer.Null && ObjectMethodTablePointer == TypeHandle.Address; + public bool IsString(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.IsString; public bool ContainsGCPointers(TypeHandle TypeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[TypeHandle.Address].Flags.ContainsGCPointers; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index bce89a76385ff1..c6057a97e920e5 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2414,10 +2414,7 @@ void DacDbiInterfaceImpl::GetClassTypeInfo(TypeHandle typeH { pTypeInfo->ClassTypeData.typeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); } - else // non-generic - { - pTypeInfo->ClassTypeData.typeHandle = VMPTR_TypeHandle::NullPtr(); - } + // else: non-generic — typeHandle stays null pTypeInfo->ClassTypeData.metadataToken = typeHandle.GetCl(); @@ -2478,8 +2475,7 @@ void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_BYREF: pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); - pTypeInfo->metadataToken = mdTokenNil; - pTypeInfo->vmAssembly = VMPTR_Assembly::NullPtr(); + // metadataToken and vmAssembly stay zero (mdTokenNil / NullPtr) break; case ELEMENT_TYPE_CLASS: @@ -2492,10 +2488,7 @@ void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle { pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); } - else - { - pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); - } + // else: vmTypeHandle stays null pTypeInfo->metadataToken = typeHandle.GetCl(); _ASSERTE(pModule); @@ -2504,29 +2497,13 @@ void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle } default: - pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); - pTypeInfo->metadataToken = mdTokenNil; - pTypeInfo->vmAssembly = VMPTR_Assembly::NullPtr(); + // All fields zero break; } return; } // DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, COR_TYPEID id, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) -{ - DD_ENTER_MAY_THROW; - - HRESULT hr = S_OK; - EX_TRY - { - - TypeHandleToExpandedTypeInfoImpl(boxed, TypeHandle::FromPtr(TO_TADDR(id.token1)), pTypeInfo); - } - EX_CATCH_HRESULT(hr); - return hr; -} - HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS addr, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) { DD_ENTER_MAY_THROW; @@ -2534,6 +2511,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValu HRESULT hr = S_OK; EX_TRY { + *pTypeInfo = {}; PTR_Object obj(TO_TADDR(addr)); TypeHandleToExpandedTypeInfoImpl(boxed, obj->GetGCSafeTypeHandle(), pTypeInfo); @@ -2543,16 +2521,16 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValu } // DacDbi API: use a type handle to get the information needed to create the corresponding RS CordbType instance -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, VMPTR_TypeHandle vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, UINT64 vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) { DD_ENTER_MAY_THROW; HRESULT hr = S_OK; EX_TRY { + *pTypeInfo = {}; - - TypeHandle typeHandle = TypeHandle::FromPtr(vmTypeHandle.GetDacPtr()); + TypeHandle typeHandle = TypeHandle::FromPtr((TADDR)vmTypeHandle); TypeHandleToExpandedTypeInfoImpl(boxed, typeHandle, pTypeInfo); } EX_CATCH_HRESULT(hr); @@ -2776,20 +2754,16 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetMethodDescParams(VMPTR_MethodD EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER { // Fill in the struct using the TypeHandle of the current type parameter if we can. - VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); - vmTypeHandle.SetDacTargetPtr(thCurrent.AsTAddr()); IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - vmTypeHandle, + (UINT64)thCurrent.AsTAddr(), &((*pGenericTypeParams)[i]))); } EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER { // On failure for a particular type, default it back to System.__Canon. - VMPTR_TypeHandle vmTHCanon = VMPTR_TypeHandle::NullPtr(); TypeHandle thCanon = TypeHandle(g_pCanonMethodTableClass); - vmTHCanon.SetDacTargetPtr(thCanon.AsTAddr()); IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - vmTHCanon, + (UINT64)thCanon.AsTAddr(), &((*pGenericTypeParams)[i]))); } EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER @@ -3214,11 +3188,8 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetTypeHandleParams(VMPTR_TypeHan // collect type information for each type parameter for (unsigned int i = 0; i < pParams->Count(); ++i) { - VMPTR_TypeHandle thInst = VMPTR_TypeHandle::NullPtr(); - thInst.SetDacTargetPtr(typeHandle.GetInstantiation()[i].AsTAddr()); - IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - thInst, + (UINT64)typeHandle.GetInstantiation()[i].AsTAddr(), &((*pParams)[i]))); } @@ -6092,15 +6063,11 @@ void DacDbiInterfaceImpl::InitObjectData(PTR_Object objPtr, DebuggerIPCE_ObjectData * pObjectData) { _ASSERTE(pObjectData != NULL); - // @todo - this is still dangerous because the object may still be invalid. - VMPTR_TypeHandle vmTypeHandle = VMPTR_TypeHandle::NullPtr(); - vmTypeHandle.SetDacTargetPtr(objPtr->GetGCSafeTypeHandle().AsTAddr()); - // Save basic object info. pObjectData->objSize = objPtr->GetSize(); pObjectData->objOffsetToVars = dac_cast((objPtr)->GetData()) - dac_cast(objPtr); - IfFailThrow(TypeHandleToExpandedTypeInfo(AllBoxed, vmTypeHandle, &(pObjectData->objTypeData))); + IfFailThrow(TypeHandleToExpandedTypeInfo(AllBoxed, (UINT64)objPtr->GetGCSafeTypeHandle().AsTAddr(), &(pObjectData->objTypeData))); // If this is a string object, set the type to ELEMENT_TYPE_STRING. if (objPtr->GetGCSafeMethodTable() == g_pStringClass) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index 54d1a5bf000d05..e7939ad686f6bd 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -244,13 +244,10 @@ class DacDbiInterfaceImpl : HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS addr, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo); - HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, COR_TYPEID id, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo); - - // @dbgtodo Microsoft inspection: change DebuggerIPCE_ExpandedTypeData to DacDbiStructures type hierarchy // once ICorDebugType and ICorDebugClass are DACized // use a type handle to get the information needed to create the corresponding RS CordbType instance - HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, VMPTR_TypeHandle vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo); + HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, UINT64 vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo); // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. HRESULT STDMETHODCALLTYPE GetTypeHandle(VMPTR_Module vmModule, mdTypeDef metadataToken, OUT VMPTR_TypeHandle * pRetVal); diff --git a/src/coreclr/debug/di/process.cpp b/src/coreclr/debug/di/process.cpp index b779b5a990727c..d48fba5ad43a17 100644 --- a/src/coreclr/debug/di/process.cpp +++ b/src/coreclr/debug/di/process.cpp @@ -2370,7 +2370,7 @@ HRESULT CordbProcess::GetTypeForTypeID(COR_TYPEID id, ICorDebugType **ppType) EX_TRY { DebuggerIPCE_ExpandedTypeData data; - IfFailThrow(GetDAC()->GetObjectExpandedTypeInfoFromID(AllBoxed, id, &data)); + IfFailThrow(GetDAC()->TypeHandleToExpandedTypeInfo(AllBoxed, id.token1, &data)); CordbType *type = 0; hr = CordbType::TypeDataToType(GetAppDomain(), &data, &type); diff --git a/src/coreclr/debug/di/rstype.cpp b/src/coreclr/debug/di/rstype.cpp index 7cfe5f9f11bc11..640083218a3516 100644 --- a/src/coreclr/debug/di/rstype.cpp +++ b/src/coreclr/debug/di/rstype.cpp @@ -1163,8 +1163,10 @@ HRESULT CordbType::TypeDataToType(CordbAppDomain *pAppDomain, DebuggerIPCE_Basic { RSLockHolder lockHolder(pProcess->GetProcessLock()); + UINT64 id; + memcpy(&id, &data->vmTypeHandle, sizeof(UINT64)); IfFailThrow(pProcess->GetDAC()->TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, // could be generics which are never boxed - data->vmTypeHandle, + id, &typeInfo)); } diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index 0b8797a4eccb8f..49153ec15b0921 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1549,14 +1549,11 @@ IDacDbiInterface : public IUnknown // vmTypeHandle - type handle for the type // output: pTypeInfo - holds information needed to build the corresponding CordbType // - virtual HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, VMPTR_TypeHandle vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; + virtual HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, UINT64 vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; virtual HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS addr, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; - virtual HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfoFromID(AreValueTypesBoxed boxed, COR_TYPEID id, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; - - // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. // Note there is no guarantee the returned handle will be fully restored (in pre-jit scenarios), // only that it exists. Later functions that use this type handle should fail if they require diff --git a/src/coreclr/debug/inc/dacdbistructures.h b/src/coreclr/debug/inc/dacdbistructures.h index aff80317c59b32..5cc56a2b8b981d 100644 --- a/src/coreclr/debug/inc/dacdbistructures.h +++ b/src/coreclr/debug/inc/dacdbistructures.h @@ -648,7 +648,12 @@ class MSLAYOUT EnCHangingFieldInfo // NoValueTypeBoxing: // TypeHandleToExpandedTypeInfo is also used to report type parameters, // and in this case none of the types are considered boxed ( -enum AreValueTypesBoxed { NoValueTypeBoxing, OnlyPrimitivesUnboxed, AllBoxed }; +enum AreValueTypesBoxed +{ + NoValueTypeBoxing = 0, + OnlyPrimitivesUnboxed = 1, + AllBoxed = 2 +}; // TypeRefData is used for resolving a type reference (see code:CordbModule::ResolveTypeRef and // code:DacDbiInterfaceImpl::ResolveTypeReference) to store relevant information about the type diff --git a/src/coreclr/inc/dacdbi.idl b/src/coreclr/inc/dacdbi.idl index f8e647b376d45a..4135f951b8be44 100644 --- a/src/coreclr/inc/dacdbi.idl +++ b/src/coreclr/inc/dacdbi.idl @@ -310,9 +310,8 @@ interface IDacDbiInterface : IUnknown [in] VMPTR_TypeHandle vmThApprox, [out] DacDbiArrayList_FieldData * pFieldList, [out] SIZE_T * pObjectSize); - HRESULT TypeHandleToExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] VMPTR_TypeHandle vmTypeHandle, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); + HRESULT TypeHandleToExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] UINT64 vmTypeHandle, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); HRESULT GetObjectExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] CORDB_ADDRESS addr, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); - HRESULT GetObjectExpandedTypeInfoFromID([in] AreValueTypesBoxed boxed, [in] COR_TYPEID id, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); HRESULT GetTypeHandle([in] VMPTR_Module vmModule, [in] mdTypeDef metadataToken, [out] VMPTR_TypeHandle * pRetVal); HRESULT GetApproxTypeHandle([in] TypeInfoList * pTypeData, [out] VMPTR_TypeHandle * pRetVal); HRESULT GetExactTypeHandle([in] struct DebuggerIPCE_ExpandedTypeData * pTypeData, [in] ArgInfoList * pArgInfo, [out] VMPTR_TypeHandle * pVmTypeHandle); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 4ba56875057856..10c29b665ca90a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -114,6 +114,8 @@ public interface IRuntimeTypeSystem : IContract // True if the MethodTable is the sentinel value associated with unallocated space in the managed heap bool IsFreeObjectMethodTable(TypeHandle typeHandle) => throw new NotImplementedException(); + // True if the MethodTable is the System.Object MethodTable (g_pObjectClass) + bool IsObject(TypeHandle typeHandle) => throw new NotImplementedException(); bool IsString(TypeHandle typeHandle) => throw new NotImplementedException(); // True if the MethodTable represents a type that contains managed references bool ContainsGCPointers(TypeHandle typeHandle) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 26debd447f39ec..f2c838bf34073b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -18,6 +18,7 @@ internal partial struct RuntimeTypeSystem_1 : IRuntimeTypeSystem private const int TYPE_MASK_OFFSET = 27; // offset of type in field desc flags2 private readonly Target _target; private readonly TargetPointer _freeObjectMethodTablePointer; + private readonly TargetPointer _objectMethodTablePointer; private readonly TargetPointer _continuationMethodTablePointer; private readonly ulong _methodDescAlignment; private readonly TypeValidation _typeValidation; @@ -433,6 +434,8 @@ internal RuntimeTypeSystem_1(Target target) _target = target; _freeObjectMethodTablePointer = target.ReadPointer( target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable)); + _objectMethodTablePointer = target.ReadPointer( + target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable)); _continuationMethodTablePointer = target.ReadPointer( target.ReadGlobalPointer(Constants.Globals.ContinuationMethodTable)); _methodDescAlignment = target.ReadGlobal(Constants.Globals.MethodDescAlignment); @@ -442,6 +445,7 @@ internal RuntimeTypeSystem_1(Target target) } internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer; + internal TargetPointer ObjectMethodTablePointer => _objectMethodTablePointer; internal TargetPointer ContinuationMethodTablePointer => _continuationMethodTablePointer; internal ulong MethodDescAlignment => _methodDescAlignment; @@ -560,6 +564,8 @@ private Data.EEClass GetClassData(TypeHandle typeHandle) public bool IsFreeObjectMethodTable(TypeHandle typeHandle) => FreeObjectMethodTablePointer == typeHandle.Address; + public bool IsObject(TypeHandle typeHandle) => ObjectMethodTablePointer != TargetPointer.Null && ObjectMethodTablePointer == typeHandle.Address; + public bool IsString(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.IsString; public bool ContainsGCPointers(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.ContainsGCPointers; public bool RequiresAlign8(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? false : _methodTables[typeHandle.Address].Flags.RequiresAlign8; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index f9e808ff23de9f..0b53cf87786b9c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -1277,14 +1278,112 @@ public int GetClassInfo(ulong thExact, nint pData) public int GetInstantiationFieldInfo(ulong vmAssembly, ulong vmTypeHandle, ulong vmExactMethodTable, nint pFieldList, nuint* pObjectSize) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetInstantiationFieldInfo(vmAssembly, vmTypeHandle, vmExactMethodTable, pFieldList, pObjectSize) : HResults.E_NOTIMPL; - public int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, nint pData) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.TypeHandleToExpandedTypeInfo(boxed, vmTypeHandle, pData) : HResults.E_NOTIMPL; + public int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + *pTypeInfo = default; + int hr = HResults.S_OK; + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TypeHandle th = rts.GetTypeHandle(new TargetPointer(vmTypeHandle)); + TypeHandleToExpandedTypeInfoImpl(rts, (AreValueTypesBoxed)boxed, th, pTypeInfo); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + DebuggerIPCE_ExpandedTypeData dataLocal; + int hrLocal = _legacy.TypeHandleToExpandedTypeInfo(boxed, vmTypeHandle, &dataLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + ValidateExpandedTypeData(pTypeInfo, &dataLocal); + } + } +#endif + return hr; + } - public int GetObjectExpandedTypeInfo(int boxed, ulong addr, nint pTypeInfo) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetObjectExpandedTypeInfo(boxed, addr, pTypeInfo) : HResults.E_NOTIMPL; + public int GetObjectExpandedTypeInfo(int boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + *pTypeInfo = default; + int hr = HResults.S_OK; + try + { + IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + TargetPointer mtAddr = _target.Contracts.Object.GetMethodTableAddress(new TargetPointer(addr)); + TypeHandle th = rts.GetTypeHandle(mtAddr); + TypeHandleToExpandedTypeInfoImpl(rts, (AreValueTypesBoxed)boxed, th, pTypeInfo); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacy is not null) + { + DebuggerIPCE_ExpandedTypeData dataLocal; + int hrLocal = _legacy.GetObjectExpandedTypeInfo(boxed, addr, &dataLocal); + Debug.ValidateHResult(hr, hrLocal); + if (hr == HResults.S_OK) + { + ValidateExpandedTypeData(pTypeInfo, &dataLocal); + } + } +#endif + return hr; + } - public int GetObjectExpandedTypeInfoFromID(int boxed, COR_TYPEID id, nint pTypeInfo) - => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetObjectExpandedTypeInfoFromID(boxed, id, pTypeInfo) : HResults.E_NOTIMPL; +#if DEBUG + private static void ValidateExpandedTypeData(DebuggerIPCE_ExpandedTypeData* cdac, DebuggerIPCE_ExpandedTypeData* dac) + { + Debug.Assert(cdac->elementType == dac->elementType, + $"cDAC elementType: {cdac->elementType}, DAC: {dac->elementType}"); + switch ((CorElementType)cdac->elementType) + { + case CorElementType.Class: + case CorElementType.ValueType: + Debug.Assert(cdac->ClassTypeData_metadataToken == dac->ClassTypeData_metadataToken, + $"cDAC ClassTypeData.metadataToken: {cdac->ClassTypeData_metadataToken:x}, DAC: {dac->ClassTypeData_metadataToken:x}"); + Debug.Assert(cdac->ClassTypeData_vmAssembly == dac->ClassTypeData_vmAssembly, + $"cDAC ClassTypeData.vmAssembly: {cdac->ClassTypeData_vmAssembly:x}, DAC: {dac->ClassTypeData_vmAssembly:x}"); + Debug.Assert(cdac->ClassTypeData_typeHandle == dac->ClassTypeData_typeHandle, + $"cDAC ClassTypeData.typeHandle: {cdac->ClassTypeData_typeHandle:x}, DAC: {dac->ClassTypeData_typeHandle:x}"); + break; + case CorElementType.Array: + case CorElementType.SzArray: + Debug.Assert(cdac->ArrayTypeData_arrayRank == dac->ArrayTypeData_arrayRank, + $"cDAC ArrayTypeData.arrayRank: {cdac->ArrayTypeData_arrayRank}, DAC: {dac->ArrayTypeData_arrayRank}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.elementType == dac->ArrayTypeData_arrayTypeArg.elementType, + $"cDAC ArrayTypeData.arrayTypeArg.elementType: {cdac->ArrayTypeData_arrayTypeArg.elementType}, DAC: {dac->ArrayTypeData_arrayTypeArg.elementType}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.metadataToken == dac->ArrayTypeData_arrayTypeArg.metadataToken, + $"cDAC ArrayTypeData.arrayTypeArg.metadataToken: {cdac->ArrayTypeData_arrayTypeArg.metadataToken:x}, DAC: {dac->ArrayTypeData_arrayTypeArg.metadataToken:x}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.vmAssembly == dac->ArrayTypeData_arrayTypeArg.vmAssembly, + $"cDAC ArrayTypeData.arrayTypeArg.vmAssembly: {cdac->ArrayTypeData_arrayTypeArg.vmAssembly:x}, DAC: {dac->ArrayTypeData_arrayTypeArg.vmAssembly:x}"); + Debug.Assert(cdac->ArrayTypeData_arrayTypeArg.vmTypeHandle == dac->ArrayTypeData_arrayTypeArg.vmTypeHandle, + $"cDAC ArrayTypeData.arrayTypeArg.vmTypeHandle: {cdac->ArrayTypeData_arrayTypeArg.vmTypeHandle:x}, DAC: {dac->ArrayTypeData_arrayTypeArg.vmTypeHandle:x}"); + break; + case CorElementType.Ptr: + case CorElementType.Byref: + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.elementType == dac->UnaryTypeData_unaryTypeArg.elementType, + $"cDAC UnaryTypeData.unaryTypeArg.elementType: {cdac->UnaryTypeData_unaryTypeArg.elementType}, DAC: {dac->UnaryTypeData_unaryTypeArg.elementType}"); + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.metadataToken == dac->UnaryTypeData_unaryTypeArg.metadataToken, + $"cDAC UnaryTypeData.unaryTypeArg.metadataToken: {cdac->UnaryTypeData_unaryTypeArg.metadataToken:x}, DAC: {dac->UnaryTypeData_unaryTypeArg.metadataToken:x}"); + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.vmAssembly == dac->UnaryTypeData_unaryTypeArg.vmAssembly, + $"cDAC UnaryTypeData.unaryTypeArg.vmAssembly: {cdac->UnaryTypeData_unaryTypeArg.vmAssembly:x}, DAC: {dac->UnaryTypeData_unaryTypeArg.vmAssembly:x}"); + Debug.Assert(cdac->UnaryTypeData_unaryTypeArg.vmTypeHandle == dac->UnaryTypeData_unaryTypeArg.vmTypeHandle, + $"cDAC UnaryTypeData.unaryTypeArg.vmTypeHandle: {cdac->UnaryTypeData_unaryTypeArg.vmTypeHandle:x}, DAC: {dac->UnaryTypeData_unaryTypeArg.vmTypeHandle:x}"); + break; + case CorElementType.FnPtr: + Debug.Assert(cdac->NaryTypeData_typeHandle == dac->NaryTypeData_typeHandle, + $"cDAC NaryTypeData.typeHandle: {cdac->NaryTypeData_typeHandle:x}, DAC: {dac->NaryTypeData_typeHandle:x}"); + break; + } + } +#endif public int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal) { @@ -2079,4 +2178,204 @@ public int GetAsyncLocals(ulong vmMethod, ulong codeAddr, uint state, nint pAsyn public int GetGenericArgTokenIndex(ulong vmMethod, uint* pIndex) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetGenericArgTokenIndex(vmMethod, pIndex) : HResults.E_NOTIMPL; + // Shared core implementation for TypeHandleToExpandedTypeInfo and GetObjectExpandedTypeInfo. + private void TypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + CorElementType elementType = GetElementType(rts, typeHandle); + WriteInt32LittleEndian(ref pTypeInfo->elementType, (int)elementType); + + switch (elementType) + { + case CorElementType.Array: + case CorElementType.SzArray: + FillArrayTypeInfo(rts, typeHandle, pTypeInfo); + break; + + case CorElementType.Ptr: + case CorElementType.Byref: + FillPtrTypeInfo(rts, boxed, typeHandle, pTypeInfo); + break; + + case CorElementType.ValueType: + if (boxed == AreValueTypesBoxed.OnlyPrimitivesUnboxed || boxed == AreValueTypesBoxed.AllBoxed) + { + WriteInt32LittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); + } + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + break; + + case CorElementType.Class: + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + break; + + case CorElementType.FnPtr: + FillFnPtrTypeInfo(rts, boxed, typeHandle, pTypeInfo); + break; + + default: + if (boxed == AreValueTypesBoxed.AllBoxed) + { + WriteInt32LittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + } + break; + } + } + + // Determines the CorElementType for a type handle, mapping System.Object and System.String + // to their specific element types (the runtime's GetSignatureCorElementType returns E_T_CLASS + // for both Object and String). + private static CorElementType GetElementType(IRuntimeTypeSystem rts, TypeHandle typeHandle) + { + if (typeHandle.IsNull) + return CorElementType.Void; + + if (rts.IsString(typeHandle)) + return CorElementType.String; + + if (rts.IsObject(typeHandle)) + return CorElementType.Object; + + return rts.GetSignatureCorElementType(typeHandle); + } + + // Mirrors native TypeHandle::UpCastTypeIfNeeded — for continuation types, returns the + // parent (continuation base) type handle instead. + private static TypeHandle UpCastTypeIfNeeded(IRuntimeTypeSystem rts, TypeHandle typeHandle) + { + if (rts.IsContinuation(typeHandle)) + { + TargetPointer parentMT = rts.GetParentMethodTable(typeHandle); + if (parentMT != TargetPointer.Null) + return rts.GetTypeHandle(parentMT); + } + return typeHandle; + } + + // Fills ArrayTypeData for E_T_ARRAY and E_T_SZARRAY. + // Mirrors native DacDbiInterfaceImpl::GetArrayTypeInfo. + private void FillArrayTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + Debug.Assert(rts.IsArray(typeHandle, out _)); + rts.IsArray(typeHandle, out uint rank); + WriteUInt32LittleEndian(ref pTypeInfo->ArrayTypeData_arrayRank, rank); + TypeHandle elemTypeHandle = rts.GetTypeParam(typeHandle); + FillBasicTypeInfo(rts, elemTypeHandle, out pTypeInfo->ArrayTypeData_arrayTypeArg); + } + + // Fills UnaryTypeData for E_T_PTR and E_T_BYREF (or ClassTypeData if AllBoxed). + private void FillPtrTypeInfo(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + if (boxed == AreValueTypesBoxed.AllBoxed) + { + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + } + else + { + TypeHandle paramTypeHandle = rts.GetTypeParam(typeHandle); + FillBasicTypeInfo(rts, paramTypeHandle, out pTypeInfo->UnaryTypeData_unaryTypeArg); + } + } + + // Fills ClassTypeData for E_T_CLASS and E_T_VALUETYPE. + private void FillClassTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + typeHandle = UpCastTypeIfNeeded(rts, typeHandle); + + TargetPointer modulePtr = rts.GetModule(typeHandle); + Contracts.ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + + ReadOnlySpan instantiation = rts.GetInstantiation(typeHandle); + if (instantiation.Length > 0) + { + // Generic instantiation — set the type handle so the debugger can fetch type arguments + WriteUInt64LittleEndian(ref pTypeInfo->ClassTypeData_typeHandle, typeHandle.Address.Value); + } + // else: non-generic — typeHandle stays null + + WriteUInt32LittleEndian(ref pTypeInfo->ClassTypeData_metadataToken, rts.GetTypeDefToken(typeHandle)); + + Debug.Assert(modulePtr != TargetPointer.Null); + WriteUInt64LittleEndian(ref pTypeInfo->ClassTypeData_vmAssembly, loader.GetAssembly(moduleHandle).Value); + } + + // Fills NaryTypeData for E_T_FNPTR (or ClassTypeData if AllBoxed). + private void FillFnPtrTypeInfo(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + { + if (boxed == AreValueTypesBoxed.AllBoxed) + { + FillClassTypeInfo(rts, typeHandle, pTypeInfo); + } + else + { + WriteUInt64LittleEndian(ref pTypeInfo->NaryTypeData_typeHandle, typeHandle.Address.Value); + } + } + + // Fills a DebuggerIPCE_BasicTypeData for a type handle — used for array element types + // and ptr/byref referent types. + private void FillBasicTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, out DebuggerIPCE_BasicTypeData typeInfo) + { + typeInfo = default; + CorElementType elementType = GetElementType(rts, typeHandle); + WriteInt32LittleEndian(ref typeInfo.elementType, (int)elementType); + + switch (elementType) + { + case CorElementType.Array: + case CorElementType.SzArray: + case CorElementType.FnPtr: + case CorElementType.Ptr: + case CorElementType.Byref: + WriteUInt64LittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); + // metadataToken and vmAssembly stay zero + break; + + case CorElementType.Class: + case CorElementType.ValueType: + { + typeHandle = UpCastTypeIfNeeded(rts, typeHandle); + + TargetPointer modulePtr = rts.GetModule(typeHandle); + Contracts.ILoader loader = _target.Contracts.Loader; + Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + + ReadOnlySpan instantiation = rts.GetInstantiation(typeHandle); + if (instantiation.Length > 0) + { + WriteUInt64LittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); + } + // else: vmTypeHandle stays null + + WriteUInt32LittleEndian(ref typeInfo.metadataToken, rts.GetTypeDefToken(typeHandle)); + Debug.Assert(modulePtr != TargetPointer.Null); + WriteUInt64LittleEndian(ref typeInfo.vmAssembly, loader.GetAssembly(moduleHandle).Value); + break; + } + + default: + // All fields zero + break; + } + } + + // Little-endian write helpers for IPCE structs. + // Native IPCE structs use Portable, which stores data in little-endian format. + // These helpers ensure managed writes match that convention. + private static void WriteInt32LittleEndian(ref int dest, int value) + { + dest = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + } + + private static void WriteUInt32LittleEndian(ref uint dest, uint value) + { + dest = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + } + + private static void WriteUInt64LittleEndian(ref ulong dest, ulong value) + { + dest = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + } + } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index d675d433240dfd..92ae77fd7f7bfd 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -139,6 +139,47 @@ public struct COR_FIELD #pragma warning restore CS0649 +public enum AreValueTypesBoxed : int +{ + NoValueTypeBoxing = 0, + OnlyPrimitivesUnboxed = 1, + AllBoxed = 2 +}; +// Matches native DebuggerIPCE_BasicTypeData layout (24 bytes). +// All fields are stored in little-endian format (Portable in native). +[StructLayout(LayoutKind.Explicit, Size = 24)] +public struct DebuggerIPCE_BasicTypeData +{ + [FieldOffset(0)] public int elementType; // Portable + [FieldOffset(4)] public uint metadataToken; // Portable + [FieldOffset(8)] public ulong vmAssembly; // VMPTR_Assembly (Portable) + [FieldOffset(16)] public ulong vmTypeHandle; // VMPTR_TypeHandle (Portable) +} + +// Matches native DebuggerIPCE_ExpandedTypeData layout (40 bytes). +// Contains a union at offset 8 (4 bytes of padding after elementType to align the +// 8-byte VMPTR fields inside the union). All fields are stored in little-endian format. +[StructLayout(LayoutKind.Explicit, Size = 40)] +public struct DebuggerIPCE_ExpandedTypeData +{ + [FieldOffset(0)] public int elementType; // Portable + + // ClassTypeData (used for E_T_CLASS, E_T_VALUETYPE) + [FieldOffset(8)] public uint ClassTypeData_metadataToken; // Portable + [FieldOffset(16)] public ulong ClassTypeData_vmAssembly; // VMPTR_Assembly + [FieldOffset(24)] public ulong ClassTypeData_typeHandle; // VMPTR_TypeHandle + + // UnaryTypeData (used for E_T_PTR, E_T_BYREF) — overlaps union at offset 8 + [FieldOffset(8)] public DebuggerIPCE_BasicTypeData UnaryTypeData_unaryTypeArg; + + // ArrayTypeData (used for E_T_ARRAY, E_T_SZARRAY) — overlaps union at offset 8 + [FieldOffset(8)] public DebuggerIPCE_BasicTypeData ArrayTypeData_arrayTypeArg; + [FieldOffset(32)] public uint ArrayTypeData_arrayRank; // Portable + + // NaryTypeData (used for E_T_FNPTR) — overlaps union at offset 8 + [FieldOffset(8)] public ulong NaryTypeData_typeHandle; // VMPTR_TypeHandle +} + public enum DynamicMethodType { kNone = 0, @@ -370,13 +411,10 @@ public unsafe partial interface IDacDbiInterface int GetInstantiationFieldInfo(ulong vmAssembly, ulong vmTypeHandle, ulong vmExactMethodTable, nint pFieldList, nuint* pObjectSize); [PreserveSig] - int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, nint pData); - - [PreserveSig] - int GetObjectExpandedTypeInfo(int boxed, ulong addr, nint pTypeInfo); + int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pData); [PreserveSig] - int GetObjectExpandedTypeInfoFromID(int boxed, COR_TYPEID id, nint pTypeInfo); + int GetObjectExpandedTypeInfo(int boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo); [PreserveSig] int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal); diff --git a/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs b/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs index 7abd590dff17e1..24f920ce4dc53f 100644 --- a/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs +++ b/src/native/managed/cdac/tests/DumpTests/RuntimeTypeSystemDumpTests.cs @@ -49,6 +49,7 @@ public void RuntimeTypeSystem_ObjectMethodTableIsValid(TestConfiguration config) TypeHandle handle = rts.GetTypeHandle(objectMT); Assert.False(rts.IsFreeObjectMethodTable(handle)); + Assert.True(rts.IsObject(handle)); } [ConditionalTheory] @@ -65,6 +66,7 @@ public void RuntimeTypeSystem_FreeObjectMethodTableIsValid(TestConfiguration con TypeHandle handle = rts.GetTypeHandle(freeObjMT); Assert.True(rts.IsFreeObjectMethodTable(handle)); + Assert.False(rts.IsObject(handle)); } [ConditionalTheory] @@ -81,6 +83,7 @@ public void RuntimeTypeSystem_StringMethodTableIsString(TestConfiguration config TypeHandle handle = rts.GetTypeHandle(stringMT); Assert.True(rts.IsString(handle)); + Assert.False(rts.IsObject(handle)); } [ConditionalTheory] diff --git a/src/native/managed/cdac/tests/MethodTableTests.cs b/src/native/managed/cdac/tests/MethodTableTests.cs index e8c15ad11f6734..f7b2d18c1deb44 100644 --- a/src/native/managed/cdac/tests/MethodTableTests.cs +++ b/src/native/managed/cdac/tests/MethodTableTests.cs @@ -35,6 +35,7 @@ internal static (string Name, ulong Value)[] CreateContractGlobals(MockRTS rtsBu [ (nameof(Constants.Globals.FreeObjectMethodTable), rtsBuilder.FreeObjectMethodTableGlobalAddress), (nameof(Constants.Globals.ContinuationMethodTable), rtsBuilder.ContinuationMethodTableGlobalAddress), + (nameof(Constants.Globals.ObjectMethodTable), rtsBuilder.ObjectMethodTableGlobalAddress), (nameof(Constants.Globals.MethodDescAlignment), rtsBuilder.MethodDescAlignment), (nameof(Constants.Globals.ArrayBaseSize), rtsBuilder.ArrayBaseSize), ]; @@ -76,6 +77,7 @@ public void HasRuntimeTypeSystemContract(MockTarget.Architecture arch) Contracts.TypeHandle handle = contract.GetTypeHandle(freeObjectMethodTableAddress); Assert.NotEqual(TargetPointer.Null, handle.Address); Assert.True(contract.IsFreeObjectMethodTable(handle)); + Assert.False(contract.IsObject(handle)); } [Theory] @@ -91,6 +93,7 @@ public void ValidateSystemObjectMethodTable(MockTarget.Architecture arch) Contracts.TypeHandle systemObjectTypeHandle = contract.GetTypeHandle(systemObjectMethodTablePtr); Assert.Equal(systemObjectMethodTablePtr.Value, systemObjectTypeHandle.Address.Value); Assert.False(contract.IsFreeObjectMethodTable(systemObjectTypeHandle)); + Assert.True(contract.IsObject(systemObjectTypeHandle)); } [Theory] diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs index ccf99761dd4dd4..33e94f1e4419f2 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.RuntimeTypeSystem.cs @@ -288,6 +288,7 @@ public class RuntimeTypeSystem { internal const ulong TestFreeObjectMethodTableGlobalAddress = 0x00000000_7a0000a0; internal const ulong TestContinuationMethodTableGlobalAddress = 0x00000000_7a0000b0; + internal const ulong TestObjectMethodTableGlobalAddress = 0x00000000_7a0000c0; private const ulong DefaultAllocationRangeStart = 0x00000000_4a000000; private const ulong DefaultAllocationRangeEnd = 0x00000000_4b000000; @@ -314,6 +315,7 @@ public class RuntimeTypeSystem internal ulong FreeObjectMethodTableAddress { get; private set; } internal ulong FreeObjectMethodTableGlobalAddress => TestFreeObjectMethodTableGlobalAddress; internal ulong ContinuationMethodTableGlobalAddress => TestContinuationMethodTableGlobalAddress; + internal ulong ObjectMethodTableGlobalAddress => TestObjectMethodTableGlobalAddress; internal ulong MethodDescAlignment => GetMethodDescAlignment(Builder.TargetTestHelpers); internal ulong ArrayBaseSize => Builder.TargetTestHelpers.ArrayBaseBaseSize; // sizeof(ContinuationObject) = sizeof(MT*) + sizeof(Next*) + sizeof(Resume*) + sizeof(Flags) + sizeof(State) @@ -346,6 +348,7 @@ private void AddGlobalPointers() { AddFreeObjectMethodTable(); AddContinuationMethodTableGlobal(); + AddObjectMethodTableGlobal(); } private void AddDefaultTypes() @@ -366,6 +369,12 @@ private void AddContinuationMethodTableGlobal() AddPointerGlobal("Address of Continuation Method Table", TestContinuationMethodTableGlobalAddress, 0); } + private void AddObjectMethodTableGlobal() + { + // Initialized to 0; patched to point to SystemObjectMethodTable in AddSystemObjectType. + AddPointerGlobal("Address of Object Method Table", TestObjectMethodTableGlobalAddress, 0); + } + private void AddSystemObjectType() { const int NumMethods = 8; @@ -380,6 +389,10 @@ private void AddSystemObjectType() SystemObjectMethodTable.NumVirtuals = NumVirtuals; SystemObjectEEClass.MethodTable = SystemObjectMethodTable.Address; SystemObjectMethodTable.EEClassOrCanonMT = SystemObjectEEClass.Address; + + // Patch the ObjectMethodTable global to point to System.Object's MethodTable. + Span globalAddrBytes = Builder.BorrowAddressRange(TestObjectMethodTableGlobalAddress, Builder.TargetTestHelpers.PointerSize); + Builder.TargetTestHelpers.WritePointer(globalAddrBytes, SystemObjectMethodTable.Address); } private void AddContinuationType() From a2de94ac12423b9b5dc298ccf65da954a5a88d11 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Sun, 10 May 2026 08:54:39 -0700 Subject: [PATCH 02/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/coreclr/debug/di/rstype.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/debug/di/rstype.cpp b/src/coreclr/debug/di/rstype.cpp index 640083218a3516..1ac4d2be67b1a5 100644 --- a/src/coreclr/debug/di/rstype.cpp +++ b/src/coreclr/debug/di/rstype.cpp @@ -1163,8 +1163,7 @@ HRESULT CordbType::TypeDataToType(CordbAppDomain *pAppDomain, DebuggerIPCE_Basic { RSLockHolder lockHolder(pProcess->GetProcessLock()); - UINT64 id; - memcpy(&id, &data->vmTypeHandle, sizeof(UINT64)); + UINT64 id = VmPtrToCookie(data->vmTypeHandle); IfFailThrow(pProcess->GetDAC()->TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, // could be generics which are never boxed id, &typeInfo)); From 511c52bbfad2e77416ab629f2439bbeff5cff926 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Mon, 11 May 2026 20:45:14 -0700 Subject: [PATCH 03/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Dbi/IDacDbiInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index 1761f19874637b..a06ca7df655868 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -158,7 +158,7 @@ public enum AreValueTypesBoxed : int NoValueTypeBoxing = 0, OnlyPrimitivesUnboxed = 1, AllBoxed = 2 -}; +} // Matches native DebuggerIPCE_BasicTypeData layout (24 bytes). // All fields are stored in little-endian format (Portable in native). [StructLayout(LayoutKind.Explicit, Size = 24)] From cb5c605de6d17463727cffc42353feba803a1b79 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Tue, 12 May 2026 10:17:18 -0700 Subject: [PATCH 04/10] memcopy --- src/coreclr/debug/di/rstype.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/di/rstype.cpp b/src/coreclr/debug/di/rstype.cpp index 1ac4d2be67b1a5..17b8fb4510a9a0 100644 --- a/src/coreclr/debug/di/rstype.cpp +++ b/src/coreclr/debug/di/rstype.cpp @@ -1163,9 +1163,11 @@ HRESULT CordbType::TypeDataToType(CordbAppDomain *pAppDomain, DebuggerIPCE_Basic { RSLockHolder lockHolder(pProcess->GetProcessLock()); - UINT64 id = VmPtrToCookie(data->vmTypeHandle); + UINT64 vmTypeHandleRaw = 0; + static_assert(sizeof(data->vmTypeHandle) <= sizeof(vmTypeHandleRaw), "VMPTR_TypeHandle is larger than UINT64"); + memcpy(&vmTypeHandleRaw, &data->vmTypeHandle, sizeof(data->vmTypeHandle)); IfFailThrow(pProcess->GetDAC()->TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, // could be generics which are never boxed - id, + vmTypeHandleRaw, &typeInfo)); } From 41ecc4bb360628a17bef70de302754f60d8f00ce Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Tue, 12 May 2026 10:22:47 -0700 Subject: [PATCH 05/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/design/datacontracts/RuntimeTypeSystem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index b994961a8d75d7..d267b5746b065d 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -441,7 +441,7 @@ The contract depends on the following globals | Global name | Meaning | | --- | --- | | `ContinuationMethodTable` | A pointer to the address of the base `Continuation` `MethodTable`, or null if no continuations have been created -| `FreeObjectMethodTablePointer` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory +| `FreeObjectMethodTable` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory | `ObjectMethodTable` | A pointer to the address of the `System.Object` `MethodTable` (`g_pObjectClass`) | `StaticsPointerMask` | For masking out a bit of DynamicStaticsInfo pointer fields | `ArrayBaseSize` | The base size of an array object; used to compute multidimensional array rank from `MethodTable::BaseSize` From b1185ddf27ba62641535e5c287ef9e3540180a20 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Fri, 15 May 2026 10:18:52 -0700 Subject: [PATCH 06/10] moving zero init --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 17 ++++++++++------- .../Dbi/DacDbiImpl.cs | 3 +-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 9dbcd42e2425d2..8db46583f83f13 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2475,7 +2475,8 @@ void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle case ELEMENT_TYPE_PTR: case ELEMENT_TYPE_BYREF: pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); - // metadataToken and vmAssembly stay zero (mdTokenNil / NullPtr) + pTypeInfo->metadataToken = mdTokenNil; + pTypeInfo->vmAssembly = VMPTR_Assembly::NullPtr(); break; case ELEMENT_TYPE_CLASS: @@ -2488,7 +2489,10 @@ void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle { pTypeInfo->vmTypeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); } - // else: vmTypeHandle stays null + else + { + pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + } pTypeInfo->metadataToken = typeHandle.GetCl(); _ASSERTE(pModule); @@ -2497,7 +2501,9 @@ void DacDbiInterfaceImpl::TypeHandleToBasicTypeInfo(TypeHandle } default: - // All fields zero + pTypeInfo->vmTypeHandle = VMPTR_TypeHandle::NullPtr(); + pTypeInfo->metadataToken = mdTokenNil; + pTypeInfo->vmAssembly = VMPTR_Assembly::NullPtr(); break; } return; @@ -2511,8 +2517,6 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValu HRESULT hr = S_OK; EX_TRY { - *pTypeInfo = {}; - PTR_Object obj(TO_TADDR(addr)); TypeHandleToExpandedTypeInfoImpl(boxed, obj->GetGCSafeTypeHandle(), pTypeInfo); } @@ -2528,8 +2532,6 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreV HRESULT hr = S_OK; EX_TRY { - *pTypeInfo = {}; - TypeHandle typeHandle = TypeHandle::FromPtr((TADDR)vmTypeHandle); TypeHandleToExpandedTypeInfoImpl(boxed, typeHandle, pTypeInfo); } @@ -2542,6 +2544,7 @@ void DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfoImpl(AreValueTypesBoxed TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) { + *pTypeInfo = {}; pTypeInfo->elementType = GetElementType(typeHandle); switch (pTypeInfo->elementType) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 148e6c644b1964..5ff8bca62a57cc 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -1280,7 +1280,6 @@ public int GetInstantiationFieldInfo(ulong vmAssembly, ulong vmTypeHandle, ulong public int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) { - *pTypeInfo = default; int hr = HResults.S_OK; try { @@ -1309,7 +1308,6 @@ public int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, DebuggerI public int GetObjectExpandedTypeInfo(int boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo) { - *pTypeInfo = default; int hr = HResults.S_OK; try { @@ -2305,6 +2303,7 @@ public int GetGenericArgTokenIndex(ulong vmMethod, uint* pIndex) // Shared core implementation for TypeHandleToExpandedTypeInfo and GetObjectExpandedTypeInfo. private void TypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, TypeHandle typeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) { + *pTypeInfo = default; CorElementType elementType = GetElementType(rts, typeHandle); WriteInt32LittleEndian(ref pTypeInfo->elementType, (int)elementType); From a0920bf0e10648e97d737a4b9ebe830535f85605 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Fri, 15 May 2026 10:21:01 -0700 Subject: [PATCH 07/10] moving zero init --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 8db46583f83f13..614d91d4ef0d0f 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2414,7 +2414,10 @@ void DacDbiInterfaceImpl::GetClassTypeInfo(TypeHandle typeH { pTypeInfo->ClassTypeData.typeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); } - // else: non-generic — typeHandle stays null + else // non-generic + { + pTypeInfo->ClassTypeData.typeHandle = VMPTR_TypeHandle::NullPtr(); + } pTypeInfo->ClassTypeData.metadataToken = typeHandle.GetCl(); From 2fbaf2b8505321f99d6125897b1a4cf62a5daa24 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Sat, 16 May 2026 21:12:58 -0700 Subject: [PATCH 08/10] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 614d91d4ef0d0f..f8c5123cbe0c4b 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2414,7 +2414,7 @@ void DacDbiInterfaceImpl::GetClassTypeInfo(TypeHandle typeH { pTypeInfo->ClassTypeData.typeHandle.SetDacTargetPtr(typeHandle.AsTAddr()); } - else // non-generic + else // non-generic { pTypeInfo->ClassTypeData.typeHandle = VMPTR_TypeHandle::NullPtr(); } From 3bf8bc13f053c218c8ae3e0920daf5a30e3050f1 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 18 May 2026 11:46:34 -0700 Subject: [PATCH 09/10] code review --- .../Dbi/DacDbiImpl.cs | 65 ++++++++++--------- .../Dbi/IDacDbiInterface.cs | 4 +- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs index 5ff8bca62a57cc..a2e3b96b044791 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Numerics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Microsoft.Diagnostics.DataContractReader.Contracts; @@ -1278,14 +1279,14 @@ public int GetClassInfo(ulong thExact, nint pData) public int GetInstantiationFieldInfo(ulong vmAssembly, ulong vmTypeHandle, ulong vmExactMethodTable, nint pFieldList, nuint* pObjectSize) => LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetInstantiationFieldInfo(vmAssembly, vmTypeHandle, vmExactMethodTable, pFieldList, pObjectSize) : HResults.E_NOTIMPL; - public int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + public int TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pTypeInfo) { int hr = HResults.S_OK; try { IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; TypeHandle th = rts.GetTypeHandle(new TargetPointer(vmTypeHandle)); - TypeHandleToExpandedTypeInfoImpl(rts, (AreValueTypesBoxed)boxed, th, pTypeInfo); + TypeHandleToExpandedTypeInfoImpl(rts, boxed, th, pTypeInfo); } catch (System.Exception ex) { @@ -1306,7 +1307,7 @@ public int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, DebuggerI return hr; } - public int GetObjectExpandedTypeInfo(int boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo) + public int GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo) { int hr = HResults.S_OK; try @@ -1314,7 +1315,7 @@ public int GetObjectExpandedTypeInfo(int boxed, ulong addr, DebuggerIPCE_Expande IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; TargetPointer mtAddr = _target.Contracts.Object.GetMethodTableAddress(new TargetPointer(addr)); TypeHandle th = rts.GetTypeHandle(mtAddr); - TypeHandleToExpandedTypeInfoImpl(rts, (AreValueTypesBoxed)boxed, th, pTypeInfo); + TypeHandleToExpandedTypeInfoImpl(rts, boxed, th, pTypeInfo); } catch (System.Exception ex) { @@ -1340,7 +1341,7 @@ private static void ValidateExpandedTypeData(DebuggerIPCE_ExpandedTypeData* cdac { Debug.Assert(cdac->elementType == dac->elementType, $"cDAC elementType: {cdac->elementType}, DAC: {dac->elementType}"); - switch ((CorElementType)cdac->elementType) + switch ((CorElementType)ReadLittleEndian(cdac->elementType)) { case CorElementType.Class: case CorElementType.ValueType: @@ -2305,7 +2306,7 @@ private void TypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTy { *pTypeInfo = default; CorElementType elementType = GetElementType(rts, typeHandle); - WriteInt32LittleEndian(ref pTypeInfo->elementType, (int)elementType); + WriteLittleEndian(ref pTypeInfo->elementType, (int)elementType); switch (elementType) { @@ -2322,7 +2323,7 @@ private void TypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTy case CorElementType.ValueType: if (boxed == AreValueTypesBoxed.OnlyPrimitivesUnboxed || boxed == AreValueTypesBoxed.AllBoxed) { - WriteInt32LittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); + WriteLittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); } FillClassTypeInfo(rts, typeHandle, pTypeInfo); break; @@ -2338,7 +2339,7 @@ private void TypeHandleToExpandedTypeInfoImpl(IRuntimeTypeSystem rts, AreValueTy default: if (boxed == AreValueTypesBoxed.AllBoxed) { - WriteInt32LittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); + WriteLittleEndian(ref pTypeInfo->elementType, (int)CorElementType.Class); FillClassTypeInfo(rts, typeHandle, pTypeInfo); } break; @@ -2381,7 +2382,7 @@ private void FillArrayTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, De { Debug.Assert(rts.IsArray(typeHandle, out _)); rts.IsArray(typeHandle, out uint rank); - WriteUInt32LittleEndian(ref pTypeInfo->ArrayTypeData_arrayRank, rank); + WriteLittleEndian(ref pTypeInfo->ArrayTypeData_arrayRank, rank); TypeHandle elemTypeHandle = rts.GetTypeParam(typeHandle); FillBasicTypeInfo(rts, elemTypeHandle, out pTypeInfo->ArrayTypeData_arrayTypeArg); } @@ -2413,14 +2414,14 @@ private void FillClassTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, De if (instantiation.Length > 0) { // Generic instantiation — set the type handle so the debugger can fetch type arguments - WriteUInt64LittleEndian(ref pTypeInfo->ClassTypeData_typeHandle, typeHandle.Address.Value); + WriteLittleEndian(ref pTypeInfo->ClassTypeData_typeHandle, typeHandle.Address.Value); } // else: non-generic — typeHandle stays null - WriteUInt32LittleEndian(ref pTypeInfo->ClassTypeData_metadataToken, rts.GetTypeDefToken(typeHandle)); + WriteLittleEndian(ref pTypeInfo->ClassTypeData_metadataToken, rts.GetTypeDefToken(typeHandle)); Debug.Assert(modulePtr != TargetPointer.Null); - WriteUInt64LittleEndian(ref pTypeInfo->ClassTypeData_vmAssembly, loader.GetAssembly(moduleHandle).Value); + WriteLittleEndian(ref pTypeInfo->ClassTypeData_vmAssembly, loader.GetAssembly(moduleHandle).Value); } // Fills NaryTypeData for E_T_FNPTR (or ClassTypeData if AllBoxed). @@ -2432,7 +2433,7 @@ private void FillFnPtrTypeInfo(IRuntimeTypeSystem rts, AreValueTypesBoxed boxed, } else { - WriteUInt64LittleEndian(ref pTypeInfo->NaryTypeData_typeHandle, typeHandle.Address.Value); + WriteLittleEndian(ref pTypeInfo->NaryTypeData_typeHandle, typeHandle.Address.Value); } } @@ -2442,7 +2443,7 @@ private void FillBasicTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, ou { typeInfo = default; CorElementType elementType = GetElementType(rts, typeHandle); - WriteInt32LittleEndian(ref typeInfo.elementType, (int)elementType); + WriteLittleEndian(ref typeInfo.elementType, (int)elementType); switch (elementType) { @@ -2451,7 +2452,7 @@ private void FillBasicTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, ou case CorElementType.FnPtr: case CorElementType.Ptr: case CorElementType.Byref: - WriteUInt64LittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); + WriteLittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); // metadataToken and vmAssembly stay zero break; @@ -2467,13 +2468,13 @@ private void FillBasicTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, ou ReadOnlySpan instantiation = rts.GetInstantiation(typeHandle); if (instantiation.Length > 0) { - WriteUInt64LittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); + WriteLittleEndian(ref typeInfo.vmTypeHandle, typeHandle.Address.Value); } // else: vmTypeHandle stays null - WriteUInt32LittleEndian(ref typeInfo.metadataToken, rts.GetTypeDefToken(typeHandle)); + WriteLittleEndian(ref typeInfo.metadataToken, rts.GetTypeDefToken(typeHandle)); Debug.Assert(modulePtr != TargetPointer.Null); - WriteUInt64LittleEndian(ref typeInfo.vmAssembly, loader.GetAssembly(moduleHandle).Value); + WriteLittleEndian(ref typeInfo.vmAssembly, loader.GetAssembly(moduleHandle).Value); break; } @@ -2483,22 +2484,28 @@ private void FillBasicTypeInfo(IRuntimeTypeSystem rts, TypeHandle typeHandle, ou } } - // Little-endian write helpers for IPCE structs. + // Little-endian read/write helpers for IPCE structs. // Native IPCE structs use Portable, which stores data in little-endian format. - // These helpers ensure managed writes match that convention. - private static void WriteInt32LittleEndian(ref int dest, int value) + // These helpers ensure managed reads/writes match that convention. + private static void WriteLittleEndian(ref T dest, T value) where T : unmanaged, IBinaryInteger { - dest = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); - } - - private static void WriteUInt32LittleEndian(ref uint dest, uint value) - { - dest = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + if (BitConverter.IsLittleEndian) + { + dest = value; + } + else + { + Span destBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref dest, 1)); + value.WriteLittleEndian(destBytes); + } } - private static void WriteUInt64LittleEndian(ref ulong dest, ulong value) + private static T ReadLittleEndian(T value) where T : unmanaged, IBinaryInteger { - dest = BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + if (BitConverter.IsLittleEndian) + return value; + MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref value, 1)).Reverse(); + return value; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs index a06ca7df655868..ee33f5ff5e659b 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs @@ -425,10 +425,10 @@ public unsafe partial interface IDacDbiInterface int GetInstantiationFieldInfo(ulong vmAssembly, ulong vmTypeHandle, ulong vmExactMethodTable, nint pFieldList, nuint* pObjectSize); [PreserveSig] - int TypeHandleToExpandedTypeInfo(int boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pData); + int TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, ulong vmTypeHandle, DebuggerIPCE_ExpandedTypeData* pData); [PreserveSig] - int GetObjectExpandedTypeInfo(int boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo); + int GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, ulong addr, DebuggerIPCE_ExpandedTypeData* pTypeInfo); [PreserveSig] int GetTypeHandle(ulong vmModule, uint metadataToken, ulong* pRetVal); From 0fb2bcefdb4f87734137f3ef515e3ff57c959491 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 18 May 2026 15:43:19 -0700 Subject: [PATCH 10/10] change uint64 to cordb_address --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 10 +++++----- src/coreclr/debug/daccess/dacdbiimpl.h | 2 +- src/coreclr/debug/di/rstype.cpp | 4 ++-- src/coreclr/debug/inc/dacdbiinterface.h | 2 +- src/coreclr/inc/dacdbi.idl | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index f8c5123cbe0c4b..00c80b7eca0403 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -2528,7 +2528,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetObjectExpandedTypeInfo(AreValu } // DacDbi API: use a type handle to get the information needed to create the corresponding RS CordbType instance -HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, UINT64 vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) +HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) { DD_ENTER_MAY_THROW; @@ -2761,7 +2761,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetMethodDescParams(VMPTR_MethodD { // Fill in the struct using the TypeHandle of the current type parameter if we can. IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - (UINT64)thCurrent.AsTAddr(), + (CORDB_ADDRESS)thCurrent.AsTAddr(), &((*pGenericTypeParams)[i]))); } EX_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER @@ -2769,7 +2769,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetMethodDescParams(VMPTR_MethodD // On failure for a particular type, default it back to System.__Canon. TypeHandle thCanon = TypeHandle(g_pCanonMethodTableClass); IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - (UINT64)thCanon.AsTAddr(), + (CORDB_ADDRESS)thCanon.AsTAddr(), &((*pGenericTypeParams)[i]))); } EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY_WITH_HANDLER @@ -3195,7 +3195,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetTypeHandleParams(VMPTR_TypeHan for (unsigned int i = 0; i < pParams->Count(); ++i) { IfFailThrow(TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, - (UINT64)typeHandle.GetInstantiation()[i].AsTAddr(), + (CORDB_ADDRESS)typeHandle.GetInstantiation()[i].AsTAddr(), &((*pParams)[i]))); } @@ -6073,7 +6073,7 @@ void DacDbiInterfaceImpl::InitObjectData(PTR_Object objPtr, pObjectData->objSize = objPtr->GetSize(); pObjectData->objOffsetToVars = dac_cast((objPtr)->GetData()) - dac_cast(objPtr); - IfFailThrow(TypeHandleToExpandedTypeInfo(AllBoxed, (UINT64)objPtr->GetGCSafeTypeHandle().AsTAddr(), &(pObjectData->objTypeData))); + IfFailThrow(TypeHandleToExpandedTypeInfo(AllBoxed, (CORDB_ADDRESS)objPtr->GetGCSafeTypeHandle().AsTAddr(), &(pObjectData->objTypeData))); // If this is a string object, set the type to ELEMENT_TYPE_STRING. if (objPtr->GetGCSafeMethodTable() == g_pStringClass) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index a90632b1869cb7..3e184aa277a008 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -247,7 +247,7 @@ class DacDbiInterfaceImpl : // @dbgtodo Microsoft inspection: change DebuggerIPCE_ExpandedTypeData to DacDbiStructures type hierarchy // once ICorDebugType and ICorDebugClass are DACized // use a type handle to get the information needed to create the corresponding RS CordbType instance - HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, UINT64 vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo); + HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo); // Get type handle for a TypeDef token, if one exists. For generics this returns the open type. HRESULT STDMETHODCALLTYPE GetTypeHandle(VMPTR_Module vmModule, mdTypeDef metadataToken, OUT VMPTR_TypeHandle * pRetVal); diff --git a/src/coreclr/debug/di/rstype.cpp b/src/coreclr/debug/di/rstype.cpp index 17b8fb4510a9a0..79ea8dabe419c5 100644 --- a/src/coreclr/debug/di/rstype.cpp +++ b/src/coreclr/debug/di/rstype.cpp @@ -1163,8 +1163,8 @@ HRESULT CordbType::TypeDataToType(CordbAppDomain *pAppDomain, DebuggerIPCE_Basic { RSLockHolder lockHolder(pProcess->GetProcessLock()); - UINT64 vmTypeHandleRaw = 0; - static_assert(sizeof(data->vmTypeHandle) <= sizeof(vmTypeHandleRaw), "VMPTR_TypeHandle is larger than UINT64"); + CORDB_ADDRESS vmTypeHandleRaw = 0; + static_assert(sizeof(data->vmTypeHandle) <= sizeof(vmTypeHandleRaw), "VMPTR_TypeHandle is larger than CORDB_ADDRESS"); memcpy(&vmTypeHandleRaw, &data->vmTypeHandle, sizeof(data->vmTypeHandle)); IfFailThrow(pProcess->GetDAC()->TypeHandleToExpandedTypeInfo(NoValueTypeBoxing, // could be generics which are never boxed vmTypeHandleRaw, diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index fdb082337ebcbb..ec2217eb7905a6 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1549,7 +1549,7 @@ IDacDbiInterface : public IUnknown // vmTypeHandle - type handle for the type // output: pTypeInfo - holds information needed to build the corresponding CordbType // - virtual HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, UINT64 vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; + virtual HRESULT STDMETHODCALLTYPE TypeHandleToExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS vmTypeHandle, DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; virtual HRESULT STDMETHODCALLTYPE GetObjectExpandedTypeInfo(AreValueTypesBoxed boxed, CORDB_ADDRESS addr, OUT DebuggerIPCE_ExpandedTypeData * pTypeInfo) = 0; diff --git a/src/coreclr/inc/dacdbi.idl b/src/coreclr/inc/dacdbi.idl index d8b41133fdef55..2ea64861a02022 100644 --- a/src/coreclr/inc/dacdbi.idl +++ b/src/coreclr/inc/dacdbi.idl @@ -310,7 +310,7 @@ interface IDacDbiInterface : IUnknown [in] VMPTR_TypeHandle vmThApprox, [out] DacDbiArrayList_FieldData * pFieldList, [out] SIZE_T * pObjectSize); - HRESULT TypeHandleToExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] UINT64 vmTypeHandle, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); + HRESULT TypeHandleToExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] CORDB_ADDRESS vmTypeHandle, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); HRESULT GetObjectExpandedTypeInfo([in] AreValueTypesBoxed boxed, [in] CORDB_ADDRESS addr, [out] struct DebuggerIPCE_ExpandedTypeData * pTypeInfo); HRESULT GetTypeHandle([in] VMPTR_Module vmModule, [in] mdTypeDef metadataToken, [out] VMPTR_TypeHandle * pRetVal); HRESULT GetApproxTypeHandle([in] TypeInfoList * pTypeData, [out] VMPTR_TypeHandle * pRetVal);