Skip to content

Commit fc09b22

Browse files
authored
[mono] Force Mono to respect explicit struct size when LayoutKind.Sequential is used (#101529)
* respect explicit size with sequential layout * test for sequential layout with explicit size
1 parent fcc2408 commit fc09b22

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

src/mono/mono/metadata/class-init.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2331,7 +2331,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
23312331

23322332
instance_size = MAX (real_size, instance_size);
23332333

2334-
if (instance_size & (min_align - 1)) {
2334+
if (instance_size & (min_align - 1) && !explicit_size) {
23352335
instance_size += min_align - 1;
23362336
instance_size &= ~(min_align - 1);
23372337
}

src/mono/mono/metadata/marshal.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5749,7 +5749,7 @@ MonoMarshalType *
57495749
mono_marshal_load_type_info (MonoClass* klass)
57505750
{
57515751
int j, count = 0;
5752-
guint32 native_size = 0, min_align = 1, packing;
5752+
guint32 native_size = 0, min_align = 1, packing, explicit_size = 0;
57535753
MonoMarshalType *info;
57545754
MonoClassField* field;
57555755
gpointer iter;
@@ -5793,7 +5793,7 @@ mono_marshal_load_type_info (MonoClass* klass)
57935793
info->num_fields = count;
57945794

57955795
/* Try to find a size for this type in metadata */
5796-
mono_metadata_packing_from_typedef (m_class_get_image (klass), m_class_get_type_token (klass), NULL, &native_size);
5796+
explicit_size = mono_metadata_packing_from_typedef (m_class_get_image (klass), m_class_get_type_token (klass), NULL, &native_size);
57975797

57985798
if (m_class_get_parent (klass)) {
57995799
int parent_size = mono_class_native_size (m_class_get_parent (klass), NULL);
@@ -5879,6 +5879,9 @@ mono_marshal_load_type_info (MonoClass* klass)
58795879
align_size = FALSE;
58805880
else
58815881
min_align = MIN (min_align, packing);
5882+
} else if (layout == TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT) {
5883+
if (explicit_size && native_size == info->native_size)
5884+
align_size = FALSE;
58825885
}
58835886
}
58845887

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
using System;
4+
using System.Runtime.InteropServices;
5+
using TestLibrary;
6+
using Xunit;
7+
8+
public static unsafe class MarshalUnalignedStructArrayTest
9+
{
10+
[Fact]
11+
public static void TestEntryPoint()
12+
{
13+
/*
14+
* This test validates that the size and offsets of InnerStruct and OuterStruct are as expected.
15+
* It also demonstrates accessing unaligned data in an array.
16+
*/
17+
// Validate that both InnerStruct and OuterStruct have the correct size
18+
Assert.Equal(12, sizeof(InnerStruct));
19+
Assert.Equal(24, sizeof(OuterStruct));
20+
21+
// Validate that the fields of InnerStruct are at the expected offsets
22+
Assert.Equal(0, Marshal.OffsetOf<InnerStruct>("F0").ToInt32());
23+
Assert.Equal(8, Marshal.OffsetOf<InnerStruct>("F1").ToInt32());
24+
25+
// Validate that the fields of OuterStruct are at the expected offsets
26+
Assert.Equal(0, Marshal.OffsetOf<OuterStruct>("F0").ToInt32());
27+
Assert.Equal(8, Marshal.OffsetOf<OuterStruct>("F1").ToInt32());
28+
Assert.Equal(20, Marshal.OffsetOf<OuterStruct>("F2").ToInt32());
29+
30+
// Validate that we are able to access unaligned in an array
31+
InnerStruct[] arrStructs = new InnerStruct[]
32+
{
33+
new InnerStruct(1, 2),
34+
new InnerStruct(3, 4),
35+
new InnerStruct(5, 6),
36+
};
37+
38+
fixed (InnerStruct* pStruct = &arrStructs[0])
39+
{
40+
byte* ptr = (byte*)pStruct;
41+
ptr += 12;
42+
Assert.Equal(3, *(long*)ptr);
43+
Assert.Equal(4, *(int*)(ptr + 8));
44+
}
45+
46+
}
47+
}
48+
49+
[StructLayout(LayoutKind.Sequential, Size = 12)]
50+
struct InnerStruct
51+
{
52+
public long F0;
53+
public uint F1;
54+
55+
public InnerStruct(long f0, uint f1)
56+
{
57+
F0 = f0;
58+
F1 = f1;
59+
}
60+
}
61+
62+
[StructLayout(LayoutKind.Sequential, Size = 24)]
63+
struct OuterStruct
64+
{
65+
public sbyte F0;
66+
public InnerStruct F1;
67+
public uint F2;
68+
69+
public OuterStruct(sbyte f0, InnerStruct f1, uint f2)
70+
{
71+
F0 = f0;
72+
F1 = f1;
73+
F2 = f2;
74+
}
75+
}

0 commit comments

Comments
 (0)