我正在尝试了解有关 .NET MSIL 的更多信息,因此我想编写我的自定义 IL 程序集。我被下面的例子困住了。
首先,我在 OSX 上,使用 Mono“Mono JIT 编译器版本 4.8.1(mono-4.8.0-branch/22a39d7 Fri Apr 7 12:00:08 EDT 2017)”,我的 il 程序集看起来像。完整文件:
.assembly extern mscorlib
{
.ver 4:0:0:0
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
}
.assembly 'Application'
{
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module sample.exe // GUID = {B239DF75-1D4B-4A83-B8B6-99BDEFD7B8A6}
.class public auto ansi MainType
extends [mscorlib]System.Object
{
// method line 1
.method public specialname rtspecialname
instance default void '.ctor' () cil managed
{
// Method begins at RVA 0x20ec
// Code size 7 (0x7)
.maxstack 8
ldarg.0
call instance void object::'.ctor'()
ret
} // end of method MainType::.ctor
// method line 2
.method public static hidebysig
default void Main (string[] A_0) cil managed
{
// Method begins at RVA 0x20f4
.entrypoint
// Code size 87 (0x57)
.maxstack 6
.locals init (
int32[] V_0,
int32 V_1)
// Allocating array of size 200
ldc.i4 200
newarr int32[]
stloc.0
// Pushing the numbers I want to output
ldc.i4 100
ldc.i4 101
ldc.i4 102
// 3 is the total number of previous pushed numbers
// it functions in this programm as a counter which
// will be decremented
ldc.i4 3
stloc.1 // storing the 3 in the temp variable V_1
ldloc.0 // push the array reference onto stack
ldc.i4 0 // push the index onto the stack
ldloc.1 // loading the 3 from the temp int32 var (V_1), push it onto the stack
stelem.i4 // save the value into the array at index 0 (defined above aka pushed on the stack)
// when it enters first, on the top of the stack should
// be the value 102 which we want to print out:
loop: call void class [mscorlib]System.Console::WriteLine(int32) // the call should consume 102 from the stack and print it out
// loading the counter from array
ldloc.0
ldc.i4 0
ldelem.i4
ldc.i4 1 // pushing 1
sub // subtract the counter by 1
// Storing the result which is on the stack
// in the array index 0 (our counter):
stloc.1
ldloc.0
ldc.i4 0
ldloc.1
stelem.i4
// Loading the counter value again onto stack to perform
// the check for branching:
ldloc.0
ldc.i4 0
ldelem.i4
ldc.i4 0 // pushing 0 to see if the counter is 0
ceq
brfalse loop // if its not yet 0 then we jump to the loop label; which means it should on the next call to WriteLine consume the next value which is on the stack, which should be 101
ret
} // end of method MainType::Main
} // end of class MainType
有趣或“有问题”的部分在这里:
.method public static hidebysig
default void Main (string[] A_0) cil managed
{
// Method begins at RVA 0x20f4
.entrypoint
// Code size 87 (0x57)
.maxstack 6
.locals init (
int32[] V_0,
int32 V_1)
// Allocating array of size 200
ldc.i4 200
newarr int32[]
stloc.0
// Pushing the numbers I want to output
ldc.i4 100
ldc.i4 101
ldc.i4 102
// 3 is the total number of previous pushed numbers
// it functions in this programm as a counter which
// will be decremented
ldc.i4 3
stloc.1 // storing the 3 in the temp variable V_1
ldloc.0 // push the array reference onto stack
ldc.i4 0 // push the index onto the stack
ldloc.1 // loading the 3 from the temp int32 var (V_1), push it onto the stack
stelem.i4 // save the value into the array at index 0 (defined above aka pushed on the stack)
// when it enters first, on the top of the stack should
// be the value 102 which we want to print out:
loop: call void class [mscorlib]System.Console::WriteLine(int32) // the call should consume 102 from the stack and print it out
// loading the counter from array
ldloc.0
ldc.i4 0
ldelem.i4
ldc.i4 1 // pushing 1
sub // subtract the counter by 1
// Storing the result which is on the stack
// in the array index 0 (our counter):
stloc.1
ldloc.0
ldc.i4 0
ldloc.1
stelem.i4
// Loading the counter value again onto stack to perform
// the check for branching:
ldloc.0
ldc.i4 0
ldelem.i4
ldc.i4 0 // pushing 0 to see if the counter is 0
ceq
brfalse loop // if its not yet 0 then we jump to the loop label; which means it should on the next call to WriteLine consume the next value which is on the stack, which should be 101
ret
} // end of method MainType::Main
我得到的错误如下:
Unhandled Exception:
System.InvalidProgramException: Invalid IL code in MainType:Main (string[]): IL_0056: ret
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidProgramException: Invalid IL code in MainType:Main (string[]): IL_0056: ret
并且“IL_0056:ret”指向该行(当我反汇编生成的可执行文件时):
IL_0044: ldc.i4 0
IL_0049: ldelem.i4
IL_004a: ldc.i4 0
IL_004f: ceq
IL_0051: brfalse IL_0028
IL_0056: ret //// <<<< HERE
} // end of method MainType::Main
一些重要的注意事项: * 当存储和分配给数组时,我似乎总是做一些不必要的“舞蹈”。我这样做是因为最后我想创建一个堆栈机器,它只适用于堆栈中的值。这也是代码看起来像这样的原因。在写它之前,我想看看我如何在 MSIL 中做到这一点。
如果有人能告诉我这里出了什么问题,我将不胜感激。非常感谢。
这是我在这里的第一个问题,我希望我已经提供了所有必要的信息以帮助我:-)
PS:我也尝试将 MonoDevelop 与“调试应用程序”一起使用,但我无法真正从中获得任何有意义的数据,gdb 也没有为我工作。
PPS:我在想我的堆栈上仍然有值,但是当我在末尾添加一个弹出(或更多)时,它只是告诉我“弹出”无效。
编辑:
我试图打印某种输出以查看出了什么问题,我发现在“循环”标签之间它没有正确地从堆栈中弹出值。我还不知道为什么,但这里有一个示例输出,它清楚地显示了它:
102
LOOP START
101
---
101
---
101
---
exit now
100
即使我进一步修改 il 代码,它也会打印更多值:
LOOP START
102
101
100
---
102
101
100
---
102
101
100
---
exit now
它不会从堆栈中弹出。由于某些奇怪的原因,“调用 WriteLine”不会弹出值,或者看起来它确实备份和恢复堆栈,但这只是我的假设。这与以下内容有什么关系:.NET CIL 操作评估堆栈 (“向后分支约束”,链接:http: //jilc.sourceforge.net/ecma_p3_cil.shtml)