Search code examples
c#clrbytecodecillow-level

How can I write and compile MSIL or CIL into an executable?


I'm currently studying a bit of MSIL/CIL code, and I'm trying to compile a basic Hello World written in MSIL. However I'm having some troubles.

At first I've read this article. Then I put the code in the article within an file and use this Stack Overflow answer as a reference as to how to use ilasm.exe and PEVerify.exe in order to make that code into an executable.

This is what I've started with:

.method static void main()
{
    .entrypoint
    .maxstack 1

    ldstr "Hello world!"
    call void [mscorlib]System.Console::WriteLine(string)

    ret
}

ilasm.exe makes the executable, however when I run PEVerify I get this output:

PS C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools> .\PEVerify.exe /MD /IL C:\clr_code\hello_world_cil.exe

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

The module 'C:\clr_code\hello_world_cil.exe' was expected to contain an assembly manifest.
1 Error(s) Verifying C:\clr_code\hello_world_cil.exe

I've searched for this error but only find references about dynamic code generation using reflection, which didn't help.

Then I decided to build a simple hello world Console Application in Visual Studio and used dotPeek to get the IL code. I then changed the hello world message and tried to compile the output, this is the code I've used:

.class private auto ansi beforefieldinit
  HelloMSILV472.Program
    extends [mscorlib]System.Object
{

  .method private hidebysig static void
    Main(
      string[] args
    ) cil managed
  {
    .entrypoint
    .maxstack 8

    // [6 9 - 6 10]
    IL_0000: nop

    // [7 13 - 7 53]
    IL_0001: ldstr        "Hello MSIL!"
    IL_0006: call         void [mscorlib]System.Console::WriteLine(string)
    IL_000b: nop

    // [8 13 - 8 35]
    IL_000c: call         int32 [mscorlib]System.Console::Read()
    IL_0011: pop

    // [9 9 - 9 10]
    IL_0012: ret

  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname instance void
    .ctor() cil managed
  {
    .maxstack 8

    IL_0000: ldarg.0      // this
    IL_0001: call         instance void [mscorlib]System.Object::.ctor()
    IL_0006: nop
    IL_0007: ret

  } // end of method Program::.ctor
} // end of class HelloMSILV472.Program

However I've got the same error. I also don't yet if there's difference between .NET Framework IL and .NET Core IL, and if the target framework affects how ilasm.exe deals with the code, so I tried the output IL of a .NET Framework application and the output IL of a .NET Core application. However I've got the same error.

How can I build a MSIL/CIL basic hello world console application from scratch, using something like Notepad++ and then compile it and have a running executable?


Solution

  • Your initial sample is fine - you just need to declare an assembly for your code to reside in by putting this line at the top:

    .assembly MyTestAssembly {}
    

    Additionally, you should reference any external assemblies that your code relies on. In this case since it's only mscorlib, ilasm will find it after displaying a warning, so not mandatory, but it would be mandatory in order to call other custom code:

    .assembly extern mscorlib {}
    

    Final code:

    .assembly extern mscorlib {}
    .assembly MyTestAssembly {}
    .method static void main()
    {
        .entrypoint
        .maxstack 1
    
        ldstr "Hello world!"
        call void [mscorlib]System.Console::WriteLine(string)
    
        ret
    }