Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ILGenerator does not properly handle function pointers #111003

Open
jgh07 opened this issue Dec 30, 2024 · 1 comment
Open

ILGenerator does not properly handle function pointers #111003

jgh07 opened this issue Dec 30, 2024 · 1 comment
Labels
area-System.Reflection.Emit untriaged New issue has not been triaged by the area owner

Comments

@jgh07
Copy link

jgh07 commented Dec 30, 2024

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

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Dec 30, 2024
@jgh07
Copy link
Author

jgh07 commented Dec 31, 2024

Based on some further tesing, when declaring new fields of function pointer types, .NET 9 creates the same non-existent type as above and .NET Framework creates fields of type native int. While that is not ideal either, it is at least better than in .NET 9, where the program will crash no matter what.

I noticed that there is (active?) work going on regarding function pointer types in Reflection.Emit, but I do feel like this can be considered a separate issue since it is dealing with functionality that used to work fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Reflection.Emit untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

1 participant