runtime: Calli with thiscall convention fails at runtime

This program (IL at the bottom of the issue) causes .net core 3.1 to fail with:

Unhandled exception. System.InvalidProgramException: Common Language Runtime detected an invalid program.
   at UnmanagedFunctionPointer.Main()

The program defines a delegate type that takes a pointer to a structure and a float, and returns a structure that contains a field from that first pointer and the float. It then creates a thiscall function pointer stub around the delegate by using Marshal.CreateFunctionPointerForDelegate, and calli’s that pointer. On .net framework, this correctly prints 12.3. On .net core, this fails with the above exception. /cc @jkoritzinsky


.assembly UnmanagedFunctionPointer
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = (
        01 00 08 00 00 00 00 00
    )
    .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = (
        01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78
        63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01
    )
    .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = (
        01 00 07 01 00 00 00 00
    )
    .permissionset reqmin = {
        [mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {
            property bool SkipVerification = bool(true)
        }
    }
    .hash algorithm 0x00008004 // SHA1
    .ver 0:0:0:0
}

.module UnmanagedFunctionPointer.dll

.class public sequential ansi sealed beforefieldinit ReturnWrapper
    extends [mscorlib]System.ValueType
{
    // Fields
    .field public int32 i1
    .field public float32 f2

    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor (
            int32 i1,
            float32 f2
        ) cil managed 
    {
        // Method begins at RVA 0x205a
        // Code size 16 (0x10)
        .maxstack 8

        nop
        ldarg.0
        ldarg.1
        stfld int32 ReturnWrapper::i1
        ldarg.0
        ldarg.2
        stfld float32 ReturnWrapper::f2
        ret
    } // end of method ReturnWrapper::.ctor

} // end of class ReturnWrapper

.class public sequential ansi sealed beforefieldinit S
    extends [mscorlib]System.ValueType
{
    // Fields
    .field public int32 i

    // Methods
    .method public hidebysig static 
        valuetype ReturnWrapper GetReturn (
            valuetype S* s,
            float32 f
        ) cil managed 
    {
        // Method begins at RVA 0x208c
        // Code size 18 (0x12)
        .maxstack 2
        .locals init (
            [0] valuetype ReturnWrapper
        )

        nop
        ldarg.0
        ldfld int32 S::i
        ldarg.1
        newobj instance void ReturnWrapper::.ctor(int32, float32)
        ret
    } // end of method S::GetReturn

} // end of class S

.class public auto ansi beforefieldinit UnmanagedFunctionPointer
    extends [mscorlib]System.Object
{
    // Nested Types
    .class nested private auto ansi sealed MultipleParams
        extends [mscorlib]System.MulticastDelegate
    {
        .custom instance void [mscorlib]System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute::.ctor(valuetype [mscorlib]System.Runtime.InteropServices.CallingConvention) = (
            01 00 04 00 00 00 00 00
        )
        // Methods
        .method public hidebysig specialname rtspecialname 
            instance void .ctor (
                object 'object',
                native int 'method'
            ) runtime managed 
        {
        } // end of method MultipleParams::.ctor

        .method public hidebysig newslot virtual 
            instance valuetype ReturnWrapper Invoke (
                valuetype S* s,
                float32 f
            ) runtime managed 
        {
        } // end of method MultipleParams::Invoke

        .method public hidebysig newslot virtual 
            instance class [mscorlib]System.IAsyncResult BeginInvoke (
                valuetype S* s,
                float32 f,
                class [mscorlib]System.AsyncCallback callback,
                object 'object'
            ) runtime managed 
        {
        } // end of method MultipleParams::BeginInvoke

        .method public hidebysig newslot virtual 
            instance valuetype ReturnWrapper EndInvoke (
                class [mscorlib]System.IAsyncResult result
            ) runtime managed 
        {
        } // end of method MultipleParams::EndInvoke

    } // end of class MultipleParams

    // Methods
    .method public hidebysig static 
        void Main () cil managed 
    {
        // Method begins at RVA 0x20e0
        // Code size 37 (0x25)
        .maxstack 4
        .entrypoint
        .locals init (
            [0] native int,
            [1] valuetype S,
            [2] valuetype ReturnWrapper
        )

        nop
        ldnull
        ldftn valuetype ReturnWrapper S::GetReturn(valuetype S*, float32)
        newobj instance void UnmanagedFunctionPointer/MultipleParams::.ctor(object, native int)
        call native int [mscorlib]System.Runtime.InteropServices.Marshal::GetFunctionPointerForDelegate<class UnmanagedFunctionPointer/MultipleParams>(!!0)
        stloc.0
        ldloc.0
        box [mscorlib]System.IntPtr
        call void [mscorlib]System.GC::KeepAlive(object)
        ldloca.s 1
        initobj [UnmanagedFunctionPointer]S
        ldloca.s 1
        ldc.i4.1
        stfld int32 [UnmanagedFunctionPointer]S::i
        ldloca.s 1
        conv.u
        ldc.r4 2.3
        ldloc.0
        calli unmanaged thiscall valuetype ReturnWrapper(valuetype S*, float32)
        stloc.2
        ldloc.2
        ldfld int32 [UnmanagedFunctionPointer]ReturnWrapper::i1
        call void [mscorlib]System.Console::Write(int32)
        ldloc.2
        ldfld float32 [UnmanagedFunctionPointer]ReturnWrapper::f2
        call void [mscorlib]System.Console::Write(float32)
        ret
    } // end of method UnmanagedFunctionPointer::GetFuncPtrMultipleParams
} // end of class UnmanagedFunctionPointer

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 22 (22 by maintainers)

Commits related to this issue

Most upvoted comments

I think we have an issue tracking it but we haven’t decided on the final shape (if we’re going to do Thiscall + Stdcall or make a new type CallConvInstanceMethod to avoid confusion)

I think we have an issue tracking it

Is this an internal issue? If not, can you link it? I couldn’t find it anywhere on the runtime repo.