MSIL方法不需要ret

Nic*_*ick 6 methods il cil return ilasm

我最近一直在编写MSIL并用ilasm编译它,当时我注意到方法确实需要一个ret指令从方法结束返回; 例如,我应该写这样的代码:

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

但是,如果省略ret,代码仍会运行并输出"Hello World!" 完美.起初我认为这可能是入口点方法特有的,但是ilasm很高兴地编译这个代码既没有警告也没有错误:

.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
}
Run Code Online (Sandbox Code Playgroud)

请注意,Main()和Foo()都没有return语句.Foo()甚至有一个返回值!编译并运行此代码时,我得到以下输出:

你好,世界!来自Foo的你好!再见!Foo回归:42!

该程序也正常终止.然后我想也许ilasm是自动插入ret语句,但是在用ildasm查看程序之后,这些方法与上面的代码相同,即没有返回.

奇怪的是,当我试图用DotPeek反编译该方法时,它拒绝用两个方法体替换 // ISSUE: unable to decompile the method.

如果我添加ret语句并重新编译DotPeek可以反复编译这两种方法而不会出现问题.

有人可以解释一下这里发生了什么吗?

svi*_*ick 5

我认为这是因为CLR有时会接受无效的IL,并且只有当它遇到它实际上无法执行的IL时才会抛出InvalidProgramException.我的猜测是出于性能原因这样做:验证IL遵循所有规则会太慢.

如果要验证IL是否有效,则应在其上使用PEVerify.

当你的代码无效时,像DotPeek这样的工具无法处理它也就不足为奇了.