dotnet / ILMerge

ILMerge is a static linker for .NET Assemblies.
MIT License
1.23k stars 170 forks source link

Invalid IL after merge > InvalidProgramException at runtime #67

Closed bitapparat closed 4 years ago

bitapparat commented 5 years ago

I ran into a case where ilmerge corrupts the IL code during merge. An example would be the Microsoft.CodeAnalysis.dll assembly. The method Microsoft.CodeAnalysis.SyntaxToken.GetFirstToken, among others, is missing an IL statement after the merge.

IL code before the merge:

  .method public hidebysig instance valuetype Microsoft.CodeAnalysis.SyntaxToken 
          GetFirstToken([opt] bool includeZeroWidth,
                        [opt] bool includeSkipped,
                        [opt] bool includeDirectives,
                        [opt] bool includeDocumentationComments) cil managed
  {
    .param [1] = bool(false)
    .param [2] = bool(false)
    .param [3] = bool(false)
    .param [4] = bool(false)
    // Code size       18 (0x12)
    .maxstack  8
    IL_0000:  ldsfld     class Microsoft.CodeAnalysis.SyntaxNavigator Microsoft.CodeAnalysis.SyntaxNavigator::Instance
    IL_0005:  ldarga.s   0
    IL_0007:  ldarg.1
    IL_0008:  ldarg.2
    IL_0009:  ldarg.3
    IL_000a:  ldarg.s    includeDocumentationComments
    IL_000c:  callvirt   instance valuetype Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.SyntaxNavigator::GetFirstToken(class Microsoft.CodeAnalysis.SyntaxNode&, bool, bool, bool, bool)
    IL_0011:  ret
  } // end of method SyntaxNode::GetFirstToken

And the code after the merge:

  .method public hidebysig instance valuetype Microsoft.CodeAnalysis.SyntaxToken 
          GetFirstToken([opt] bool includeZeroWidth,
                        [opt] bool includeSkipped,
                        [opt] bool includeDirectives,
                        [opt] bool includeDocumentationComments) cil managed
  {
    .param [1] = bool(false)
    .param [2] = bool(false)
    .param [3] = bool(false)
    .param [4] = bool(false)
    // Code size       16 (0x10)
    .maxstack  8
    IL_0000:  ldsfld     class Microsoft.CodeAnalysis.SyntaxNavigator Microsoft.CodeAnalysis.SyntaxNavigator::Instance
    IL_0005:  ldarg.1
    IL_0006:  ldarg.2
    IL_0007:  ldarg.3
    IL_0008:  ldarg.s    includeDocumentationComments
    IL_000a:  callvirt   instance valuetype Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.SyntaxNavigator::GetFirstToken(class Microsoft.CodeAnalysis.SyntaxNode&, bool, bool, bool, bool)
    IL_000f:  ret
  } // end of method SyntaxNode::GetFirstToken

Notice the missing "IL_0005: ldarga.s 0".

toehead2001 commented 4 years ago

I too ran into this issue with the exact same Method.

If it helps to resolve the issue, the source code for this Method is here: https://github.com/dotnet/roslyn/blob/master/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs#L891

mike-barnett commented 4 years ago

Sorry about that! I think I may have fixed it, but it would be great if you could test it out. I have created a branch, 'issue67' that contains the fix. Could you build a local copy from that branch and see if it fixes the problem for you? Here is the small repro that I created in order to test it out:

public class C { public void Foo() { Bar(this); } public void Bar(in C c){} }

toehead2001 commented 4 years ago

Yes, it seems to be working without issue now. Thanks for the quick work.

mike-barnett commented 4 years ago

You're welcome! Although @bitapparat might not agree about it being "quick".