我最近一直在玩MSIL并用ilasm编译它,当时我注意到方法确实需要从方法结束返回一个ret指令; 例如,我应该应该写这样的代码:
.method static void Main()
{
.entrypoint
ldstr "Hello World!"
call void [mscorlib]System.Console::WriteLine(string)
ret //I am returning properly
}
但是,如果省略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
}
请注意,Main()和Foo()都没有return语句。 Foo()甚至有一个返回值! 编译并运行此代码时,我得到以下输出:
Hello World! 来自Foo的你好! 再见! Foo回来了:42!
该程序也正常终止。然后我想也许ilasm是自动插入ret语句,但是在用ildasm查看程序之后,这些方法与上面的代码相同,即没有返回。
奇怪的是,当我尝试使用DotPeek反编译该方法时,它拒绝用// ISSUE: unable to decompile the method.
替换两个方法体
如果我添加了ret语句并重新编译,DotPeek可以毫无问题地反编译这两种方法。
有人可以解释一下这里发生了什么吗?
答案 0 :(得分:5)
我认为这是因为CLR有时会接受无效的IL,并且只有当遇到IL它实际上无法执行时,它才会抛出InvalidProgramException
。我的猜测是出于性能原因这样做:验证IL遵循所有规则会太慢。
如果您想验证您的IL是否有效,您应该使用PEVerify。
当你的代码无效时,像DotPeek这样的工具无法处理它就不足为奇了。