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

BadImageFormatException thrown on ldftn opcode with interface method since .NET Core 3.0 #521

Closed
epeshk opened this issue Dec 4, 2019 · 3 comments

Comments

@epeshk
Copy link
Contributor

epeshk commented Dec 4, 2019

Since .NET Core 3.0 ldftn opcode can't be used with interface methods (probably due to default implementations-related changes). Following code works on .NET Framework & .NET Core 2.x, but fails on .NET Core 3.0-3.1. Tested on Windows x64.

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

namespace Repro
{
    public interface IA
    {
        void X();
    }
    public class A : IA
    {
        public void X() => Console.WriteLine("!");
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Repro"), AssemblyBuilderAccess.Run);
            var module = assembly.DefineDynamicModule("module");
            var typeBuilder = module.DefineType(
            	"Type", 
            	TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.Public);
            var method = typeBuilder.DefineMethod(
            	"Method",
            	MethodAttributes.Static | MethodAttributes.Public,
            	typeof(Action<IA>),
            	Type.EmptyTypes);

            var il = method.GetILGenerator();
            il.Emit(OpCodes.Ldnull);
            il.Emit(OpCodes.Ldftn, typeof(IA).GetMethod(nameof(IA.X)));
            il.Emit(OpCodes.Newobj, typeof(Action<IA>).GetConstructor(new[]{typeof(object), typeof(IntPtr)}));
            il.Emit(OpCodes.Ret);
            
            var type = typeBuilder.CreateType();
            
            var methodInfo = type.GetMethod(method.Name, BindingFlags.Static|BindingFlags.Public);

            var callable = (Func<Action<IA>>) methodInfo.CreateDelegate(typeof(Func<Action<IA>>));
            callable()(new A());
        }
    }
}

Without manual code generation, this instruction could be obtained from C++/CLI compiler.

public interface struct IA
{
	void X();
};

public ref struct A : IA
{
	static initonly Action<IA^>^ XAction = gcnew Action<IA^>(&IA::X);
	virtual void X() {
		Console::WriteLine("!");
	}
};

In this case, BadImageFormatException will be thrown from A static constructor.

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Dec 4, 2019
@jeffschwMSFT jeffschwMSFT removed the untriaged New issue has not been triaged by the area owner label Jan 8, 2020
@jeffschwMSFT jeffschwMSFT added this to the 5.0 milestone Jan 8, 2020
@jeffschwMSFT
Copy link
Member

cc @MichalStrehovsky

@MichalStrehovsky
Copy link
Member

I made sure to preserve the legacy behavior in the original implementation of default interface methods (it was possible to keep it), but the C# compiler team requested that we do a conscious breaking change here. dotnet/coreclr#23032 was the breaking change. See the linked issue.

Cc @AlekseyTs

@davidwrighton
Copy link
Member

Support for the ldftn opcode in combination with an interface method and a null object parameter that was then passed to a delegate constructor was an extension to the ECMA 335 specification which the .NET runtime no longer supports. However, it is still possible to construct such delegates through reflection.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants