From 875869c2304383a95c4f7a29871ba378ef199f9d Mon Sep 17 00:00:00 2001 From: rcj1 Date: Sat, 27 Jun 2026 17:29:02 -0700 Subject: [PATCH 01/10] add EnC metadata and caching --- docs/design/datacontracts/EcmaMetadata.md | 278 ++++-------------- src/coreclr/md/enc/liteweightstgdbrw.cpp | 1 + src/coreclr/md/enc/metamodelrw.cpp | 3 + src/coreclr/md/inc/liteweightstgdb.h | 9 + src/coreclr/md/inc/metamodel.h | 9 + src/coreclr/md/inc/metamodelrw.h | 14 + src/coreclr/md/inc/stgpool.h | 21 +- src/coreclr/vm/ceeload.cpp | 2 + src/coreclr/vm/ceeload.h | 5 + src/coreclr/vm/datadescriptor/CMakeLists.txt | 2 + .../vm/datadescriptor/datadescriptor.h | 4 + .../vm/datadescriptor/datadescriptor.inc | 56 ++++ src/coreclr/vm/encee.h | 8 + src/coreclr/vm/peassembly.h | 6 + .../Contracts/EcmaMetadata_1.cs | 244 +++++++++++---- .../Data/CLiteWeightStgdbRW.cs | 11 + .../Data/CMiniMdRW.cs | 35 +++ .../Data/CMiniMdSchema.cs | 12 + .../Data/EditAndContinueModule.cs | 10 + .../Data/MDInternalRW.cs | 10 + .../Data/Module.cs | 1 + .../Data/PEAssembly.cs | 2 + .../Data/StgPool.cs | 12 + .../Data/StgPoolSeg.cs | 12 + .../DataType.cs | 8 + .../EcmaMetadataUtils.cs | 23 ++ 26 files changed, 524 insertions(+), 274 deletions(-) create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CLiteWeightStgdbRW.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdSchema.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MDInternalRW.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPool.cs create mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPoolSeg.cs diff --git a/docs/design/datacontracts/EcmaMetadata.md b/docs/design/datacontracts/EcmaMetadata.md index a5b445f10dce6a..fff25e4b3eb378 100644 --- a/docs/design/datacontracts/EcmaMetadata.md +++ b/docs/design/datacontracts/EcmaMetadata.md @@ -23,9 +23,40 @@ Data descriptors used: | --- | --- | --- | | `Module` | `Base` | Pointer to start of PE file in memory | | `Module` | `DynamicMetadata` | Pointer to saved metadata for reflection emit modules | +| `Module` | `MetadataGeneration` | Counter incremented each time a dynamic module re-serializes its saved metadata copy | | `Module` | `FieldDefToDescMap` | Mapping table | +| `Module` | `PEAssembly` | Pointer to the module's `PEAssembly` | +| `Module` | `Flags` | Module transient flags | +| `EditAndContinueModule` | `ApplyChangesCount` | Counter incremented each time an EnC edit is applied | | `DynamicMetadata` | `Size` | Size of the dynamic metadata blob (as a 32bit uint) | | `DynamicMetadata` | `Data` | Start of dynamic metadata data array | +| `PEAssembly` | `MDImportIsRW` | Non-zero when `MDImport` is a read-write importer | +| `PEAssembly` | `MDImport` | An `MDInternalRW` when `MDImportIsRW` is set | +| `MDInternalRW` | `Stgdb` | Pointer to the read-write storage database | +| `CLiteWeightStgdbRW` | `MiniMd` | Address of the embedded `CMiniMdRW` model | +| `CLiteWeightStgdbRW` | `MetadataAddress` | Pointer to the metadata image | +| `CMiniMdRW` | `Schema` | Address of the embedded `CMiniMdSchema` | +| `CMiniMdRW` | `TableCount` | Number of valid tables | +| `CMiniMdRW` | `All4ByteColumns` | Whether all variable-width columns are 4 bytes wide | +| `CMiniMdRW` | `Tables` | Address of the first table's record storage pool | +| `CMiniMdRW` | `StringHeap` | Address of the string heap's storage pool | +| `CMiniMdRW` | `BlobHeap` | Address of the blob heap's storage pool | +| `CMiniMdRW` | `UserStringHeap` | Address of the user-string heap's storage pool | +| `CMiniMdRW` | `GuidHeap` | Address of the GUID heap's storage pool | +| `CMiniMdSchema` | `Heaps` | Heap-size flags byte | +| `CMiniMdSchema` | `Sorted` | Sorted-table bit mask | +| `CMiniMdSchema` | `RecordCounts` | Address of the inline per-table row count array | +| `StgPool` | `SegData` | Pointer to the head segment's data | +| `StgPool` | `NextSegment` | Pointer to the next pool segment | +| `StgPool` | `DataSize` | Live byte count of the head segment | +| `StgPoolSeg` | `SegData` | Pointer to this extension segment's data | +| `StgPoolSeg` | `NextSegment` | Pointer to the next pool segment, or null | +| `StgPoolSeg` | `DataSize` | Live byte count of this extension segment | + +### Contract Constants: +| Name | Type | Purpose | Value | +| --- | --- | --- | --- | +| `ModuleFlagsEncCapable` | uint | `Module` transient-flags bit (`IS_ENC_CAPABLE`) indicating the module is an `EditAndContinueModule` | `0x200` | ```csharp @@ -75,152 +106,12 @@ MetadataReader? GetMetadata(ModuleHandle handle) } case AvailableMetadataType.ReadWrite: { - var targetEcmaMetadata = GetReadWriteMetadata(handle); - - // From the multiple different target spans, we need to build a single - // contiguous ECMA-335 metadata blob. - BlobBuilder builder = new BlobBuilder(); - builder.WriteUInt32(0x424A5342); - - // major version - builder.WriteUInt16(1); - - // minor version - builder.WriteUInt16(1); - - // reserved - builder.WriteUInt32(0); - - string version = targetEcmaMetadata.Schema.MetadataVersion; - builder.WriteInt32(AlignUp(version.Length, 4)); - Write4ByteAlignedString(builder, version); - - // reserved - builder.WriteUInt16(0); - - // number of streams - ushort numStreams = 5; // #Strings, #US, #Blob, #GUID, #~ (metadata) - if (targetEcmaMetadata.Schema.VariableSizedColumnsAreAll4BytesLong) - { - // We direct MetadataReader to use 4-byte encoding for all variable-sized columns - // by providing the marker stream for a "minimal delta" image. - numStreams++; - } - builder.WriteUInt16(numStreams); - - // Write Stream headers - if (targetEcmaMetadata.Schema.VariableSizedColumnsAreAll4BytesLong) - { - // Write the #JTD stream to indicate that all variable-sized columns are 4 bytes long. - WriteStreamHeader(builder, "#JTD", 0).WriteInt32(builder.Count); - } - - BlobWriter stringsOffset = WriteStreamHeader(builder, "#Strings", (int)AlignUp(targetEcmaMetadata.StringHeap.Size, 4ul)); - BlobWriter blobOffset = WriteStreamHeader(builder, "#Blob", (int)targetEcmaMetadata.BlobHeap.Size); - BlobWriter guidOffset = WriteStreamHeader(builder, "#GUID", (int)targetEcmaMetadata.GuidHeap.Size); - BlobWriter userStringOffset = WriteStreamHeader(builder, "#US", (int)targetEcmaMetadata.UserStringHeap.Size); - - // We'll use the "uncompressed" tables stream name as the runtime may have created the *Ptr tables - // that are only present in the uncompressed tables stream. - BlobWriter tablesOffset = WriteStreamHeader(builder, "#-", 0); - - // Write the heap-style Streams - - stringsOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.StringHeap); - for (ulong i = targetEcmaMetadata.StringHeap.Size; i < AlignUp(targetEcmaMetadata.StringHeap.Size, 4ul); i++) - { - builder.WriteByte(0); - } - - blobOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.BlobHeap); - - guidOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.GuidHeap); - - userStringOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.UserStringHeap); - - // Write tables stream - tablesOffset.WriteInt32(builder.Count); - - // Write tables stream header - builder.WriteInt32(0); // reserved - builder.WriteByte(2); // major version - builder.WriteByte(0); // minor version - uint heapSizes = - (targetEcmaMetadata.Schema.LargeStringHeap ? 1u << 0 : 0) | - (targetEcmaMetadata.Schema.LargeBlobHeap ? 1u << 1 : 0) | - (targetEcmaMetadata.Schema.LargeGuidHeap ? 1u << 2 : 0); - - builder.WriteByte((byte)heapSizes); - builder.WriteByte(1); // reserved - - ulong validTables = 0; - for (int i = 0; i < targetEcmaMetadata.Schema.RowCount.Length; i++) - { - if (targetEcmaMetadata.Schema.RowCount[i] != 0) - { - validTables |= 1ul << i; - } - } - - ulong sortedTables = 0; - for (int i = 0; i < targetEcmaMetadata.Schema.IsSorted.Length; i++) - { - if (targetEcmaMetadata.Schema.IsSorted[i]) - { - sortedTables |= 1ul << i; - } - } - - builder.WriteUInt64(validTables); - builder.WriteUInt64(sortedTables); - - foreach (int rowCount in targetEcmaMetadata.Schema.RowCount) - { - if (rowCount > 0) - { - builder.WriteInt32(rowCount); - } - } - - // Write the tables - foreach (TargetSpan span in targetEcmaMetadata.Tables) - { - WriteTargetSpan(builder, span); - } - - MemoryStream metadataStream = new MemoryStream(); - builder.WriteContentTo(metadataStream); - return MetadataReaderProvider.FromMetadataStream(metadataStream).GetMetadataReader(); - - void WriteTargetSpan(BlobBuilder builder, TargetSpan span) - { - Blob blob = builder.ReserveBytes(checked((int)span.Size)); - _target.ReadBuffer(span.Address, blob.GetBytes().AsSpan()); - } - - static BlobWriter WriteStreamHeader(BlobBuilder builder, string name, int size) - { - BlobWriter offset = new(builder.ReserveBytes(4)); - builder.WriteInt32(size); - Write4ByteAlignedString(builder, name); - return offset; - } - - static void Write4ByteAlignedString(BlobBuilder builder, string value) - { - int bufferStart = builder.Count; - builder.WriteUTF8(value); - builder.WriteByte(0); - int stringEnd = builder.Count; - for (int i = stringEnd; i < bufferStart + AlignUp(value.Length, 4); i++) - { - builder.WriteByte(0); - } - } + // From the ModuleHandle, walk Module -> PEAssembly -> MDInternalRW -> CLiteWeightStgdbRW -> CMiniMdRW. + // Read the schema and whether every variable-width column is 4 bytes, which selects the #JTD "minimal delta" marker when reserializing. + // Each heap and each entry of the Tables array is a storage pool; read its head segment with the + // StgPool descriptor and walk the rest of the chain as bare StgPoolSeg via NextSegment, concatenating every segment's + // data into a contiguous buffer. The resulting heaps and per-table record blobs, + // together with the schema, are returned as TargetEcmaMetadata and reserialized into an ECMA-335 image. } } } @@ -229,68 +120,6 @@ MetadataReader? GetMetadata(ModuleHandle handle) ### Helper Methods ``` csharp -using System; -using System.Numerics; - -struct EcmaMetadataSchema -{ - public EcmaMetadataSchema(string metadataVersion, bool largeStringHeap, bool largeBlobHeap, bool largeGuidHeap, int[] rowCount, bool[] isSorted, bool variableSizedColumnsAre4BytesLong) - { - MetadataVersion = metadataVersion; - LargeStringHeap = largeStringHeap; - LargeBlobHeap = largeBlobHeap; - LargeGuidHeap = largeGuidHeap; - - _rowCount = rowCount; - _isSorted = isSorted; - - VariableSizedColumnsAreAll4BytesLong = variableSizedColumnsAre4BytesLong; - } - - public readonly string MetadataVersion; - - public readonly bool LargeStringHeap; - public readonly bool LargeBlobHeap; - public readonly bool LargeGuidHeap; - - // Table data, these structures hold MetadataTable.Count entries - private readonly int[] _rowCount; - public readonly ReadOnlySpan RowCount => _rowCount; - - private readonly bool[] _isSorted; - public readonly ReadOnlySpan IsSorted => _isSorted; - - // In certain scenarios the size of the tables is forced to be the maximum size - // Otherwise the size of columns should be computed based on RowSize/the various heap flags - public readonly bool VariableSizedColumnsAreAll4BytesLong; -} - -class TargetEcmaMetadata -{ - public TargetEcmaMetadata(EcmaMetadataSchema schema, - TargetSpan[] tables, - TargetSpan stringHeap, - TargetSpan userStringHeap, - TargetSpan blobHeap, - TargetSpan guidHeap) - { - Schema = schema; - _tables = tables; - StringHeap = stringHeap; - UserStringHeap = userStringHeap; - BlobHeap = blobHeap; - GuidHeap = guidHeap; - } - - public EcmaMetadataSchema Schema { get; init; } - - private TargetSpan[] _tables; - public ReadOnlySpan Tables => _tables; - public TargetSpan StringHeap { get; init; } - public TargetSpan UserStringHeap { get; init; } - public TargetSpan BlobHeap { get; init; } - public TargetSpan GuidHeap { get; init; } -} [Flags] enum AvailableMetadataType @@ -310,31 +139,38 @@ AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) TargetPointer dynamicMetadata = Target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */); if (dynamicMetadata != TargetPointer.Null) + { flags |= AvailableMetadataType.ReadWriteSavedCopy; + } + else if (UseReadWriteMetadata(module)) + { + flags |= AvailableMetadataType.ReadWrite; + } else + { flags |= AvailableMetadataType.ReadOnly; + } return flags; } +bool UseReadWriteMetadata(ModuleHandle handle) +{ + TargetPointer PEAssembly = Target.ReadPointer(handle.Address + /* Module::PEAssembly offset */); + if (PEAssembly == TargetPointer.Null) + return false; + + bool isEnCCapable = Target.Read(handle.Address + /* Module::Flags offset */) != 0; + bool hasRWMetadata = Target.Read(PEAssembly + /* PEAssembly::MDImportIsRW offset */) != 0; + bool hasMDImport = Target.ReadPointer(PEAssembly + /* PEAssembly::MDImport offset */) != TargetPointer.Null; + return hasRWMetadata && hasMDImport && isEnCCapable; +} + TargetSpan GetReadWriteSavedMetadataAddress(ModuleHandle handle) { - Data.Module module = new Data.Module(Target, handle.Address); TargetPointer dynamicMetadata = Target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */); - ulong size = Target.Read(handle.Address + /* DynamicMetadata::Size offset */); TargetPointer result = handle.Address + /* DynamicMetadata::Data offset */; return new(result, size); } - -TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) -{ - // [cdac] TODO. -} - -T AlignUp(T input, T alignment) - where T : IBinaryInteger -{ - return input + (alignment - T.One) & ~(alignment - T.One); -} ``` diff --git a/src/coreclr/md/enc/liteweightstgdbrw.cpp b/src/coreclr/md/enc/liteweightstgdbrw.cpp index df43ef1bcfdcb3..5a867df0e1f98c 100644 --- a/src/coreclr/md/enc/liteweightstgdbrw.cpp +++ b/src/coreclr/md/enc/liteweightstgdbrw.cpp @@ -186,6 +186,7 @@ CLiteWeightStgdbRW::InitFileForRead( if (SUCCEEDED(pStorage->OpenStream(MINIMAL_MD_STREAM, &cbData, &pvData))) { m_MiniMd.m_fMinimalDelta = TRUE; + m_MiniMd.m_fAll4ByteColumns = TRUE; } // Load the string pool. diff --git a/src/coreclr/md/enc/metamodelrw.cpp b/src/coreclr/md/enc/metamodelrw.cpp index 6d9f52fd4b7b67..7bb292e6f515ad 100644 --- a/src/coreclr/md/enc/metamodelrw.cpp +++ b/src/coreclr/md/enc/metamodelrw.cpp @@ -711,6 +711,7 @@ CMiniMdRW::CMiniMdRW() m_pHostFilter(0), m_pTokenRemapManager(0), m_fMinimalDelta(FALSE), + m_fAll4ByteColumns(FALSE), m_rENCRecs(0) { #ifdef _DEBUG @@ -1276,6 +1277,7 @@ CMiniMdRW::ComputeGrowLimits( m_limIx = USHRT_MAX << 1; m_limRid = USHRT_MAX << 1; m_eGrow = eg_grown; + m_fAll4ByteColumns = TRUE; } } // CMiniMdRW::ComputeGrowLimits @@ -3556,6 +3558,7 @@ CMiniMdRW::ExpandTables() // Remember that we've grown. m_eGrow = eg_grown; + m_fAll4ByteColumns = TRUE; m_maxRid = m_maxIx = UINT32_MAX; ErrExit: diff --git a/src/coreclr/md/inc/liteweightstgdb.h b/src/coreclr/md/inc/liteweightstgdb.h index 9b9536e5cb04b5..440fabbc28261c 100644 --- a/src/coreclr/md/inc/liteweightstgdb.h +++ b/src/coreclr/md/inc/liteweightstgdb.h @@ -15,6 +15,7 @@ #include "metadata.h" #include "metamodelro.h" #include "metamodelrw.h" +#include "cdacdata.h" #include "stgtiggerstorage.h" @@ -79,6 +80,7 @@ void CLiteWeightStgdb::Uninit() class CLiteWeightStgdbRW : public CLiteWeightStgdb { + friend struct ::cdac_data; friend class RegMeta; friend class VerifyLayoutsMD; friend HRESULT TranslateSigHelper( @@ -235,4 +237,11 @@ class CLiteWeightStgdbRW : public CLiteWeightStgdb #endif }; // class CLiteWeightStgdbRW +template<> +struct cdac_data +{ + static constexpr size_t MiniMd = offsetof(CLiteWeightStgdbRW, m_MiniMd); + static constexpr size_t MetadataAddress = offsetof(CLiteWeightStgdbRW, m_pvMd); +}; + #endif // __LiteWeightStgdb_h__ diff --git a/src/coreclr/md/inc/metamodel.h b/src/coreclr/md/inc/metamodel.h index 22ace7113f2aea..322c295ca291be 100644 --- a/src/coreclr/md/inc/metamodel.h +++ b/src/coreclr/md/inc/metamodel.h @@ -16,6 +16,7 @@ #include #include #include +#include "cdacdata.h" #include "../datablob.h" #include "../debug_metadata.h" @@ -403,6 +404,7 @@ class CMiniMdBase : public IMetaModelCommonRO { friend class VerifyLayoutsMD; // verifies class layout doesn't accidentally change + friend struct ::cdac_data; public: CMiniMdBase(); @@ -587,6 +589,13 @@ class CMiniMdBase : public IMetaModelCommonRO BOOL UsesAllocatedMemory(CMiniColDef* pCols); }; +template<> +struct cdac_data +{ + static constexpr size_t Schema = offsetof(CMiniMdBase, m_Schema); + static constexpr size_t TableCount = offsetof(CMiniMdBase, m_TblCount); +}; + #ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN #define MINIMD_POSSIBLE_INTERNAL_POINTER_EXPOSED() MarkUnsafeToDelete() diff --git a/src/coreclr/md/inc/metamodelrw.h b/src/coreclr/md/inc/metamodelrw.h index 175beaaeb20ff0..47661f5ebe2986 100644 --- a/src/coreclr/md/inc/metamodelrw.h +++ b/src/coreclr/md/inc/metamodelrw.h @@ -20,6 +20,7 @@ #include "metadatahash.h" #include "rwutil.h" #include "shash.h" +#include "cdacdata.h" #include "../heaps/export.h" #include "../tables/export.h" @@ -220,6 +221,7 @@ class CMiniMdRW : public CMiniMdTemplate friend class FilterTable; friend class ImportHelper; friend class VerifyLayoutsMD; + friend struct ::cdac_data; CMiniMdRW(); ~CMiniMdRW(); @@ -1328,6 +1330,7 @@ class CMiniMdRW : public CMiniMdTemplate private: BOOL m_fMinimalDelta; + BOOL m_fAll4ByteColumns; public: BOOL IsMinimalDelta() @@ -1396,4 +1399,15 @@ class CMiniMdRW : public CMiniMdTemplate }; // class CMiniMdRW : public CMiniMdTemplate +template<> +struct cdac_data +{ + static constexpr size_t All4ByteColumns = offsetof(CMiniMdRW, m_fAll4ByteColumns); + static constexpr size_t Tables = offsetof(CMiniMdRW, m_Tables); + static constexpr size_t StringHeap = offsetof(CMiniMdRW, m_StringHeap); + static constexpr size_t BlobHeap = offsetof(CMiniMdRW, m_BlobHeap); + static constexpr size_t UserStringHeap = offsetof(CMiniMdRW, m_UserStringHeap); + static constexpr size_t GuidHeap = offsetof(CMiniMdRW, m_GuidHeap); +}; + #endif // _METAMODELRW_H_ diff --git a/src/coreclr/md/inc/stgpool.h b/src/coreclr/md/inc/stgpool.h index 702f7542672827..0593326b2a4414 100644 --- a/src/coreclr/md/inc/stgpool.h +++ b/src/coreclr/md/inc/stgpool.h @@ -27,6 +27,7 @@ #include "sarray.h" #include "memoryrange.h" #include "../datablob.h" +#include "cdacdata.h" //***************************************************************************** // NOTE: @@ -49,8 +50,6 @@ class StgStringPool; class StgBlobPool; class StgCodePool; -template struct cdac_data; - // Perform binary search on index table. // class RIDBinarySearch : public CBinarySearch @@ -84,6 +83,7 @@ class RIDBinarySearch : public CBinarySearch class StgPoolSeg { friend class VerifyLayoutsMD; + friend struct ::cdac_data; public: StgPoolSeg() : m_pSegData((BYTE*)m_zeros), @@ -422,6 +422,7 @@ friend class StgBlobPool; friend class RecordPool; friend class CBlobPoolHash; friend class VerifyLayoutsMD; +friend struct ::cdac_data; public: StgPool(ULONG ulGrowInc=512, UINT32 nAlignment=4) : @@ -1210,6 +1211,22 @@ class StgBlobPool : public StgPool CBlobPoolHash m_Hash; // Hash table for lookups. }; // class StgBlobPool +template<> +struct cdac_data +{ + static constexpr size_t SegData = offsetof(StgPoolSeg, m_pSegData); + static constexpr size_t NextSegment = offsetof(StgPoolSeg, m_pNextSeg); + static constexpr size_t DataSize = offsetof(StgPoolSeg, m_cbSegNext); +}; + +template<> +struct cdac_data +{ + static constexpr size_t SegData = offsetof(StgPool, m_pSegData); + static constexpr size_t NextSegment = offsetof(StgPool, m_pNextSeg); + static constexpr size_t DataSize = offsetof(StgPool, m_cbSegNext); +}; + #ifdef _MSC_VER #pragma warning (default : 4355) #endif diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index ab5a28c591051e..fb4fcac094a2e6 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -373,6 +373,7 @@ Module::Module(Assembly *pAssembly, PEAssembly *pPEAssembly) m_loaderAllocator = NULL; m_pDynamicMetadata = (TADDR)NULL; + m_dwMetadataGeneration = 0; m_pPEAssembly->AddRef(); } @@ -4038,6 +4039,7 @@ void ReflectionModule::CaptureModuleMetaDataToMemory() delete (uint32_t*)m_pDynamicMetadata; m_pDynamicMetadata = (TADDR)pBuffer.Extract(); + m_dwMetadataGeneration++; } // diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 8a8349272025bc..218bc6e4ea5e62 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -1682,6 +1682,10 @@ class Module : public ModuleBase protected: TADDR m_pDynamicMetadata; + // Incremented each time a dynamic module re-serializes its metadata. + // Indicates update to out-of-process readers. + uint32_t m_dwMetadataGeneration; + public: #if !defined(DACCESS_COMPILE) PTR_Assembly GetNativeMetadataAssemblyRefFromCache(DWORD rid) @@ -1712,6 +1716,7 @@ struct cdac_data static constexpr size_t Flags = offsetof(Module, m_dwTransientFlags); static constexpr size_t LoaderAllocator = offsetof(Module, m_loaderAllocator); static constexpr size_t DynamicMetadata = offsetof(Module, m_pDynamicMetadata); + static constexpr size_t MetadataGeneration = offsetof(Module, m_dwMetadataGeneration); static constexpr size_t SimpleName = offsetof(Module, m_pSimpleName); static constexpr size_t Path = offsetof(Module, m_path); static constexpr size_t FileName = offsetof(Module, m_fileName); diff --git a/src/coreclr/vm/datadescriptor/CMakeLists.txt b/src/coreclr/vm/datadescriptor/CMakeLists.txt index dabe48a83d6fb7..8ae3ad36320cf6 100644 --- a/src/coreclr/vm/datadescriptor/CMakeLists.txt +++ b/src/coreclr/vm/datadescriptor/CMakeLists.txt @@ -11,6 +11,8 @@ add_library(runtime_descriptor_interface INTERFACE) target_include_directories(runtime_descriptor_interface INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + +target_compile_definitions(runtime_descriptor_interface INTERFACE FEATURE_METADATA_INTERNAL_APIS) add_dependencies(runtime_descriptor_interface cee_wks_core) generate_data_descriptors( LIBRARY_NAME cdac_contract_descriptor diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.h b/src/coreclr/vm/datadescriptor/datadescriptor.h index 229284445cef9f..297a073fe58bd2 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.h +++ b/src/coreclr/vm/datadescriptor/datadescriptor.h @@ -35,3 +35,7 @@ #ifdef HAVE_GCCOVER #include "gccover.h" #endif // HAVE_GCCOVER + +#include "stgpool.h" +#include "liteweightstgdb.h" +#include "mdinternalrw.h" diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index d8ad288c1e1af2..b281d0e7068be1 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -298,6 +298,7 @@ CDAC_TYPE_FIELD(Module, T_POINTER, Base, cdac_data::Base) CDAC_TYPE_FIELD(Module, T_UINT32, Flags, cdac_data::Flags) CDAC_TYPE_FIELD(Module, T_POINTER, LoaderAllocator, cdac_data::LoaderAllocator) CDAC_TYPE_FIELD(Module, T_POINTER, DynamicMetadata, cdac_data::DynamicMetadata) +CDAC_TYPE_FIELD(Module, T_UINT32, MetadataGeneration, cdac_data::MetadataGeneration) CDAC_TYPE_FIELD(Module, T_POINTER, SimpleName, cdac_data::SimpleName) CDAC_TYPE_FIELD(Module, T_POINTER, Path, cdac_data::Path) CDAC_TYPE_FIELD(Module, T_POINTER, FileName, cdac_data::FileName) @@ -364,6 +365,11 @@ CDAC_TYPE_BEGIN(EnCSyncBlockInfo) CDAC_TYPE_INDETERMINATE(EnCSyncBlockInfo) CDAC_TYPE_FIELD(EnCSyncBlockInfo, T_POINTER, List, cdac_data::List) CDAC_TYPE_END(EnCSyncBlockInfo) + +CDAC_TYPE_BEGIN(EditAndContinueModule) +CDAC_TYPE_INDETERMINATE(EditAndContinueModule) +CDAC_TYPE_FIELD(EditAndContinueModule, T_INT32, ApplyChangesCount, cdac_data::ApplyChangesCount) +CDAC_TYPE_END(EditAndContinueModule) #endif // FEATURE_METADATA_UPDATER CDAC_TYPE_BEGIN(ModuleLookupMap) @@ -431,6 +437,8 @@ CDAC_TYPE_BEGIN(PEAssembly) CDAC_TYPE_INDETERMINATE(PEAssembly) CDAC_TYPE_FIELD(PEAssembly, T_POINTER, PEImage, cdac_data::PEImage) CDAC_TYPE_FIELD(PEAssembly, T_POINTER, AssemblyBinder, cdac_data::AssemblyBinder) +CDAC_TYPE_FIELD(PEAssembly, T_UINT32, MDImportIsRW, cdac_data::MDImportIsRW) +CDAC_TYPE_FIELD(PEAssembly, T_POINTER, MDImport, cdac_data::MDImport) CDAC_TYPE_END(PEAssembly) CDAC_TYPE_BEGIN(AssemblyBinder) @@ -606,6 +614,54 @@ CDAC_TYPE_FIELD(DynamicMetadata, T_UINT32, Size, cdac_data::Siz CDAC_TYPE_FIELD(DynamicMetadata, T_ARRAY(T_UINT8), Data, cdac_data::Data) CDAC_TYPE_END(DynamicMetadata) +CDAC_TYPE_BEGIN(MDInternalRW) +CDAC_TYPE_INDETERMINATE(MDInternalRW) +CDAC_TYPE_FIELD(MDInternalRW, T_POINTER, Stgdb, offsetof(MDInternalRW, m_pStgdb)) +CDAC_TYPE_END(MDInternalRW) + +CDAC_TYPE_BEGIN(CLiteWeightStgdbRW) +CDAC_TYPE_INDETERMINATE(CLiteWeightStgdbRW) +CDAC_TYPE_FIELD(CLiteWeightStgdbRW, TYPE(CMiniMdRW), MiniMd, cdac_data::MiniMd) +CDAC_TYPE_FIELD(CLiteWeightStgdbRW, T_POINTER, MetadataAddress, cdac_data::MetadataAddress) +CDAC_TYPE_END(CLiteWeightStgdbRW) + +CDAC_TYPE_BEGIN(CMiniMdRW) +CDAC_TYPE_INDETERMINATE(CMiniMdRW) +CDAC_TYPE_FIELD(CMiniMdRW, TYPE(CMiniMdSchema), Schema, cdac_data::Schema) +CDAC_TYPE_FIELD(CMiniMdRW, T_UINT32, TableCount, cdac_data::TableCount) +CDAC_TYPE_FIELD(CMiniMdRW, T_UINT32, All4ByteColumns, cdac_data::All4ByteColumns) +CDAC_TYPE_FIELD(CMiniMdRW, TYPE(TableRW), Tables, cdac_data::Tables) +CDAC_TYPE_FIELD(CMiniMdRW, TYPE(StgPool), StringHeap, cdac_data::StringHeap) +CDAC_TYPE_FIELD(CMiniMdRW, TYPE(StgPool), BlobHeap, cdac_data::BlobHeap) +CDAC_TYPE_FIELD(CMiniMdRW, TYPE(StgPool), UserStringHeap, cdac_data::UserStringHeap) +CDAC_TYPE_FIELD(CMiniMdRW, TYPE(StgPool), GuidHeap, cdac_data::GuidHeap) +CDAC_TYPE_END(CMiniMdRW) + +CDAC_TYPE_BEGIN(CMiniMdSchema) +CDAC_TYPE_INDETERMINATE(CMiniMdSchema) +CDAC_TYPE_FIELD(CMiniMdSchema, T_UINT8, Heaps, offsetof(CMiniMdSchema, m_heaps)) +CDAC_TYPE_FIELD(CMiniMdSchema, T_UINT64, Sorted, offsetof(CMiniMdSchema, m_sorted)) +CDAC_TYPE_FIELD(CMiniMdSchema, T_ARRAY(T_UINT32), RecordCounts, offsetof(CMiniMdSchema, m_cRecs)) +CDAC_TYPE_END(CMiniMdSchema) + +CDAC_TYPE_BEGIN(TableRW) +CDAC_TYPE_SIZE(sizeof(MetaData::TableRW)) +CDAC_TYPE_END(TableRW) + +CDAC_TYPE_BEGIN(StgPoolSeg) +CDAC_TYPE_INDETERMINATE(StgPoolSeg) +CDAC_TYPE_FIELD(StgPoolSeg, T_POINTER, SegData, cdac_data::SegData) +CDAC_TYPE_FIELD(StgPoolSeg, T_POINTER, NextSegment, cdac_data::NextSegment) +CDAC_TYPE_FIELD(StgPoolSeg, T_UINT32, DataSize, cdac_data::DataSize) +CDAC_TYPE_END(StgPoolSeg) + +CDAC_TYPE_BEGIN(StgPool) +CDAC_TYPE_INDETERMINATE(StgPool) +CDAC_TYPE_FIELD(StgPool, T_POINTER, SegData, cdac_data::SegData) +CDAC_TYPE_FIELD(StgPool, T_POINTER, NextSegment, cdac_data::NextSegment) +CDAC_TYPE_FIELD(StgPool, T_UINT32, DataSize, cdac_data::DataSize) +CDAC_TYPE_END(StgPool) + #ifdef STRESS_LOG CDAC_TYPE_BEGIN(StressLog) CDAC_TYPE_SIZE(sizeof(StressLog)) diff --git a/src/coreclr/vm/encee.h b/src/coreclr/vm/encee.h index 09f0e8a47051a8..0eb7ba081190cd 100644 --- a/src/coreclr/vm/encee.h +++ b/src/coreclr/vm/encee.h @@ -208,6 +208,8 @@ class EditAndContinueModule : public Module { VPTR_VTABLE_CLASS(EditAndContinueModule, Module) + friend struct ::cdac_data; + // keep track of the number of changes - this is used to apply a version number // to an updated function. The version number for a function is the overall edit count, // ie the number of times ApplyChanges has been called, not the number of times that @@ -297,6 +299,12 @@ class EditAndContinueModule : public Module } }; +template<> +struct cdac_data +{ + static constexpr size_t ApplyChangesCount = offsetof(EditAndContinueModule, m_applyChangesCount); +}; + // Information about an instance field value added by EnC // When an instance field is added to an object, we will lazily create an EnCAddedField // for EACH instance of that object, but there will be a single EnCFieldDesc. diff --git a/src/coreclr/vm/peassembly.h b/src/coreclr/vm/peassembly.h index fe1aa193ab1970..31de87df31a0a4 100644 --- a/src/coreclr/vm/peassembly.h +++ b/src/coreclr/vm/peassembly.h @@ -434,6 +434,12 @@ struct cdac_data { static constexpr size_t PEImage = offsetof(PEAssembly, m_PEImage); static constexpr size_t AssemblyBinder = offsetof(PEAssembly, m_pAssemblyBinder); + static constexpr size_t MDImportIsRW = offsetof(PEAssembly, m_MDImportIsRW_Debugger_Use_Only); +#ifdef DACCESS_COMPILE + static constexpr size_t MDImport = offsetof(PEAssembly, m_pMDImport_UseAccessor); +#else + static constexpr size_t MDImport = offsetof(PEAssembly, m_pMDImport); +#endif }; typedef ReleaseHolder PEAssemblyHolder; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs index 00bcd71d07fe72..0733ea2a78f693 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs @@ -13,13 +13,21 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; internal sealed class EcmaMetadata_1(Target target) : IEcmaMetadata { - private readonly Dictionary _metadata = []; + // Heap index size flags (ECMA-335 II.24.2.6) + private const byte HEAP_STRING_4 = 0x01; + private const byte HEAP_GUID_4 = 0x02; + private const byte HEAP_BLOB_4 = 0x04; + private const uint ModuleFlagsEncCapable = 0x200; + private readonly Dictionary _metadata = []; private readonly Dictionary _readOnlyMetadataAddress = []; public void Flush(FlushScope scope) { - _metadata.Clear(); - _readOnlyMetadataAddress.Clear(); + if (scope == FlushScope.All) + { + _metadata.Clear(); + _readOnlyMetadataAddress.Clear(); + } } public TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle) @@ -49,16 +57,17 @@ public TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle) public MetadataReader? GetMetadata(ModuleHandle handle) { - if (_metadata.TryGetValue(handle, out MetadataReaderProvider? result)) - { - return result?.GetMetadataReader(); - } - else + uint generation = GetMetadataGeneration(handle); + + if (_metadata.TryGetValue(handle, out (uint Generation, MetadataReaderProvider? Provider) cached) + && cached.Generation == generation) { - MetadataReaderProvider? provider = GetMetadataProvider(handle); - _metadata.Add(handle, provider); - return provider?.GetMetadataReader(); + return cached.Provider?.GetMetadataReader(); } + + MetadataReaderProvider? provider = GetMetadataProvider(handle); + _metadata[handle] = (generation, provider); + return provider?.GetMetadataReader(); } private MetadataReaderProvider? GetMetadataProvider(ModuleHandle handle) @@ -102,7 +111,7 @@ public TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle) builder.WriteUInt32(0); string version = targetEcmaMetadata.Schema.MetadataVersion; - builder.WriteInt32(AlignUp(version.Length, 4)); + builder.WriteInt32(AlignUp(version.Length + 1, 4)); Write4ByteAlignedString(builder, version); // reserved @@ -125,44 +134,44 @@ public TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle) WriteStreamHeader(builder, "#JTD", 0).WriteInt32(builder.Count); } - BlobWriter stringsOffset = WriteStreamHeader(builder, "#Strings", (int)AlignUp(targetEcmaMetadata.StringHeap.Size, 4ul)); - BlobWriter blobOffset = WriteStreamHeader(builder, "#Blob", (int)targetEcmaMetadata.BlobHeap.Size); - BlobWriter guidOffset = WriteStreamHeader(builder, "#GUID", (int)targetEcmaMetadata.GuidHeap.Size); - BlobWriter userStringOffset = WriteStreamHeader(builder, "#US", (int)targetEcmaMetadata.UserStringHeap.Size); + BlobWriter stringsOffset = WriteStreamHeader(builder, "#Strings", (int)AlignUp((ulong)targetEcmaMetadata.StringHeap.Length, 4ul)); + BlobWriter blobOffset = WriteStreamHeader(builder, "#Blob", (int)AlignUp((ulong)targetEcmaMetadata.BlobHeap.Length, 4ul)); + BlobWriter guidOffset = WriteStreamHeader(builder, "#GUID", (int)AlignUp((ulong)targetEcmaMetadata.GuidHeap.Length, 4ul)); + BlobWriter userStringOffset = WriteStreamHeader(builder, "#US", (int)AlignUp((ulong)targetEcmaMetadata.UserStringHeap.Length, 4ul)); // We'll use the "uncompressed" tables stream name as the runtime may have created the *Ptr tables // that are only present in the uncompressed tables stream. - BlobWriter tablesOffset = WriteStreamHeader(builder, "#-", 0); + BlobWriter tablesOffset = new(builder.ReserveBytes(4)); + BlobWriter tablesSize = new(builder.ReserveBytes(4)); + Write4ByteAlignedString(builder, "#-"); // Write the heap-style Streams stringsOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.StringHeap); - for (ulong i = targetEcmaMetadata.StringHeap.Size; i < AlignUp(targetEcmaMetadata.StringHeap.Size, 4ul); i++) - { - builder.WriteByte(0); - } + WriteAlignedHeap(builder, targetEcmaMetadata.StringHeap); blobOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.BlobHeap); + WriteAlignedHeap(builder, targetEcmaMetadata.BlobHeap); guidOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.GuidHeap); + WriteAlignedHeap(builder, targetEcmaMetadata.GuidHeap); userStringOffset.WriteInt32(builder.Count); - WriteTargetSpan(builder, targetEcmaMetadata.UserStringHeap); + WriteAlignedHeap(builder, targetEcmaMetadata.UserStringHeap); // Write tables stream - tablesOffset.WriteInt32(builder.Count); + int tableStreamStart = builder.Count; + tablesOffset.WriteInt32(tableStreamStart); // Write tables stream header builder.WriteInt32(0); // reserved + // ECMA-335 II.24.2.6: MajorVersion shall be 2, MinorVersion shall be 0. builder.WriteByte(2); // major version builder.WriteByte(0); // minor version uint heapSizes = - (targetEcmaMetadata.Schema.LargeStringHeap ? 1u << 0 : 0) | - (targetEcmaMetadata.Schema.LargeBlobHeap ? 1u << 1 : 0) | - (targetEcmaMetadata.Schema.LargeGuidHeap ? 1u << 2 : 0); + (targetEcmaMetadata.Schema.LargeStringHeap ? (uint)HEAP_STRING_4 : 0) | + (targetEcmaMetadata.Schema.LargeGuidHeap ? (uint)HEAP_GUID_4 : 0) | + (targetEcmaMetadata.Schema.LargeBlobHeap ? (uint)HEAP_BLOB_4 : 0); builder.WriteByte((byte)heapSizes); builder.WriteByte(1); // reserved @@ -197,21 +206,19 @@ public TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle) } // Write the tables - foreach (TargetSpan span in targetEcmaMetadata.Tables) + foreach (byte[] table in targetEcmaMetadata.Tables) { - WriteTargetSpan(builder, span); + builder.WriteBytes(table); } + // Patch the #- stream size now that the full table stream has been written. + tablesSize.WriteInt32(builder.Count - tableStreamStart); + MemoryStream metadataStream = new MemoryStream(); builder.WriteContentTo(metadataStream); + metadataStream.Position = 0; return MetadataReaderProvider.FromMetadataStream(metadataStream); - void WriteTargetSpan(BlobBuilder builder, TargetSpan span) - { - Blob blob = builder.ReserveBytes(checked((int)span.Size)); - target.ReadBuffer(span.Address, blob.GetBytes().AsSpan()); - } - static BlobWriter WriteStreamHeader(BlobBuilder builder, string name, int size) { BlobWriter offset = new(builder.ReserveBytes(4)); @@ -220,13 +227,24 @@ static BlobWriter WriteStreamHeader(BlobBuilder builder, string name, int size) return offset; } + static void WriteAlignedHeap(BlobBuilder builder, byte[] heap) + { + builder.WriteBytes(heap); + for (int i = heap.Length; i < (int)AlignUp((ulong)heap.Length, 4ul); i++) + { + builder.WriteByte(0); + } + } + static void Write4ByteAlignedString(BlobBuilder builder, string value) { int bufferStart = builder.Count; builder.WriteUTF8(value); builder.WriteByte(0); int stringEnd = builder.Count; - for (int i = stringEnd; i < bufferStart + AlignUp(value.Length, 4); i++) + // The name field occupies the null-terminated string padded to a 4-byte boundary, + // i.e. AlignUp(length + 1, 4) bytes (the +1 accounts for the null terminator). + for (int i = stringEnd; i < bufferStart + AlignUp(value.Length + 1, 4); i++) { builder.WriteByte(0); } @@ -273,11 +291,11 @@ public EcmaMetadataSchema(string metadataVersion, bool largeStringHeap, bool lar private sealed class TargetEcmaMetadata { public TargetEcmaMetadata(EcmaMetadataSchema schema, - TargetSpan[] tables, - TargetSpan stringHeap, - TargetSpan userStringHeap, - TargetSpan blobHeap, - TargetSpan guidHeap) + byte[][] tables, + byte[] stringHeap, + byte[] userStringHeap, + byte[] blobHeap, + byte[] guidHeap) { Schema = schema; _tables = tables; @@ -289,12 +307,12 @@ public TargetEcmaMetadata(EcmaMetadataSchema schema, public EcmaMetadataSchema Schema { get; init; } - private TargetSpan[] _tables; - public ReadOnlySpan Tables => _tables; - public TargetSpan StringHeap { get; init; } - public TargetSpan UserStringHeap { get; init; } - public TargetSpan BlobHeap { get; init; } - public TargetSpan GuidHeap { get; init; } + private byte[][] _tables; + public ReadOnlySpan Tables => _tables; + public byte[] StringHeap { get; init; } + public byte[] UserStringHeap { get; init; } + public byte[] BlobHeap { get; init; } + public byte[] GuidHeap { get; init; } } [Flags] @@ -313,14 +331,48 @@ private AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) AvailableMetadataType flags = AvailableMetadataType.None; if (module.DynamicMetadata != TargetPointer.Null) + { flags |= AvailableMetadataType.ReadWriteSavedCopy; + } + else if (UseReadWriteMetadata(module)) + { + flags |= AvailableMetadataType.ReadWrite; + } else + { flags |= AvailableMetadataType.ReadOnly; + } - // TODO(cdac) implement direct reading of unsaved ReadWrite metadata return flags; } + private bool UseReadWriteMetadata(Data.Module module) + { + if (module.PEAssembly == TargetPointer.Null) + return false; + + Data.PEAssembly peAssembly = target.ProcessedData.GetOrAdd(module.PEAssembly); + return peAssembly.MDImportIsRW != 0 && peAssembly.MDImport != TargetPointer.Null && (module.Flags & ModuleFlagsEncCapable) != 0; + } + + private uint GetMetadataGeneration(ModuleHandle handle) + { + Data.Module module = new(target, handle.Address); // do not cache + + if (module.DynamicMetadata != TargetPointer.Null) + { + return module.MetadataGeneration; + } + + if (UseReadWriteMetadata(module)) + { + Data.EditAndContinueModule encModule = new(target, handle.Address); // do not cache + return (uint)encModule.ApplyChangesCount; + } + + return 0; + } + private TargetSpan GetReadWriteSavedMetadataAddress(ModuleHandle handle) { Data.Module module = target.ProcessedData.GetOrAdd(handle.Address); @@ -331,7 +383,97 @@ private TargetSpan GetReadWriteSavedMetadataAddress(ModuleHandle handle) private TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) { - throw new NotImplementedException(); + Data.Module module = target.ProcessedData.GetOrAdd(handle.Address); + Data.PEAssembly peAssembly = target.ProcessedData.GetOrAdd(module.PEAssembly); + Data.MDInternalRW mdRW = target.ProcessedData.GetOrAdd(peAssembly.MDImport); + Data.CLiteWeightStgdbRW stgdb = target.ProcessedData.GetOrAdd(mdRW.Stgdb); + Data.CMiniMdRW miniMd = target.ProcessedData.GetOrAdd(stgdb.MiniMd); + Data.CMiniMdSchema schema = target.ProcessedData.GetOrAdd(miniMd.Schema); + + int tableCount = checked((int)miniMd.TableCount); + + // ECMA-335 II.24.2.6 + int[] rowCounts = new int[tableCount]; + for (int i = 0; i < tableCount; i++) + { + rowCounts[i] = checked((int)target.Read(schema.RecordCounts + (ulong)(i * sizeof(uint)))); + } + + // ECMA-335 II.24.2.6 + bool[] isSorted = new bool[tableCount]; + for (int i = 0; i < tableCount; i++) + { + isSorted[i] = (schema.Sorted & (1UL << i)) != 0; + } + + bool largeStringHeap = (schema.Heaps & HEAP_STRING_4) != 0; + bool largeGuidHeap = (schema.Heaps & HEAP_GUID_4) != 0; + bool largeBlobHeap = (schema.Heaps & HEAP_BLOB_4) != 0; + + bool variableColumnsAll4Bytes = + miniMd.All4ByteColumns != 0; + + byte[] stringHeap = ReadStoragePool(miniMd.StringHeap); + byte[] blobHeap = ReadStoragePool(miniMd.BlobHeap); + byte[] userStringHeap = ReadStoragePool(miniMd.UserStringHeap); + byte[] guidHeap = ReadStoragePool(miniMd.GuidHeap); + + // Coalesce the record data for each table. + byte[][] tables = new byte[tableCount][]; + for (int i = 0; i < tableCount; i++) + { + tables[i] = ReadStoragePool(miniMd.TableSegments[i]); + } + + string version = EcmaMetadataUtils.ReadMetadataVersion(target, stgdb.MetadataAddress); + + EcmaMetadataSchema ecmaSchema = new EcmaMetadataSchema( + version, + largeStringHeap, + largeBlobHeap, + largeGuidHeap, + rowCounts, + isSorted, + variableColumnsAll4Bytes); + return new TargetEcmaMetadata(ecmaSchema, tables, stringHeap, userStringHeap, blobHeap, guidHeap); + } + + private byte[] ReadStoragePool(TargetPointer poolAddress) + { + List<(TargetPointer Data, uint Size)> segments = []; + long totalSize = 0; + + Data.StgPool head = target.ProcessedData.GetOrAdd(poolAddress); + TargetPointer segData = head.SegData; + uint dataSize = head.DataSize; + TargetPointer nextSegment = head.NextSegment; + + while (true) + { + if (dataSize > 0) + { + segments.Add((segData, dataSize)); + totalSize += dataSize; + } + if (nextSegment == TargetPointer.Null) + { + break; + } + + Data.StgPoolSeg segment = target.ProcessedData.GetOrAdd(nextSegment); + segData = segment.SegData; + dataSize = segment.DataSize; + nextSegment = segment.NextSegment; + } + + byte[] result = new byte[checked((int)totalSize)]; + int offset = 0; + foreach ((TargetPointer data, uint size) in segments) + { + target.ReadBuffer(data, result.AsSpan(offset, checked((int)size))); + offset += (int)size; + } + return result; } private static T AlignUp(T input, T alignment) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CLiteWeightStgdbRW.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CLiteWeightStgdbRW.cs new file mode 100644 index 00000000000000..59ddaaf1c87b13 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CLiteWeightStgdbRW.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +[CdacType(nameof(DataType.CLiteWeightStgdbRW))] +internal sealed partial class CLiteWeightStgdbRW : IData +{ + [FieldAddress] public TargetPointer MiniMd { get; } + [Field] public TargetPointer MetadataAddress { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs new file mode 100644 index 00000000000000..c37e68bf7d2090 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs @@ -0,0 +1,35 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +[CdacType(nameof(DataType.CMiniMdRW))] +internal sealed partial class CMiniMdRW : IData +{ + [FieldAddress] public TargetPointer Schema { get; } + [Field] public uint TableCount { get; } + [Field] public uint All4ByteColumns { get; } + [FieldAddress] public TargetPointer Tables { get; } + [FieldAddress] public TargetPointer StringHeap { get; } + [FieldAddress] public TargetPointer BlobHeap { get; } + [FieldAddress] public TargetPointer UserStringHeap { get; } + [FieldAddress] public TargetPointer GuidHeap { get; } + public TargetPointer[] TableSegments { get; private set; } + + [MemberNotNull(nameof(TableSegments))] + partial void OnInit(Target target, TargetPointer address) + { + int tableCount = checked((int)TableCount); + uint tableStride = target.GetTypeInfo(DataType.TableRW).Size + ?? throw new InvalidOperationException("TableRW size is required to index the tables array."); + + TableSegments = new TargetPointer[tableCount]; + for (int i = 0; i < tableCount; i++) + { + TableSegments[i] = Tables + (ulong)i * tableStride; + } + } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdSchema.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdSchema.cs new file mode 100644 index 00000000000000..f1b6838a6c5236 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdSchema.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +[CdacType(nameof(DataType.CMiniMdSchema))] +internal sealed partial class CMiniMdSchema : IData +{ + [Field] public byte Heaps { get; } + [Field] public ulong Sorted { get; } + [FieldAddress] public TargetPointer RecordCounts { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs new file mode 100644 index 00000000000000..5966d0410ac343 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +[CdacType(nameof(DataType.EditAndContinueModule))] +internal sealed partial class EditAndContinueModule : IData +{ + [Field] public int ApplyChangesCount { get; private set; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MDInternalRW.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MDInternalRW.cs new file mode 100644 index 00000000000000..e5228692e8bc9d --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MDInternalRW.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +[CdacType(nameof(DataType.MDInternalRW))] +internal sealed partial class MDInternalRW : IData +{ + [Field] public TargetPointer Stgdb { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs index 58d29e1d81ea2e..2f0a5b69928097 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs @@ -15,6 +15,7 @@ internal sealed partial class Module : IData [Field] public TargetPointer Base { get; } [Field] public TargetPointer LoaderAllocator { get; } [Field] public TargetPointer DynamicMetadata { get; } + [Field] public uint MetadataGeneration { get; } [Field] public TargetPointer SimpleName { get; } [Field] public TargetPointer Path { get; } [Field] public TargetPointer FileName { get; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs index 6697676a619f86..19c248521bd585 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs @@ -8,4 +8,6 @@ internal sealed partial class PEAssembly : IData { [Field] public TargetPointer PEImage { get; } [Field] public TargetPointer AssemblyBinder { get; } + [Field] public uint MDImportIsRW { get; } + [Field] public TargetPointer MDImport { get; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPool.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPool.cs new file mode 100644 index 00000000000000..08f8df30dfc9f4 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPool.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +[CdacType(nameof(DataType.StgPool))] +internal sealed partial class StgPool : IData +{ + [Field] public TargetPointer SegData { get; } + [Field] public TargetPointer NextSegment { get; } + [Field] public uint DataSize { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPoolSeg.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPoolSeg.cs new file mode 100644 index 00000000000000..abfcd573540f24 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/StgPoolSeg.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +[CdacType(nameof(DataType.StgPoolSeg))] +internal sealed partial class StgPoolSeg : IData +{ + [Field] public TargetPointer SegData { get; } + [Field] public TargetPointer NextSegment { get; } + [Field] public uint DataSize { get; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs index b1f47c20892850..0100214671bb32 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs @@ -71,6 +71,13 @@ public enum DataType FnPtrTypeDesc, FieldDesc, DynamicMetadata, + MDInternalRW, + CLiteWeightStgdbRW, + CMiniMdRW, + CMiniMdSchema, + TableRW, + StgPool, + StgPoolSeg, StressLog, StressLogModuleDesc, StressLogHeader, @@ -211,6 +218,7 @@ public enum DataType EnCAddedField, EnCAddedStaticField, EnCSyncBlockInfo, + EditAndContinueModule, UnorderedArrayBase, } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs index cc0ee0647f916f..a7ba099eab7c86 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs @@ -38,4 +38,27 @@ public static uint CreateFieldDef(uint tokenParts) Debug.Assert((tokenParts & 0xff000000) == 0, $"Token type should not be set in {nameof(tokenParts)}"); return (uint)TokenType.mdtFieldDef | tokenParts; } + + // ECMA-335 II.24.2.1 metadata root: + // Signature(4) | MajorVersion(2) | MinorVersion(2) | Reserved(4) | VersionLength(4) | Version[VersionLength] + private const ulong MetadataRootVersionLengthOffset = 12; + private const ulong MetadataRootVersionStringOffset = 16; + + // Reads the metadata version string from the metadata root (ECMA-335 II.24.2.1) at the given + // address. Returns an empty string when the address is null or no version string is present. + public static string ReadMetadataVersion(Target target, TargetPointer metadataRootAddress) + { + if (metadataRootAddress == TargetPointer.Null) + { + return string.Empty; + } + + uint versionLength = target.Read(metadataRootAddress + MetadataRootVersionLengthOffset); + if (versionLength == 0) + { + return string.Empty; + } + + return target.ReadUtf8String(metadataRootAddress + MetadataRootVersionStringOffset); + } } From 7bdd91fdf9d47bbe1d4b820b9d46145bc72f7c7c Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Sat, 27 Jun 2026 18:15:23 -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> --- .../Data/EditAndContinueModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs index 5966d0410ac343..d517eba839bd5a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs @@ -6,5 +6,5 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; [CdacType(nameof(DataType.EditAndContinueModule))] internal sealed partial class EditAndContinueModule : IData { - [Field] public int ApplyChangesCount { get; private set; } + [Field] public int ApplyChangesCount { get; } } From 58459646c79be25dd5e1d78c7bf0bd1c526e90c2 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Sat, 27 Jun 2026 18:52:19 -0700 Subject: [PATCH 03/10] ccr --- docs/design/datacontracts/EcmaMetadata.md | 2 +- src/coreclr/md/inc/VerifyLayouts.inc | 1 + src/coreclr/vm/datadescriptor/CMakeLists.txt | 2 +- .../Contracts/EcmaMetadata_1.cs | 11 ++++++++--- .../EcmaMetadataUtils.cs | 14 +++++++++++++- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/docs/design/datacontracts/EcmaMetadata.md b/docs/design/datacontracts/EcmaMetadata.md index fff25e4b3eb378..ab147f76e68036 100644 --- a/docs/design/datacontracts/EcmaMetadata.md +++ b/docs/design/datacontracts/EcmaMetadata.md @@ -160,7 +160,7 @@ bool UseReadWriteMetadata(ModuleHandle handle) if (PEAssembly == TargetPointer.Null) return false; - bool isEnCCapable = Target.Read(handle.Address + /* Module::Flags offset */) != 0; + bool isEnCCapable = (Target.Read(handle.Address + /* Module::Flags offset */) & ModuleFlagsEncCapable) != 0; bool hasRWMetadata = Target.Read(PEAssembly + /* PEAssembly::MDImportIsRW offset */) != 0; bool hasMDImport = Target.ReadPointer(PEAssembly + /* PEAssembly::MDImport offset */) != TargetPointer.Null; return hasRWMetadata && hasMDImport && isEnCCapable; diff --git a/src/coreclr/md/inc/VerifyLayouts.inc b/src/coreclr/md/inc/VerifyLayouts.inc index 6521e4b474fe4d..97f841d10c8bf3 100644 --- a/src/coreclr/md/inc/VerifyLayouts.inc +++ b/src/coreclr/md/inc/VerifyLayouts.inc @@ -200,6 +200,7 @@ ALIGN_FIELD(CMiniMdRW, m_bSortable, sizeof(BYTE)*TBL_COUNT, sizeof(BYTE)) FIELD(CMiniMdRW, dbg_m_pLock, sizeof(void*)) #endif FIELD(CMiniMdRW, m_fMinimalDelta, 4) +FIELD(CMiniMdRW, m_fAll4ByteColumns, 4) FIELD(CMiniMdRW, m_rENCRecs, sizeof(void*)) END_TYPE(CMiniMdRW, 8) diff --git a/src/coreclr/vm/datadescriptor/CMakeLists.txt b/src/coreclr/vm/datadescriptor/CMakeLists.txt index 8ae3ad36320cf6..66562719a37b0c 100644 --- a/src/coreclr/vm/datadescriptor/CMakeLists.txt +++ b/src/coreclr/vm/datadescriptor/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories(runtime_descriptor_interface INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -target_compile_definitions(runtime_descriptor_interface INTERFACE FEATURE_METADATA_INTERNAL_APIS) +target_compile_definitions(runtime_descriptor_interface INTERFACE -DFEATURE_METADATA_INTERNAL_APIS) add_dependencies(runtime_descriptor_interface cee_wks_core) generate_data_descriptors( LIBRARY_NAME cdac_contract_descriptor diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs index 0733ea2a78f693..59888dfd0b0864 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs @@ -6,6 +6,7 @@ using System.IO; using System.Numerics; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; @@ -388,9 +389,13 @@ private TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) Data.MDInternalRW mdRW = target.ProcessedData.GetOrAdd(peAssembly.MDImport); Data.CLiteWeightStgdbRW stgdb = target.ProcessedData.GetOrAdd(mdRW.Stgdb); Data.CMiniMdRW miniMd = target.ProcessedData.GetOrAdd(stgdb.MiniMd); - Data.CMiniMdSchema schema = target.ProcessedData.GetOrAdd(miniMd.Schema); + Data.CMiniMdSchema schema = new(target, miniMd.Schema); int tableCount = checked((int)miniMd.TableCount); + if ((uint)tableCount > (uint)MetadataTokens.TableCount) + { + throw new InvalidOperationException($"Unexpected metadata table count {tableCount}."); + } // ECMA-335 II.24.2.6 int[] rowCounts = new int[tableCount]; @@ -443,7 +448,7 @@ private byte[] ReadStoragePool(TargetPointer poolAddress) List<(TargetPointer Data, uint Size)> segments = []; long totalSize = 0; - Data.StgPool head = target.ProcessedData.GetOrAdd(poolAddress); + Data.StgPool head = new(target, poolAddress); TargetPointer segData = head.SegData; uint dataSize = head.DataSize; TargetPointer nextSegment = head.NextSegment; @@ -460,7 +465,7 @@ private byte[] ReadStoragePool(TargetPointer poolAddress) break; } - Data.StgPoolSeg segment = target.ProcessedData.GetOrAdd(nextSegment); + Data.StgPoolSeg segment = new(target, nextSegment); segData = segment.SegData; dataSize = segment.DataSize; nextSegment = segment.NextSegment; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs index a7ba099eab7c86..8deb61e64a5611 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/EcmaMetadataUtils.cs @@ -1,7 +1,9 @@ // 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.Diagnostics; +using System.Text; namespace Microsoft.Diagnostics.DataContractReader; @@ -43,6 +45,7 @@ public static uint CreateFieldDef(uint tokenParts) // Signature(4) | MajorVersion(2) | MinorVersion(2) | Reserved(4) | VersionLength(4) | Version[VersionLength] private const ulong MetadataRootVersionLengthOffset = 12; private const ulong MetadataRootVersionStringOffset = 16; + private const uint MaxMetadataVersionLength = 256; // Reads the metadata version string from the metadata root (ECMA-335 II.24.2.1) at the given // address. Returns an empty string when the address is null or no version string is present. @@ -59,6 +62,15 @@ public static string ReadMetadataVersion(Target target, TargetPointer metadataRo return string.Empty; } - return target.ReadUtf8String(metadataRootAddress + MetadataRootVersionStringOffset); + int length = (int)Math.Min(versionLength, MaxMetadataVersionLength); + Span buffer = stackalloc byte[length]; + target.ReadBuffer(metadataRootAddress + MetadataRootVersionStringOffset, buffer); + int terminator = buffer.IndexOf((byte)0); + if (terminator >= 0) + { + buffer = buffer[..terminator]; + } + + return Encoding.UTF8.GetString(buffer); } } From e05c7626e3487fa0353e0cb7e880d6a8883f6d66 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Sat, 27 Jun 2026 19:22:43 -0700 Subject: [PATCH 04/10] fix build --- .../tests/UnitTests/MockDescriptors/MockDescriptors.Loader.cs | 2 ++ .../MockDescriptors/MockDescriptors.RuntimeMutableTypeSystem.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Loader.cs b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Loader.cs index 09ad3d25260a58..a1cefcd69367e3 100644 --- a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Loader.cs +++ b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Loader.cs @@ -64,6 +64,7 @@ internal sealed class MockLoaderModule : TypedView private const string FlagsFieldName = "Flags"; private const string LoaderAllocatorFieldName = "LoaderAllocator"; private const string DynamicMetadataFieldName = "DynamicMetadata"; + private const string MetadataGenerationFieldName = "MetadataGeneration"; private const string SimpleNameFieldName = "SimpleName"; private const string PathFieldName = "Path"; private const string FileNameFieldName = "FileName"; @@ -88,6 +89,7 @@ public static Layout CreateLayout(MockTarget.Architecture arch .AddUInt32Field(FlagsFieldName) .AddPointerField(LoaderAllocatorFieldName) .AddPointerField(DynamicMetadataFieldName) + .AddUInt32Field(MetadataGenerationFieldName) .AddPointerField(SimpleNameFieldName) .AddPointerField(PathFieldName) .AddPointerField(FileNameFieldName) diff --git a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.RuntimeMutableTypeSystem.cs b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.RuntimeMutableTypeSystem.cs index 992957fdc947c7..213dfcf867be6c 100644 --- a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.RuntimeMutableTypeSystem.cs +++ b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.RuntimeMutableTypeSystem.cs @@ -32,6 +32,7 @@ public static Layout CreateLayout(MockTarget.Architecture archite .AddPointerField(nameof(Data.Module.Base)) .AddPointerField(nameof(Data.Module.LoaderAllocator)) .AddPointerField(nameof(Data.Module.DynamicMetadata)) + .AddUInt32Field(nameof(Data.Module.MetadataGeneration)) .AddPointerField(nameof(Data.Module.SimpleName)) .AddPointerField(nameof(Data.Module.Path)) .AddPointerField(nameof(Data.Module.FileName)) From e58ad79638159f6a615a3c1a620031b2d3bb2211 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Sat, 27 Jun 2026 21:51:23 -0700 Subject: [PATCH 05/10] ccr --- docs/design/datacontracts/EcmaMetadata.md | 4 +--- .../Contracts/EcmaMetadata_1.cs | 14 +++++++++++--- .../managed/cdac/tests/UnitTests/LoaderTests.cs | 4 ++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/design/datacontracts/EcmaMetadata.md b/docs/design/datacontracts/EcmaMetadata.md index ab147f76e68036..4b15e00718e2db 100644 --- a/docs/design/datacontracts/EcmaMetadata.md +++ b/docs/design/datacontracts/EcmaMetadata.md @@ -132,8 +132,6 @@ enum AvailableMetadataType AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) { - Data.Module module = new Data.Module(Target, handle.Address); - AvailableMetadataType flags = AvailableMetadataType.None; TargetPointer dynamicMetadata = Target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */); @@ -142,7 +140,7 @@ AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) { flags |= AvailableMetadataType.ReadWriteSavedCopy; } - else if (UseReadWriteMetadata(module)) + else if (UseReadWriteMetadata(handle)) { flags |= AvailableMetadataType.ReadWrite; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs index 59888dfd0b0864..530adc12841fd1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs @@ -26,6 +26,11 @@ public void Flush(FlushScope scope) { if (scope == FlushScope.All) { + foreach ((uint _, MetadataReaderProvider? provider) in _metadata.Values) + { + provider?.Dispose(); + } + _metadata.Clear(); _readOnlyMetadataAddress.Clear(); } @@ -60,10 +65,13 @@ public TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle) { uint generation = GetMetadataGeneration(handle); - if (_metadata.TryGetValue(handle, out (uint Generation, MetadataReaderProvider? Provider) cached) - && cached.Generation == generation) + if (_metadata.TryGetValue(handle, out (uint Generation, MetadataReaderProvider? Provider) cached)) { - return cached.Provider?.GetMetadataReader(); + if (cached.Generation == generation) + { + return cached.Provider?.GetMetadataReader(); + } + cached.Provider?.Dispose(); } MetadataReaderProvider? provider = GetMetadataProvider(handle); diff --git a/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs b/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs index 8843a4d5161011..0645eb7f04ab89 100644 --- a/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs @@ -536,6 +536,8 @@ private static (TestPlaceholderTarget Target, TargetPointer PEAssemblyAddr, Targ var peAssemblyLayout = helpers.LayoutFields([ new(nameof(Data.PEAssembly.PEImage), DataType.pointer), new(nameof(Data.PEAssembly.AssemblyBinder), DataType.pointer), + new(nameof(Data.PEAssembly.MDImportIsRW), DataType.uint32), + new(nameof(Data.PEAssembly.MDImport), DataType.pointer), ]); var peImageLayout = helpers.LayoutFields([ new(nameof(Data.PEImage.LoadedImageLayout), DataType.pointer), @@ -754,6 +756,8 @@ public void IsModuleMapped_ReturnsExpected(MockTarget.Architecture arch, uint fo var peAssemblyLayout = helpers.LayoutFields([ new(nameof(Data.PEAssembly.PEImage), DataType.pointer), new(nameof(Data.PEAssembly.AssemblyBinder), DataType.pointer), + new(nameof(Data.PEAssembly.MDImportIsRW), DataType.uint32), + new(nameof(Data.PEAssembly.MDImport), DataType.pointer), ]); var peImageLayout = helpers.LayoutFields([ new(nameof(Data.PEImage.LoadedImageLayout), DataType.pointer), From 10be9e37c97a6df18ec1f8689360d90965a44317 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Sun, 28 Jun 2026 00:04:33 -0700 Subject: [PATCH 06/10] fix merge --- src/native/managed/cdac/tests/UnitTests/LoaderTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs b/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs index 1b50a978a0c3f0..8b85ac118dd38f 100644 --- a/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs @@ -1095,6 +1095,8 @@ private static (TestPlaceholderTarget Target, TargetPointer ModuleAddr) CreatePE var peAssemblyLayout = helpers.LayoutFields([ new(nameof(Data.PEAssembly.PEImage), DataType.pointer), new(nameof(Data.PEAssembly.AssemblyBinder), DataType.pointer), + new(nameof(Data.PEAssembly.MDImportIsRW), DataType.uint32), + new(nameof(Data.PEAssembly.MDImport), DataType.pointer), ]); var peImageLayout = helpers.LayoutFields([ new(nameof(Data.PEImage.LoadedImageLayout), DataType.pointer), From f6ed782a9566943c9db3e2bb63077372ba89eae7 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 29 Jun 2026 11:53:36 -0700 Subject: [PATCH 07/10] code review --- docs/design/datacontracts/EcmaMetadata.md | 15 +++----- src/coreclr/vm/peassembly.h | 4 +- .../Contracts/EcmaMetadata_1.cs | 37 +++++++------------ .../Data/CMiniMdRW.cs | 2 +- 4 files changed, 21 insertions(+), 37 deletions(-) diff --git a/docs/design/datacontracts/EcmaMetadata.md b/docs/design/datacontracts/EcmaMetadata.md index 4b15e00718e2db..24d22798694fc9 100644 --- a/docs/design/datacontracts/EcmaMetadata.md +++ b/docs/design/datacontracts/EcmaMetadata.md @@ -53,10 +53,10 @@ Data descriptors used: | `StgPoolSeg` | `NextSegment` | Pointer to the next pool segment, or null | | `StgPoolSeg` | `DataSize` | Live byte count of this extension segment | -### Contract Constants: -| Name | Type | Purpose | Value | -| --- | --- | --- | --- | -| `ModuleFlagsEncCapable` | uint | `Module` transient-flags bit (`IS_ENC_CAPABLE`) indicating the module is an `EditAndContinueModule` | `0x200` | +Contracts used: +| Contract Name | +| --- | +| `Loader` | ```csharp @@ -154,11 +154,8 @@ AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) bool UseReadWriteMetadata(ModuleHandle handle) { - TargetPointer PEAssembly = Target.ReadPointer(handle.Address + /* Module::PEAssembly offset */); - if (PEAssembly == TargetPointer.Null) - return false; - - bool isEnCCapable = (Target.Read(handle.Address + /* Module::Flags offset */) & ModuleFlagsEncCapable) != 0; + TargetPointer PEAssembly = Target.Contracts.Loader.GetPEAssembly(handle.Address); + bool isEnCCapable = Target.Contracts.Loader.GetFlags(handle) & ModuleFlags.EncCapable != 0 bool hasRWMetadata = Target.Read(PEAssembly + /* PEAssembly::MDImportIsRW offset */) != 0; bool hasMDImport = Target.ReadPointer(PEAssembly + /* PEAssembly::MDImport offset */) != TargetPointer.Null; return hasRWMetadata && hasMDImport && isEnCCapable; diff --git a/src/coreclr/vm/peassembly.h b/src/coreclr/vm/peassembly.h index 31de87df31a0a4..4c2a6baf11a4a7 100644 --- a/src/coreclr/vm/peassembly.h +++ b/src/coreclr/vm/peassembly.h @@ -435,9 +435,7 @@ struct cdac_data static constexpr size_t PEImage = offsetof(PEAssembly, m_PEImage); static constexpr size_t AssemblyBinder = offsetof(PEAssembly, m_pAssemblyBinder); static constexpr size_t MDImportIsRW = offsetof(PEAssembly, m_MDImportIsRW_Debugger_Use_Only); -#ifdef DACCESS_COMPILE - static constexpr size_t MDImport = offsetof(PEAssembly, m_pMDImport_UseAccessor); -#else +#ifndef DACCESS_COMPILE static constexpr size_t MDImport = offsetof(PEAssembly, m_pMDImport); #endif }; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs index 530adc12841fd1..ddab436167554f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs @@ -18,7 +18,6 @@ internal sealed class EcmaMetadata_1(Target target) : IEcmaMetadata private const byte HEAP_STRING_4 = 0x01; private const byte HEAP_GUID_4 = 0x02; private const byte HEAP_BLOB_4 = 0x04; - private const uint ModuleFlagsEncCapable = 0x200; private readonly Dictionary _metadata = []; private readonly Dictionary _readOnlyMetadataAddress = []; @@ -26,11 +25,6 @@ public void Flush(FlushScope scope) { if (scope == FlushScope.All) { - foreach ((uint _, MetadataReaderProvider? provider) in _metadata.Values) - { - provider?.Dispose(); - } - _metadata.Clear(); _readOnlyMetadataAddress.Clear(); } @@ -343,7 +337,7 @@ private AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) { flags |= AvailableMetadataType.ReadWriteSavedCopy; } - else if (UseReadWriteMetadata(module)) + else if (UseReadWriteMetadata(handle)) { flags |= AvailableMetadataType.ReadWrite; } @@ -355,27 +349,26 @@ private AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) return flags; } - private bool UseReadWriteMetadata(Data.Module module) + private bool UseReadWriteMetadata(ModuleHandle handle) { - if (module.PEAssembly == TargetPointer.Null) - return false; - - Data.PEAssembly peAssembly = target.ProcessedData.GetOrAdd(module.PEAssembly); - return peAssembly.MDImportIsRW != 0 && peAssembly.MDImport != TargetPointer.Null && (module.Flags & ModuleFlagsEncCapable) != 0; + ILoader loader = target.Contracts.Loader; + TargetPointer peAssemblyPtr = loader.GetPEAssembly(handle); + Data.PEAssembly peAssembly = target.ProcessedData.GetOrAdd(peAssemblyPtr); + return peAssembly.MDImportIsRW != 0 && peAssembly.MDImport != TargetPointer.Null && (loader.GetFlags(handle) & ModuleFlags.EncCapable) != 0; } private uint GetMetadataGeneration(ModuleHandle handle) { - Data.Module module = new(target, handle.Address); // do not cache + Data.Module module = target.ProcessedData.GetOrAdd(handle.Address); if (module.DynamicMetadata != TargetPointer.Null) { return module.MetadataGeneration; } - if (UseReadWriteMetadata(module)) + if (UseReadWriteMetadata(handle)) { - Data.EditAndContinueModule encModule = new(target, handle.Address); // do not cache + Data.EditAndContinueModule encModule = target.ProcessedData.GetOrAdd(handle.Address); return (uint)encModule.ApplyChangesCount; } @@ -397,7 +390,7 @@ private TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) Data.MDInternalRW mdRW = target.ProcessedData.GetOrAdd(peAssembly.MDImport); Data.CLiteWeightStgdbRW stgdb = target.ProcessedData.GetOrAdd(mdRW.Stgdb); Data.CMiniMdRW miniMd = target.ProcessedData.GetOrAdd(stgdb.MiniMd); - Data.CMiniMdSchema schema = new(target, miniMd.Schema); + Data.CMiniMdSchema schema = target.ProcessedData.GetOrAdd(miniMd.Schema); int tableCount = checked((int)miniMd.TableCount); if ((uint)tableCount > (uint)MetadataTokens.TableCount) @@ -422,10 +415,6 @@ private TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) bool largeStringHeap = (schema.Heaps & HEAP_STRING_4) != 0; bool largeGuidHeap = (schema.Heaps & HEAP_GUID_4) != 0; bool largeBlobHeap = (schema.Heaps & HEAP_BLOB_4) != 0; - - bool variableColumnsAll4Bytes = - miniMd.All4ByteColumns != 0; - byte[] stringHeap = ReadStoragePool(miniMd.StringHeap); byte[] blobHeap = ReadStoragePool(miniMd.BlobHeap); byte[] userStringHeap = ReadStoragePool(miniMd.UserStringHeap); @@ -447,7 +436,7 @@ private TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) largeGuidHeap, rowCounts, isSorted, - variableColumnsAll4Bytes); + miniMd.All4ByteColumns); return new TargetEcmaMetadata(ecmaSchema, tables, stringHeap, userStringHeap, blobHeap, guidHeap); } @@ -456,7 +445,7 @@ private byte[] ReadStoragePool(TargetPointer poolAddress) List<(TargetPointer Data, uint Size)> segments = []; long totalSize = 0; - Data.StgPool head = new(target, poolAddress); + Data.StgPool head = target.ProcessedData.GetOrAdd(poolAddress); TargetPointer segData = head.SegData; uint dataSize = head.DataSize; TargetPointer nextSegment = head.NextSegment; @@ -473,7 +462,7 @@ private byte[] ReadStoragePool(TargetPointer poolAddress) break; } - Data.StgPoolSeg segment = new(target, nextSegment); + Data.StgPoolSeg segment = target.ProcessedData.GetOrAdd(nextSegment); segData = segment.SegData; dataSize = segment.DataSize; nextSegment = segment.NextSegment; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs index c37e68bf7d2090..dd11f64c260475 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/CMiniMdRW.cs @@ -11,7 +11,7 @@ internal sealed partial class CMiniMdRW : IData { [FieldAddress] public TargetPointer Schema { get; } [Field] public uint TableCount { get; } - [Field] public uint All4ByteColumns { get; } + [Field(UnderlyingBoolType = typeof(uint))] public bool All4ByteColumns { get; } [FieldAddress] public TargetPointer Tables { get; } [FieldAddress] public TargetPointer StringHeap { get; } [FieldAddress] public TargetPointer BlobHeap { get; } From 803dd712a3929e07d69aaec010f7f7519aacefd4 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 29 Jun 2026 13:48:23 -0700 Subject: [PATCH 08/10] profiler edits --- docs/design/datacontracts/EcmaMetadata.md | 15 ++-------- src/coreclr/vm/ceeload.cpp | 1 + .../vm/datadescriptor/datadescriptor.inc | 5 ---- src/coreclr/vm/encee.h | 7 ----- .../Contracts/EcmaMetadata_1.cs | 28 +++---------------- .../Data/EditAndContinueModule.cs | 10 ------- .../DataType.cs | 1 - 7 files changed, 7 insertions(+), 60 deletions(-) delete mode 100644 src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs diff --git a/docs/design/datacontracts/EcmaMetadata.md b/docs/design/datacontracts/EcmaMetadata.md index 24d22798694fc9..b3b93840f6d9ce 100644 --- a/docs/design/datacontracts/EcmaMetadata.md +++ b/docs/design/datacontracts/EcmaMetadata.md @@ -25,9 +25,6 @@ Data descriptors used: | `Module` | `DynamicMetadata` | Pointer to saved metadata for reflection emit modules | | `Module` | `MetadataGeneration` | Counter incremented each time a dynamic module re-serializes its saved metadata copy | | `Module` | `FieldDefToDescMap` | Mapping table | -| `Module` | `PEAssembly` | Pointer to the module's `PEAssembly` | -| `Module` | `Flags` | Module transient flags | -| `EditAndContinueModule` | `ApplyChangesCount` | Counter incremented each time an EnC edit is applied | | `DynamicMetadata` | `Size` | Size of the dynamic metadata blob (as a 32bit uint) | | `DynamicMetadata` | `Data` | Start of dynamic metadata data array | | `PEAssembly` | `MDImportIsRW` | Non-zero when `MDImport` is a read-write importer | @@ -135,12 +132,13 @@ AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) AvailableMetadataType flags = AvailableMetadataType.None; TargetPointer dynamicMetadata = Target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */); + uint metadataGeneration = Target.ReadPointer(handle.Address + /* Module::MetadataGeneration offset */); if (dynamicMetadata != TargetPointer.Null) { flags |= AvailableMetadataType.ReadWriteSavedCopy; } - else if (UseReadWriteMetadata(handle)) + else if (metadataGeneration != 0) { flags |= AvailableMetadataType.ReadWrite; } @@ -152,15 +150,6 @@ AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) return flags; } -bool UseReadWriteMetadata(ModuleHandle handle) -{ - TargetPointer PEAssembly = Target.Contracts.Loader.GetPEAssembly(handle.Address); - bool isEnCCapable = Target.Contracts.Loader.GetFlags(handle) & ModuleFlags.EncCapable != 0 - bool hasRWMetadata = Target.Read(PEAssembly + /* PEAssembly::MDImportIsRW offset */) != 0; - bool hasMDImport = Target.ReadPointer(PEAssembly + /* PEAssembly::MDImport offset */) != TargetPointer.Null; - return hasRWMetadata && hasMDImport && isEnCCapable; -} - TargetSpan GetReadWriteSavedMetadataAddress(ModuleHandle handle) { TargetPointer dynamicMetadata = Target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */); diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index e36a13b4fc17bb..650e989b23e4f8 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -666,6 +666,7 @@ void Module::ApplyMetaData() // Ensure for MethodDef ulCount = GetMDImport()->GetCountWithTokenKind(mdtMethodDef) + 1; EnsureMethodDefCanBeStored(TokenFromRid(ulCount, mdtMethodDef)); + m_dwMetadataGeneration++; } // diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 58c56afbf98fba..c5432e449a0613 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -373,11 +373,6 @@ CDAC_TYPE_BEGIN(EnCSyncBlockInfo) CDAC_TYPE_INDETERMINATE(EnCSyncBlockInfo) CDAC_TYPE_FIELD(EnCSyncBlockInfo, T_POINTER, List, cdac_data::List) CDAC_TYPE_END(EnCSyncBlockInfo) - -CDAC_TYPE_BEGIN(EditAndContinueModule) -CDAC_TYPE_INDETERMINATE(EditAndContinueModule) -CDAC_TYPE_FIELD(EditAndContinueModule, T_INT32, ApplyChangesCount, cdac_data::ApplyChangesCount) -CDAC_TYPE_END(EditAndContinueModule) #endif // FEATURE_METADATA_UPDATER CDAC_TYPE_BEGIN(ModuleLookupMap) diff --git a/src/coreclr/vm/encee.h b/src/coreclr/vm/encee.h index 0eb7ba081190cd..17a8939ce8970b 100644 --- a/src/coreclr/vm/encee.h +++ b/src/coreclr/vm/encee.h @@ -208,8 +208,6 @@ class EditAndContinueModule : public Module { VPTR_VTABLE_CLASS(EditAndContinueModule, Module) - friend struct ::cdac_data; - // keep track of the number of changes - this is used to apply a version number // to an updated function. The version number for a function is the overall edit count, // ie the number of times ApplyChanges has been called, not the number of times that @@ -299,11 +297,6 @@ class EditAndContinueModule : public Module } }; -template<> -struct cdac_data -{ - static constexpr size_t ApplyChangesCount = offsetof(EditAndContinueModule, m_applyChangesCount); -}; // Information about an instance field value added by EnC // When an instance field is added to an object, we will lazily create an EnCAddedField diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs index ddab436167554f..060bcdb15c03d8 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/EcmaMetadata_1.cs @@ -337,7 +337,7 @@ private AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) { flags |= AvailableMetadataType.ReadWriteSavedCopy; } - else if (UseReadWriteMetadata(handle)) + else if (module.MetadataGeneration != 0) { flags |= AvailableMetadataType.ReadWrite; } @@ -349,30 +349,10 @@ private AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) return flags; } - private bool UseReadWriteMetadata(ModuleHandle handle) - { - ILoader loader = target.Contracts.Loader; - TargetPointer peAssemblyPtr = loader.GetPEAssembly(handle); - Data.PEAssembly peAssembly = target.ProcessedData.GetOrAdd(peAssemblyPtr); - return peAssembly.MDImportIsRW != 0 && peAssembly.MDImport != TargetPointer.Null && (loader.GetFlags(handle) & ModuleFlags.EncCapable) != 0; - } - private uint GetMetadataGeneration(ModuleHandle handle) { Data.Module module = target.ProcessedData.GetOrAdd(handle.Address); - - if (module.DynamicMetadata != TargetPointer.Null) - { - return module.MetadataGeneration; - } - - if (UseReadWriteMetadata(handle)) - { - Data.EditAndContinueModule encModule = target.ProcessedData.GetOrAdd(handle.Address); - return (uint)encModule.ApplyChangesCount; - } - - return 0; + return module.MetadataGeneration; } private TargetSpan GetReadWriteSavedMetadataAddress(ModuleHandle handle) @@ -385,8 +365,8 @@ private TargetSpan GetReadWriteSavedMetadataAddress(ModuleHandle handle) private TargetEcmaMetadata GetReadWriteMetadata(ModuleHandle handle) { - Data.Module module = target.ProcessedData.GetOrAdd(handle.Address); - Data.PEAssembly peAssembly = target.ProcessedData.GetOrAdd(module.PEAssembly); + TargetPointer peAssemblyPtr = target.Contracts.Loader.GetPEAssembly(handle); + Data.PEAssembly peAssembly = target.ProcessedData.GetOrAdd(peAssemblyPtr); Data.MDInternalRW mdRW = target.ProcessedData.GetOrAdd(peAssembly.MDImport); Data.CLiteWeightStgdbRW stgdb = target.ProcessedData.GetOrAdd(mdRW.Stgdb); Data.CMiniMdRW miniMd = target.ProcessedData.GetOrAdd(stgdb.MiniMd); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs deleted file mode 100644 index d517eba839bd5a..00000000000000 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EditAndContinueModule.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.Diagnostics.DataContractReader.Data; - -[CdacType(nameof(DataType.EditAndContinueModule))] -internal sealed partial class EditAndContinueModule : IData -{ - [Field] public int ApplyChangesCount { get; } -} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs index e3352990a431d3..895ba670c69370 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/DataType.cs @@ -219,7 +219,6 @@ public enum DataType EnCAddedField, EnCAddedStaticField, EnCSyncBlockInfo, - EditAndContinueModule, UnorderedArrayBase, } From f2d9cb56d411360ad0ef8aafcb30116b2a420b5f Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Mon, 29 Jun 2026 14:32:33 -0700 Subject: [PATCH 09/10] Update encee.h --- src/coreclr/vm/encee.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/vm/encee.h b/src/coreclr/vm/encee.h index 17a8939ce8970b..09f0e8a47051a8 100644 --- a/src/coreclr/vm/encee.h +++ b/src/coreclr/vm/encee.h @@ -297,7 +297,6 @@ class EditAndContinueModule : public Module } }; - // Information about an instance field value added by EnC // When an instance field is added to an object, we will lazily create an EnCAddedField // for EACH instance of that object, but there will be a single EnCFieldDesc. From cdf940df705a3b70e6e71f1c68e1393cbdf51bd5 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Mon, 29 Jun 2026 21:44:06 -0700 Subject: [PATCH 10/10] ccr --- docs/design/datacontracts/EcmaMetadata.md | 5 ++--- src/coreclr/vm/datadescriptor/datadescriptor.inc | 1 - src/coreclr/vm/peassembly.h | 1 - .../Data/PEAssembly.cs | 1 - src/native/managed/cdac/tests/UnitTests/LoaderTests.cs | 3 --- 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/docs/design/datacontracts/EcmaMetadata.md b/docs/design/datacontracts/EcmaMetadata.md index b3b93840f6d9ce..50054a06fa2924 100644 --- a/docs/design/datacontracts/EcmaMetadata.md +++ b/docs/design/datacontracts/EcmaMetadata.md @@ -27,8 +27,7 @@ Data descriptors used: | `Module` | `FieldDefToDescMap` | Mapping table | | `DynamicMetadata` | `Size` | Size of the dynamic metadata blob (as a 32bit uint) | | `DynamicMetadata` | `Data` | Start of dynamic metadata data array | -| `PEAssembly` | `MDImportIsRW` | Non-zero when `MDImport` is a read-write importer | -| `PEAssembly` | `MDImport` | An `MDInternalRW` when `MDImportIsRW` is set | +| `PEAssembly` | `MDImport` | An `MDInternalRW` when `MetadataGeneration` is non-zero and module is not dynamic | | `MDInternalRW` | `Stgdb` | Pointer to the read-write storage database | | `CLiteWeightStgdbRW` | `MiniMd` | Address of the embedded `CMiniMdRW` model | | `CLiteWeightStgdbRW` | `MetadataAddress` | Pointer to the metadata image | @@ -132,7 +131,7 @@ AvailableMetadataType GetAvailableMetadataType(ModuleHandle handle) AvailableMetadataType flags = AvailableMetadataType.None; TargetPointer dynamicMetadata = Target.ReadPointer(handle.Address + /* Module::DynamicMetadata offset */); - uint metadataGeneration = Target.ReadPointer(handle.Address + /* Module::MetadataGeneration offset */); + uint metadataGeneration = Target.Read(handle.Address + /* Module::MetadataGeneration offset */); if (dynamicMetadata != TargetPointer.Null) { diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index c5432e449a0613..0f068d5490150b 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -440,7 +440,6 @@ CDAC_TYPE_BEGIN(PEAssembly) CDAC_TYPE_INDETERMINATE(PEAssembly) CDAC_TYPE_FIELD(PEAssembly, T_POINTER, PEImage, cdac_data::PEImage) CDAC_TYPE_FIELD(PEAssembly, T_POINTER, AssemblyBinder, cdac_data::AssemblyBinder) -CDAC_TYPE_FIELD(PEAssembly, T_UINT32, MDImportIsRW, cdac_data::MDImportIsRW) CDAC_TYPE_FIELD(PEAssembly, T_POINTER, MDImport, cdac_data::MDImport) CDAC_TYPE_END(PEAssembly) diff --git a/src/coreclr/vm/peassembly.h b/src/coreclr/vm/peassembly.h index 4c2a6baf11a4a7..f8d4244e75778f 100644 --- a/src/coreclr/vm/peassembly.h +++ b/src/coreclr/vm/peassembly.h @@ -434,7 +434,6 @@ struct cdac_data { static constexpr size_t PEImage = offsetof(PEAssembly, m_PEImage); static constexpr size_t AssemblyBinder = offsetof(PEAssembly, m_pAssemblyBinder); - static constexpr size_t MDImportIsRW = offsetof(PEAssembly, m_MDImportIsRW_Debugger_Use_Only); #ifndef DACCESS_COMPILE static constexpr size_t MDImport = offsetof(PEAssembly, m_pMDImport); #endif diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs index 19c248521bd585..57361746ca6f04 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PEAssembly.cs @@ -8,6 +8,5 @@ internal sealed partial class PEAssembly : IData { [Field] public TargetPointer PEImage { get; } [Field] public TargetPointer AssemblyBinder { get; } - [Field] public uint MDImportIsRW { get; } [Field] public TargetPointer MDImport { get; } } diff --git a/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs b/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs index 8b85ac118dd38f..e7b14cb1df7370 100644 --- a/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/LoaderTests.cs @@ -521,7 +521,6 @@ private static (TestPlaceholderTarget Target, TargetPointer PEAssemblyAddr, Targ var peAssemblyLayout = helpers.LayoutFields([ new(nameof(Data.PEAssembly.PEImage), DataType.pointer), new(nameof(Data.PEAssembly.AssemblyBinder), DataType.pointer), - new(nameof(Data.PEAssembly.MDImportIsRW), DataType.uint32), new(nameof(Data.PEAssembly.MDImport), DataType.pointer), ]); var peImageLayout = helpers.LayoutFields([ @@ -741,7 +740,6 @@ public void IsModuleMapped_ReturnsExpected(MockTarget.Architecture arch, uint fo var peAssemblyLayout = helpers.LayoutFields([ new(nameof(Data.PEAssembly.PEImage), DataType.pointer), new(nameof(Data.PEAssembly.AssemblyBinder), DataType.pointer), - new(nameof(Data.PEAssembly.MDImportIsRW), DataType.uint32), new(nameof(Data.PEAssembly.MDImport), DataType.pointer), ]); var peImageLayout = helpers.LayoutFields([ @@ -1095,7 +1093,6 @@ private static (TestPlaceholderTarget Target, TargetPointer ModuleAddr) CreatePE var peAssemblyLayout = helpers.LayoutFields([ new(nameof(Data.PEAssembly.PEImage), DataType.pointer), new(nameof(Data.PEAssembly.AssemblyBinder), DataType.pointer), - new(nameof(Data.PEAssembly.MDImportIsRW), DataType.uint32), new(nameof(Data.PEAssembly.MDImport), DataType.pointer), ]); var peImageLayout = helpers.LayoutFields([