Skip to content

Field offset not being set when offset is 0 on explicit layout types. #105795

Closed
@TrueLunacy

Description

@TrueLunacy

Description

Field offsets aren't being set in the IL for types with an offset of zero. This causes the types to fail to load at runtime.

The cause, as far as I can tell, seems to be from lines 641 in the WriteFields function in ModuleBuilderImpl.cs

if (field._offset > 0 && (typeBuilder.Attributes & TypeAttributes.ExplicitLayout) != 0)
{
    AddFieldLayout(handle, field._offset);
}

Reproduction Steps

using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Loader;

PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly);
ModuleBuilder mob = ab.DefineDynamicModule("MyModule");

TypeBuilder exp = mob.DefineType("ExplicitLayoutType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.ExplicitLayout, typeof(ValueType), 8);
var f1 = exp.DefineField("first", typeof(int), FieldAttributes.Public);
f1.SetOffset(0);
var f2 = exp.DefineField("second", typeof(int), FieldAttributes.Public);
f2.SetOffset(4);
exp.CreateType();

TypeBuilder tb = mob.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
tb.DefineField("structfield", exp, FieldAttributes.Public | FieldAttributes.Static);

MethodBuilder meb = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static,
                                                        typeof(int), new Type[] { typeof(int), typeof(int) });
ILGenerator il = meb.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);

tb.CreateType();

using var stream = new MemoryStream();
ab.Save(stream); 
stream.Seek(0, SeekOrigin.Begin);
Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(stream);
MethodInfo method = assembly.GetType("MyType").GetMethod("SumMethod");
Console.WriteLine(method.Invoke(null, new object[] { 5, 10 }));

Expected behavior

Type loads, program prints 15.

Actual behavior

Unhandled exception. System.TypeLoadException: Could not load type 'ExplicitLayoutType' from assembly 'MyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' because field 'first' was not given an explicit offset.
   at System.Reflection.RuntimeAssembly.GetTypeCore(QCallAssembly assembly, String typeName, ReadOnlySpan`1 nestedTypeNames, Int32 nestedTypeNamesLength, ObjectHandleOnStack retType)
   at System.Reflection.RuntimeAssembly.GetTypeCore(String typeName, ReadOnlySpan`1 nestedTypeNames, Boolean throwOnError, Boolean ignoreCase)
   at System.Reflection.TypeNameResolver.GetType(String escapedTypeName, ReadOnlySpan`1 nestedTypeNames, TypeName parsedName)
   at System.Reflection.TypeNameResolver.GetSimpleType(TypeName typeName)
   at System.Reflection.TypeNameResolver.Resolve(TypeName typeName)
   at System.Reflection.TypeNameResolver.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Assembly topLevelAssembly)
   at Program.<Main>$(String[] args) in Program.cs:line 32

Regression?

No response

Known Workarounds

No response

Configuration

Arch Linux x64, dotnet 9.0.100-rc.1.24381.3

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.Reflection.Emitbughelp wanted[up-for-grabs] Good issue for external contributorsin-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions