Lokad/ILPack

ILVerification fails on delegate ctor of generated assembly

AqlaSolutions opened this issue · 2 comments

[IL]: Error [CallCtor]: [.ProcessBookDelegate::.ctor(object, native int)][offset 0x00000001] call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj.
 [IL]: Error [StackUnexpected]: [.ProcessBookDelegate::.ctor(object, native int)][offset 0x00000007][found ref '[TriAxis.RunSharp.Tests.12_Delegates.GenBookstore]ProcessBookDelegate'] Unexpected type on the stack.
 [IL]: Error [ThisUninitReturn]: [.ProcessBookDelegate::.ctor(object, native int)][offset 0x0000000C] Return from .ctor when this is uninitialized.
 [IL]: Error [CallCtor]: [.ProcessBookDelegate::.ctor(object, native int)][offset 0x00000001] call to .ctor only allowed to initialize this pointer from within a .ctor. Try newobj.
 [IL]: Error [StackUnexpected]: [.ProcessBookDelegate::.ctor(object, native int)][offset 0x00000007][found ref '[TriAxis.RunSharp.Tests.12_Delegates.GenBookstore]ProcessBookDelegate'] Unexpected type on the stack.
 [IL]: Error [ThisUninitReturn]: [.ProcessBookDelegate::.ctor(object, native int)][offset 0x0000000C] Return from .ctor when this is uninitialized.

TriAxis.RunSharp.Tests.12_Delegates.GenBookstore.zip

Source code:

public delegate void ProcessBookDelegate();

public class Test
{
    public static void Main()
    {
    }
}

ildasm:

// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi sealed ProcessBookDelegate
       extends [System.Runtime]System.MulticastDelegate
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  {
  } // end of method ProcessBookDelegate::.ctor

  .method public hidebysig newslot virtual 
          instance class [System.Runtime]System.IAsyncResult 
          BeginInvoke(class [System.Runtime]System.AsyncCallback callback,
                      object 'object') runtime managed
  {
  } // end of method ProcessBookDelegate::BeginInvoke

  .method public hidebysig newslot virtual 
          instance void  EndInvoke(class [System.Runtime]System.IAsyncResult result) runtime managed
  {
  } // end of method ProcessBookDelegate::EndInvoke

  .method public hidebysig newslot virtual 
          instance void  Invoke() runtime managed
  {
  } // end of method ProcessBookDelegate::Invoke

} // end of class ProcessBookDelegate

.class public auto ansi beforefieldinit Test
       extends [System.Runtime]System.Object
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
    IL_0006:  ldarg.0
    IL_0007:  call       instance void Test::$$ctor$PST06000007()
    IL_000c:  ret
  } // end of method Test::.ctor

  .method public hidebysig static void  Main() cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method Test::Main

  .method privatescope hidebysig instance void 
          $$ctor$PST06000007() cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method Test::$$ctor

} // end of class Test

The same code compiled for .NET 5 in VS generates almost same IL but passes ILVerification without errors:

// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi sealed ProcessBookDelegate
       extends [System.Runtime]System.MulticastDelegate
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  {
  } // end of method ProcessBookDelegate::.ctor

  .method public hidebysig newslot virtual 
          instance class [System.Runtime]System.IAsyncResult 
          BeginInvoke(class [System.Runtime]System.AsyncCallback callback,
                      object 'object') runtime managed
  {
  } // end of method ProcessBookDelegate::BeginInvoke

  .method public hidebysig newslot virtual 
          instance void  EndInvoke(class [System.Runtime]System.IAsyncResult result) runtime managed
  {
  } // end of method ProcessBookDelegate::EndInvoke

  .method public hidebysig newslot virtual 
          instance void  Invoke() runtime managed
  {
  } // end of method ProcessBookDelegate::Invoke

} // end of class ProcessBookDelegate

.class public auto ansi beforefieldinit Test
       extends [System.Runtime]System.Object
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [System.Runtime]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Test::.ctor

  .method public hidebysig static void  Main() cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method Test::Main

} // end of class Test

Re-saving the erroneous IL in dnSpy generates assembly that passes ILVerification:
unchanged.zip

// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi sealed ProcessBookDelegate
       extends [System.Private.CoreLib]System.MulticastDelegate
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  {
  } // end of method ProcessBookDelegate::.ctor

  .method public hidebysig newslot virtual 
          instance class [System.Private.CoreLib]System.IAsyncResult 
          BeginInvoke(class [System.Private.CoreLib]System.AsyncCallback callback,
                      object 'object') runtime managed
  {
  } // end of method ProcessBookDelegate::BeginInvoke

  .method public hidebysig newslot virtual 
          instance void  EndInvoke(class [System.Private.CoreLib]System.IAsyncResult result) runtime managed
  {
  } // end of method ProcessBookDelegate::EndInvoke

  .method public hidebysig newslot virtual 
          instance void  Invoke() runtime managed
  {
  } // end of method ProcessBookDelegate::Invoke

} // end of class ProcessBookDelegate

.class public auto ansi beforefieldinit Test
       extends [System.Private.CoreLib]System.Object
{
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [System.Private.CoreLib]System.Object::.ctor()
    IL_0006:  ldarg.0
    IL_0007:  call       instance void Test::$$ctor$PST06000007()
    IL_000c:  ret
  } // end of method Test::.ctor

  .method public hidebysig static void  Main() cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method Test::Main

  .method privatescope hidebysig instance void 
          $$ctor$PST06000007() cil managed
  {
    // Code size       1 (0x1)
    .maxstack  8
    IL_0000:  ret
  } // end of method Test::$$ctor

} // end of class Test

The only difference in this fix is using System.Private.CoreLib library instead of System.Runtime and that it's a dll, not exe.

Expected behavior: generated by ILPack assembly should pass IL verification without issues.

@AqlaSolutions Thanks a lot for the report. I don't have resources to devote to this at this point of time, but it's a nice precisely report nonetheless.

@vermorel This one is a duplicate of issue #121 so I suggest closing it for now. The original issue describes the problem more precisely with proper root cause pointing.