From b1b4d63145000abb5ef81ad216e58dd34bf610a6 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Wed, 8 Feb 2023 14:20:05 -0800 Subject: [PATCH] Fix nested non-byref like VC with no pointer fields (#80642) (#80873) * Fix nested non-byref like VC with no pointer fields --- src/coreclr/vm/methodtablebuilder.cpp | 25 ++--- .../objrefandnonobjrefoverlap/case13.cs | 92 +++++++++++++++++++ .../objrefandnonobjrefoverlap/case13.csproj | 10 ++ 3 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.cs create mode 100644 src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.csproj diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index b0c0c1ce749d3..245fcaf55bf2b 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -8743,29 +8743,30 @@ MethodTableBuilder::HandleExplicitLayout( if (pMT->IsByRefLike()) return CheckByRefLikeValueClassLayout(pMT, pFieldLayout); - // This method assumes there is a GC desc associated with the MethodTable. - _ASSERTE(pMT->ContainsPointers()); - // Build a layout of the value class (vc). Don't know the sizes of all the fields easily, but // do know (a) vc is already consistent so don't need to check it's overlaps and // (b) size and location of all objectrefs. So build it by setting all non-oref - // then fill in the orefs later + // then fill in the orefs later if present. UINT fieldSize = pMT->GetNumInstanceFieldBytes(); CQuickBytes qb; bmtFieldLayoutTag *vcLayout = (bmtFieldLayoutTag*) qb.AllocThrows(fieldSize * sizeof(bmtFieldLayoutTag)); memset((void*)vcLayout, nonoref, fieldSize); - // use pointer series to locate the orefs - CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); - CGCDescSeries *pSeries = map->GetLowestSeries(); - - for (SIZE_T j = 0; j < map->GetNumSeries(); j++) + // If the type contains pointers fill it out from the GC data + if (pMT->ContainsPointers()) { - CONSISTENCY_CHECK(pSeries <= map->GetHighestSeries()); + // use pointer series to locate the orefs + CGCDesc* map = CGCDesc::GetCGCDescFromMT(pMT); + CGCDescSeries *pSeries = map->GetLowestSeries(); - memset((void*)&vcLayout[pSeries->GetSeriesOffset() - OBJECT_SIZE], oref, pSeries->GetSeriesSize() + pMT->GetBaseSize()); - pSeries++; + for (SIZE_T j = 0; j < map->GetNumSeries(); j++) + { + CONSISTENCY_CHECK(pSeries <= map->GetHighestSeries()); + + memset((void*)&vcLayout[pSeries->GetSeriesOffset() - OBJECT_SIZE], oref, pSeries->GetSeriesSize() + pMT->GetBaseSize()); + pSeries++; + } } ExplicitClassTrust explicitClassTrust; diff --git a/src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.cs b/src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.cs new file mode 100644 index 0000000000000..99ca59fdd47e7 --- /dev/null +++ b/src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.cs @@ -0,0 +1,92 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Xunit; + +public class Test +{ + public struct WithORefs + { + public object F; + } + + public struct WithNoORefs + { + public int F; + } + + public ref struct WithByRefs + { + public ref int F; + } + + [StructLayout(LayoutKind.Explicit)] + public ref struct Explicit1 + { + [FieldOffset(0)] public Inner1 Field1; + public ref struct Inner1 + { + public WithORefs Field2; + } + } + + [StructLayout(LayoutKind.Explicit)] + public ref struct Explicit2 + { + [FieldOffset(0)] public Inner2 Field1; + public ref struct Inner2 + { + public WithNoORefs Field2; + } + } + + [StructLayout(LayoutKind.Explicit)] + public ref struct Explicit3 + { + [FieldOffset(0)] public Inner3 Field1; + public ref struct Inner3 + { + public WithByRefs Field2; + } + } + + [Fact] + public static void Validate_Explicit1() + { + Load1(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static string Load1() + { + return typeof(Explicit1).ToString(); + } + } + + [Fact] + public static void Validate_Explicit2() + { + Load2(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static string Load2() + { + return typeof(Explicit2).ToString(); + } + } + + [Fact] + public static void Validate_Explicit3() + { + Load3(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static string Load3() + { + return typeof(Explicit3).ToString(); + } + } +} diff --git a/src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.csproj b/src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.csproj new file mode 100644 index 0000000000000..0de2ecf447373 --- /dev/null +++ b/src/tests/Loader/classloader/explicitlayout/objrefandnonobjrefoverlap/case13.csproj @@ -0,0 +1,10 @@ + + + true + Exe + false + + + + +