Search code examples
c#cil

Not able to call 2 functions in CIL


I've started learning CIL today and have been using multiple tutorials to gain a basic understanding.

Currently I have 2 functions, 1 function adds 10 to the integer given as an argument and prints the answer. The second function takes 2 integers, add 5 to both of them and than multiplies the answers. Than it gets returned.

Both the functions work when only 1 of them is called, but when both are called I get the following error:

Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program.

at Bewerkingen.Program.Main(String[] args)

I do not know how to make the 2 functions work when called after each other. This is my code:

.assembly extern mscorlib {} 
.assembly Bewerkingen {} 
.module Bewerkingen.exe

.class public Functions
extends [mscorlib]System.Object
{
 .method public specialname void .ctor()
 {
  ret
 }
 .method public void Add(int32)
 {
  ldarg.1
  ldc.i4 10
  add
  call void [mscorlib]System.Console::WriteLine(int32)
  ret
 }
 .method public int32 add5mul(int32,int32)
 {
  ldarg.1
  ldc.i4 5
  add
  ldarg.2
  ldc.i4 5
  add
  mul
  ret
 }
}

.class Bewerkingen.Program
extends [mscorlib]System.Object
{
 .method static void Main(string[] args)
 cil managed
 {
  .entrypoint
  newobj instance void Functions::.ctor()
  ldc.i4 3
  call instance void Functions::Add(int32)
  ldc.i4 5
  ldc.i4 3
  call instance int32 Functions::add5mul(int32,int32)
  call void [mscorlib]System.Console::WriteLine(int32)
  ret
 }
}

Solution

  • You've got a slight problem, in that your .ctor needs to call the base object's ctor (ldarg.0, call instance void [System.Private.CoreLib]System.Object::.ctor(), ref). Both for Program and for Functions

    The main problem however is that you try and call add5mul on... nothing. There's no Functions object on the stack to call it on.

    // Push Functions instance onto stack
    // Stack: [functions]
      newobj instance void Functions::.ctor()
    
    // Push 3 onto stack
    // Stack: [3, functions]
      ldc.i4 3
    
    // Pop 3 and functions off the stack
    // Stack: []
      call instance void Functions::Add(int32)
    
    // Push 5 and 3 onto stack
    // Stack: [3, 5]
      ldc.i4 5
      ldc.i4 3
    
    // Pop 5, 3, and... nothing. We're missing the Functions instance to call it on.
      call instance int32 Functions::add5mul(int32,int32)
    

    You can fix this by duplicating the Functions instance before you consume it for the first time:

      newobj instance void Functions::.ctor()
      dup                                                     <-- Here
      ldc.i4 3
      call instance void Functions::Add(int32)
      ldc.i4 5
      ldc.i4 3
      call instance int32 Functions::add5mul(int32,int32)
      call void [mscorlib]System.Console::WriteLine(int32)
      ret
    

    You can also store that Functions instance in a local slot:

    .method static void Main(string[] args)
     cil managed
     {
      .locals init (
        [0] class Functions
      )
      .entrypoint
      newobj instance void Functions::.ctor()
      stloc.0
      ldloc.0
      ldc.i4 3
      call instance void Functions::Add(int32)
      ldloc.0
      ldc.i4 5
      ldc.i4 3
      call instance int32 Functions::add5mul(int32,int32)
      call void [mscorlib]System.Console::WriteLine(int32)
      ret
     }
    
    

    SharpLab.io is a great resource for learning IL. Here's your code, translated to C# then decompiled into IL.