Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MSIL Methods not requiring ret

I have recently been playing around with writing MSIL and compiling it with ilasm, when I noticed that methods do require a ret instruction to return from the end of the method; For example I should be supposed to write code like this:

.method static void Main()
{
    .entrypoint
    ldstr "Hello World!"
    call void [mscorlib]System.Console::WriteLine(string)
    ret //I am returning properly
}

However, if I omit the ret, the code still runs and outputs "Hello World!" perfectly. At first I thought this could be specific to the entrypoint method, but ilasm happily compiles this code with neither warnings nor errors:

.assembly Program{}
.assembly extern mscorlib{}
.method static void Main()
{
.entrypoint
ldstr   "Hello World!"
call    void    [mscorlib]System.Console::WriteLine(string)
ldstr   "Foo returned: {0}!"
call    int32   Foo()
box     int32
call    void    [mscorlib]System.Console::WriteLine(string, object)
}
.method static int32 Foo()
{
ldstr   "Hello from Foo!"
call    void    [mscorlib]System.Console::WriteLine(string)
ldstr   "GoodBye!"
call    void    [mscorlib]System.Console::WriteLine(string)
ldc.i4  42
}

Notice that neither Main() nor Foo() have return statements. Foo() even has a return value! When this code is compiled and run I get the following output:

Hello World! Hello from Foo! GoodBye! Foo returned: 42!

The program also terminates normally. I then thought that perhaps ilasm was auto-inserting ret statements, but after looking at the program with ildasm the methods were identical to the code above i.e. without returns.

Curiously, when I attempted to decompile the method with DotPeek, it flat out refused replacing both method bodies with // ISSUE: unable to decompile the method.

If I add the ret statements and recompile DotPeek can decompile both methods without issue.

Can someone please explain what is going on here?

like image 606
Nick Avatar asked Aug 18 '13 01:08

Nick


1 Answers

I think this is because the CLR sometimes accepts IL that's not valid, and only when it encounters IL that it actually can't execute, it throws InvalidProgramException. My guess is that this is done for performance reasons: verifying that the IL follows all the rules would be too slow.

If you want to verify that your IL is valid, you should use PEVerify on it.

And when you have invalid code, it's not surprising that some tools like DotPeek can't handle it.

like image 161
svick Avatar answered Oct 20 '22 16:10

svick