diff --git a/src/tools/crossgen2/Common/TypeSystem/Common/ExplicitLayoutValidator.cs b/src/tools/crossgen2/Common/TypeSystem/Common/ExplicitLayoutValidator.cs new file mode 100644 index 000000000000..b1c4ae54a09a --- /dev/null +++ b/src/tools/crossgen2/Common/TypeSystem/Common/ExplicitLayoutValidator.cs @@ -0,0 +1,155 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace Internal.TypeSystem +{ + public struct ExplicitLayoutValidator + { + private enum FieldLayoutTag : byte + { + Empty, + NonORef, + ORef, + } + + private readonly int _pointerSize; + + private readonly FieldLayoutTag[] _fieldLayout; + + private readonly MetadataType _typeBeingValidated; + + private ExplicitLayoutValidator(MetadataType type, int typeSizeInBytes) + { + _typeBeingValidated = type; + _pointerSize = type.Context.Target.PointerSize; + _fieldLayout = new FieldLayoutTag[typeSizeInBytes]; + } + + public static void Validate(MetadataType type, ComputedInstanceFieldLayout layout) + { + ExplicitLayoutValidator validator = new ExplicitLayoutValidator(type, layout.ByteCountUnaligned.AsInt); + foreach (FieldAndOffset fieldAndOffset in layout.Offsets) + { + validator.AddToFieldLayout(fieldAndOffset.Offset.AsInt, fieldAndOffset.Field.FieldType); + } + } + + private void AddToFieldLayout(int offset, TypeDesc fieldType) + { + if (fieldType.IsGCPointer) + { + if (offset % _pointerSize != 0) + { + // Misaligned ORef + ThrowFieldLayoutError(offset); + } + SetFieldLayout(offset, _pointerSize, FieldLayoutTag.ORef); + } + else if (fieldType.IsPointer || fieldType.IsFunctionPointer) + { + SetFieldLayout(offset, _pointerSize, FieldLayoutTag.NonORef); + } + else if (fieldType.IsValueType) + { + if (fieldType.IsByRefLike && offset % _pointerSize != 0) + { + // Misaligned ByRefLike + ThrowFieldLayoutError(offset); + } + + MetadataType mdType = (MetadataType)fieldType; + int fieldSize = mdType.InstanceByteCountUnaligned.AsInt; + if (!mdType.ContainsGCPointers) + { + // Plain value type, mark the entire range as NonORef + SetFieldLayout(offset, fieldSize, FieldLayoutTag.NonORef); + } + else + { + if (offset % _pointerSize != 0) + { + // Misaligned struct with GC pointers + ThrowFieldLayoutError(offset); + } + + bool[] fieldORefMap = new bool[fieldSize]; + MarkORefLocations(mdType, fieldORefMap, offset: 0); + for (int index = 0; index < fieldSize; index++) + { + SetFieldLayout(offset + index, fieldORefMap[index] ? FieldLayoutTag.ORef : FieldLayoutTag.NonORef); + } + } + } + else if (fieldType.IsByRef) + { + if (offset % _pointerSize != 0) + { + // Misaligned pointer field + ThrowFieldLayoutError(offset); + } + SetFieldLayout(offset, _pointerSize, FieldLayoutTag.NonORef); + } + else + { + Debug.Assert(false, fieldType.ToString()); + } + } + + private void MarkORefLocations(MetadataType type, bool[] orefMap, int offset) + { + // Recurse into struct fields + foreach (FieldDesc field in type.GetFields()) + { + if (!field.IsStatic) + { + int fieldOffset = offset + field.Offset.AsInt; + if (field.FieldType.IsGCPointer) + { + for (int index = 0; index < _pointerSize; index++) + { + orefMap[fieldOffset + index] = true; + } + } + else if (field.FieldType.IsValueType) + { + MetadataType mdFieldType = (MetadataType)field.FieldType; + if (mdFieldType.ContainsGCPointers) + { + MarkORefLocations(mdFieldType, orefMap, fieldOffset); + } + } + } + } + } + + private void SetFieldLayout(int offset, int count, FieldLayoutTag tag) + { + for (int index = 0; index < count; index++) + { + SetFieldLayout(offset + index, tag); + } + } + + private void SetFieldLayout(int offset, FieldLayoutTag tag) + { + FieldLayoutTag existingTag = _fieldLayout[offset]; + if (existingTag != tag) + { + if (existingTag != FieldLayoutTag.Empty) + { + ThrowFieldLayoutError(offset); + } + _fieldLayout[offset] = tag; + } + } + + private void ThrowFieldLayoutError(int offset) + { + ThrowHelper.ThrowTypeLoadException(ExceptionStringID.ClassLoadExplicitLayout, _typeBeingValidated, offset.ToStringInvariant()); + } + } +} diff --git a/src/tools/crossgen2/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/tools/crossgen2/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 79cb2078af80..b998f6dce936 100644 --- a/src/tools/crossgen2/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/tools/crossgen2/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -367,6 +367,8 @@ protected static ComputedInstanceFieldLayout ComputeExplicitFieldLayout(Metadata computedLayout.ByteCountAlignment = instanceByteSizeAndAlignment.Alignment; computedLayout.Offsets = offsets; + ExplicitLayoutValidator.Validate(type, computedLayout); + return computedLayout; } diff --git a/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj b/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj index 2948bc0a2972..0fe25e26ec52 100644 --- a/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj +++ b/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj @@ -114,6 +114,9 @@ TypeSystem\Common\CastingHelper.cs + + TypeSystem\Common\ExplicitLayoutValidator.cs + TypeSystem\Common\FunctionPointerType.cs