未验证 提交 053a472b 编写于 作者: A Aaron Robinson 提交者: GitHub

Update tests to help uncover CrossGen2 scenario validation (#65477)

* Update CrossGen2 to support ByRef fields in all scenarios

* Update tests to help uncover CrossGen2 scenario validation

* Add support in Mono for the ByReference`1 being considered a ref field
  during Explicit offset validation.
上级 ea5376ad
...@@ -14,6 +14,7 @@ private enum FieldLayoutTag : byte ...@@ -14,6 +14,7 @@ private enum FieldLayoutTag : byte
Empty, Empty,
NonORef, NonORef,
ORef, ORef,
ByRef,
} }
private struct FieldLayoutInterval : IComparable<FieldLayoutInterval> private struct FieldLayoutInterval : IComparable<FieldLayoutInterval>
...@@ -92,15 +93,9 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType) ...@@ -92,15 +93,9 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
} }
else if (fieldType.IsValueType) else if (fieldType.IsValueType)
{ {
if (fieldType.IsByRefLike && offset % _pointerSize != 0)
{
// Misaligned ByRefLike
ThrowFieldLayoutError(offset);
}
MetadataType mdType = (MetadataType)fieldType; MetadataType mdType = (MetadataType)fieldType;
int fieldSize = mdType.InstanceByteCountUnaligned.AsInt; int fieldSize = mdType.InstanceByteCountUnaligned.AsInt;
if (!mdType.ContainsGCPointers) if (!mdType.ContainsGCPointers && !mdType.IsByRefLike)
{ {
// Plain value type, mark the entire range as NonORef // Plain value type, mark the entire range as NonORef
SetFieldLayout(offset, fieldSize, FieldLayoutTag.NonORef); SetFieldLayout(offset, fieldSize, FieldLayoutTag.NonORef);
...@@ -109,27 +104,27 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType) ...@@ -109,27 +104,27 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
{ {
if (offset % _pointerSize != 0) if (offset % _pointerSize != 0)
{ {
// Misaligned struct with GC pointers // Misaligned struct with GC pointers or ByRef
ThrowFieldLayoutError(offset); ThrowFieldLayoutError(offset);
} }
List<FieldLayoutInterval> fieldORefMap = new List<FieldLayoutInterval>(); List<FieldLayoutInterval> fieldRefMap = new();
MarkORefLocations(mdType, fieldORefMap, offset: 0); MarkByRefAndORefLocations(mdType, fieldRefMap, offset: 0);
// Merge in fieldORefMap from structure specifying not attributed intervals as NonORef // Merge in fieldRefMap from structure specifying not attributed intervals as NonORef
int lastGCRegionReportedEnd = 0; int lastGCRegionReportedEnd = 0;
foreach (var gcRegion in fieldORefMap) foreach (var gcRegion in fieldRefMap)
{ {
SetFieldLayout(offset + lastGCRegionReportedEnd, gcRegion.Start - lastGCRegionReportedEnd, FieldLayoutTag.NonORef); SetFieldLayout(offset + lastGCRegionReportedEnd, gcRegion.Start - lastGCRegionReportedEnd, FieldLayoutTag.NonORef);
Debug.Assert(gcRegion.Tag == FieldLayoutTag.ORef); Debug.Assert(gcRegion.Tag == FieldLayoutTag.ORef || gcRegion.Tag == FieldLayoutTag.ByRef);
SetFieldLayout(offset + gcRegion.Start, gcRegion.Size, gcRegion.Tag); SetFieldLayout(offset + gcRegion.Start, gcRegion.Size, gcRegion.Tag);
lastGCRegionReportedEnd = gcRegion.EndSentinel; lastGCRegionReportedEnd = gcRegion.EndSentinel;
} }
if (fieldORefMap.Count > 0) if (fieldRefMap.Count > 0)
{ {
int trailingRegionStart = fieldORefMap[fieldORefMap.Count - 1].EndSentinel; int trailingRegionStart = fieldRefMap[fieldRefMap.Count - 1].EndSentinel;
int trailingRegionSize = fieldSize - trailingRegionStart; int trailingRegionSize = fieldSize - trailingRegionStart;
SetFieldLayout(offset + trailingRegionStart, trailingRegionSize, FieldLayoutTag.NonORef); SetFieldLayout(offset + trailingRegionStart, trailingRegionSize, FieldLayoutTag.NonORef);
} }
...@@ -142,7 +137,7 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType) ...@@ -142,7 +137,7 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
// Misaligned pointer field // Misaligned pointer field
ThrowFieldLayoutError(offset); ThrowFieldLayoutError(offset);
} }
SetFieldLayout(offset, _pointerSize, FieldLayoutTag.NonORef); SetFieldLayout(offset, _pointerSize, FieldLayoutTag.ByRef);
} }
else else
{ {
...@@ -150,7 +145,7 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType) ...@@ -150,7 +145,7 @@ private void AddToFieldLayout(int offset, TypeDesc fieldType)
} }
} }
private void MarkORefLocations(MetadataType type, List<FieldLayoutInterval> orefMap, int offset) private void MarkByRefAndORefLocations(MetadataType type, List<FieldLayoutInterval> refMap, int offset)
{ {
// Recurse into struct fields // Recurse into struct fields
foreach (FieldDesc field in type.GetFields()) foreach (FieldDesc field in type.GetFields())
...@@ -160,14 +155,18 @@ private void MarkORefLocations(MetadataType type, List<FieldLayoutInterval> oref ...@@ -160,14 +155,18 @@ private void MarkORefLocations(MetadataType type, List<FieldLayoutInterval> oref
int fieldOffset = offset + field.Offset.AsInt; int fieldOffset = offset + field.Offset.AsInt;
if (field.FieldType.IsGCPointer) if (field.FieldType.IsGCPointer)
{ {
SetFieldLayout(orefMap, offset, _pointerSize, FieldLayoutTag.ORef); SetFieldLayout(refMap, offset, _pointerSize, FieldLayoutTag.ORef);
}
else if (field.FieldType.IsByRef || field.FieldType.IsByReferenceOfT)
{
SetFieldLayout(refMap, offset, _pointerSize, FieldLayoutTag.ByRef);
} }
else if (field.FieldType.IsValueType) else if (field.FieldType.IsValueType)
{ {
MetadataType mdFieldType = (MetadataType)field.FieldType; MetadataType mdFieldType = (MetadataType)field.FieldType;
if (mdFieldType.ContainsGCPointers) if (mdFieldType.ContainsGCPointers || mdFieldType.IsByRefLike)
{ {
MarkORefLocations(mdFieldType, orefMap, fieldOffset); MarkByRefAndORefLocations(mdFieldType, refMap, fieldOffset);
} }
} }
} }
......
...@@ -1843,12 +1843,35 @@ class_has_ref_fields (MonoClass *klass) ...@@ -1843,12 +1843,35 @@ class_has_ref_fields (MonoClass *klass)
return klass->has_ref_fields; return klass->has_ref_fields;
} }
static gboolean
class_is_byreference (MonoClass* klass)
{
const char* klass_name_space = m_class_get_name_space (klass);
const char* klass_name = m_class_get_name (klass);
MonoImage* klass_image = m_class_get_image (klass);
gboolean in_corlib = klass_image == mono_defaults.corlib;
if (in_corlib &&
!strcmp (klass_name_space, "System") &&
!strcmp (klass_name, "ByReference`1")) {
return TRUE;
}
return FALSE;
}
static gboolean static gboolean
type_has_ref_fields (MonoType *ftype) type_has_ref_fields (MonoType *ftype)
{ {
if (m_type_is_byref (ftype) || (MONO_TYPE_ISSTRUCT (ftype) && class_has_ref_fields (mono_class_from_mono_type_internal (ftype)))) if (m_type_is_byref (ftype) || (MONO_TYPE_ISSTRUCT (ftype) && class_has_ref_fields (mono_class_from_mono_type_internal (ftype))))
return TRUE; return TRUE;
/* Check for the ByReference`1 type */
if (MONO_TYPE_ISSTRUCT (ftype)) {
MonoClass* klass = mono_class_from_mono_type_internal (ftype);
return class_is_byreference (klass);
}
return FALSE; return FALSE;
} }
...@@ -1942,7 +1965,7 @@ validate_struct_fields_overlaps (guint8 *layout_check, int layout_size, MonoClas ...@@ -1942,7 +1965,7 @@ validate_struct_fields_overlaps (guint8 *layout_check, int layout_size, MonoClas
if (mono_type_is_struct (ftype)) { if (mono_type_is_struct (ftype)) {
// recursively check the layout of the embedded struct // recursively check the layout of the embedded struct
MonoClass *embedded_class = mono_class_from_mono_type_internal (ftype); MonoClass *embedded_class = mono_class_from_mono_type_internal (ftype);
mono_class_setup_fields (embedded_class); mono_class_setup_fields (embedded_class);
const int embedded_fields_count = mono_class_get_field_count (embedded_class); const int embedded_fields_count = mono_class_get_field_count (embedded_class);
int *embedded_offsets = g_new0 (int, embedded_fields_count); int *embedded_offsets = g_new0 (int, embedded_fields_count);
...@@ -1962,7 +1985,7 @@ validate_struct_fields_overlaps (guint8 *layout_check, int layout_size, MonoClas ...@@ -1962,7 +1985,7 @@ validate_struct_fields_overlaps (guint8 *layout_check, int layout_size, MonoClas
} else { } else {
int align = 0; int align = 0;
int size = mono_type_size (field->type, &align); int size = mono_type_size (field->type, &align);
guint8 type = type_has_references (klass, ftype) ? 1 : m_type_is_byref (ftype) ? 2 : 3; guint8 type = type_has_references (klass, ftype) ? 1 : (m_type_is_byref (ftype) || class_is_byreference (klass)) ? 2 : 3;
// Mark the bytes used by this fields type based on if it contains references or not. // Mark the bytes used by this fields type based on if it contains references or not.
// Make sure there are no overlaps between object and non-object fields. // Make sure there are no overlaps between object and non-object fields.
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
01 00 00 00 01 00 00 00
) )
.field public int32& Field .field public int32& Field
.field public int32 Size
} }
.class public explicit ansi sealed beforefieldinit InvalidCSharp.IntPtrOverlapWithInnerFieldType .class public explicit ansi sealed beforefieldinit InvalidCSharp.IntPtrOverlapWithInnerFieldType
......
...@@ -4,12 +4,20 @@ ...@@ -4,12 +4,20 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using InvalidCSharp; using InvalidCSharp;
using Xunit; using Xunit;
class Validate class Validate
{ {
[StructLayout(LayoutKind.Explicit)]
private ref struct Explicit
{
[FieldOffset(0)] public Span<byte> Bytes;
[FieldOffset(0)] public Guid Guid;
}
[Fact] [Fact]
public static void Validate_Invalid_RefField_Fails() public static void Validate_Invalid_RefField_Fails()
{ {
...@@ -17,8 +25,31 @@ public static void Validate_Invalid_RefField_Fails() ...@@ -17,8 +25,31 @@ public static void Validate_Invalid_RefField_Fails()
Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidStructWithRefField); }); Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidStructWithRefField); });
Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidRefFieldAlignment); }); Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidRefFieldAlignment); });
Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidObjectRefRefFieldOverlap); }); Assert.Throws<TypeLoadException>(() => { var t = typeof(InvalidObjectRefRefFieldOverlap); });
Assert.Throws<TypeLoadException>(() => { var t = typeof(IntPtrRefFieldOverlap); }); Assert.Throws<TypeLoadException>(() =>
Assert.Throws<TypeLoadException>(() => { var t = typeof(IntPtrOverlapWithInnerFieldType); }); {
var t = new IntPtrRefFieldOverlap()
{
Field = IntPtr.Zero
};
return t.Field.ToString();
});
Assert.Throws<TypeLoadException>(() =>
{
var t = new IntPtrOverlapWithInnerFieldType()
{
Field = IntPtr.Zero
};
ref var i = ref t.Invalid;
return i.Size;
});
Assert.Throws<TypeLoadException>(() =>
{
var t = new Explicit()
{
Guid = Guid.NewGuid()
};
return t.Bytes.Length;
});
} }
[Fact] [Fact]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册