Skip to content

ILGenerator does not properly handle function pointers #111003

Open
@jgh07

Description

@jgh07

Description

When using the new PersistedAssemblyBuilder introduced in .NET 9 and trying to load a field whose type is a function pointer, invalid IL gets generated, causing the application to crash with a System.MissingFieldException when executed.

(On another note, is there currently a way of generating such fields using ILGenerator? How do you get a Type object for a function pointer at runtime?)

Reproduction Steps

The following code reproduces the problem in .NET 9. It doesn't produce a working executable by itself since I did not set the entry point, so to execute code from the generated assembly, you would need to reference it by some other assembly.

using System;
using System.Reflection;
using System.Reflection.Emit;

PersistedAssemblyBuilder ab = new(new("Test"), typeof(object).Assembly);
ModuleBuilder mb = ab.DefineDynamicModule("Test.dll");
TypeBuilder tb = mb.DefineType("Program");

MethodBuilder methb = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static);
methb.SetReturnType(typeof(void));

ILGenerator il = methb.GetILGenerator();

il.Emit(OpCodes.Call, typeof(Container).GetMethod("Init"));

il.Emit(OpCodes.Ldc_I4_2);
il.Emit(OpCodes.Ldc_I4_3);
il.Emit(OpCodes.Ldsfld, typeof(Container).GetField("Method"));
il.EmitCalli(OpCodes.Calli, System.Runtime.InteropServices.CallingConvention.Winapi, typeof(int), [typeof(int), typeof(int)]);
il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Static, null, [typeof(int)], []));

il.Emit(OpCodes.Ret);

tb.CreateType();
ab.Save("Test.dll");

public unsafe static class Container
{
    public static delegate*<int, int, int> Method;

    public static int Add(int a, int b) => a + b;
    public static void Init() => Method = &Add;
}

Expected behavior

The following IL is generated as the method body for Test::Main() when running under .NET Framework 4.8.1:

call void [Issue]Container::Init()
ldc.i4.2
ldc.i4.3
ldsfld method int32 *(int32,int32) [Issue]Container::Method
calli unmanaged stdcall int32(int32,int32)
call void [mscorlib]System.Console::WriteLine(int32)
ret

The generated application runs correctly and prints 5.

Actual behavior

The following IL is generated when running under .NET 9:

call void [Issue]Container::Init()
ldc.i4.2
ldc.i4.3
ldsfld class [System.Private.CoreLib]$TR$2 [Issue]Container::Method
calli int32(int32,int32)
call void [System.Console]System.Console::WriteLine(int32)
ret

The ldsfld instruction, instead of correctly using a function pointer as the field type, generates some garbage type (as is displayed by ildasm).

When executed, the following exception is thrown:

Unhandled exception. System.MissingFieldException: Field not found: 'Container.Method'.
   at Program.Main()

Regression?

API was newly introduced with .NET 9. Works fine in .NET Framework 4.8.1.

Known Workarounds

Don't know of any.

Configuration

  • .NET 9.0.100
  • Windows 11 23H2
  • Debug, x64

Other information

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions