在CLR上学习IL的方法

24

由于我更多地看到这些IL代码,我希望学习如何正确解释它们。

我找不到像C#编译器或其他任何文档,所以我认为在学习这些常见的代码之后,我就可以照顾好其余的部分:

以下是一些示例IL代码,包含我需要了解的内容:

示例1:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       15 (0xf)
  .maxstack  1
  .locals init ([0] class EnumReflection.DerivedClass derivedClass)
  IL_0000:  nop
  IL_0001:  newobj     instance void EnumReflection.DerivedClass::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance void EnumReflection.DerivedClass::WriteOutput()
  IL_000d:  nop
  IL_000e:  ret
} // end of method Program::Main

范例2:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       38 (0x26)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldstr      "Hello"
  IL_0006:  stfld      string EnumReflection.DerivedClass::hello
  IL_000b:  ldarg.0
  IL_000c:  ldstr      "World"
  IL_0011:  stfld      string EnumReflection.DerivedClass::world
  IL_0016:  ldarg.0
  IL_0017:  ldc.i4.s   123
  IL_0019:  stfld      int32 EnumReflection.DerivedClass::age
  IL_001e:  ldarg.0
  IL_001f:  call       instance void EnumReflection.BaseClass::.ctor()
  IL_0024:  nop
  IL_0025:  ret
} // end of method DerivedClass::.ctor

我知道这些代码的作用,因为是我写的 :-) 但是我想更多地了解相应的IL代码。

这些示例包含诸如此类的IL代码,您能否解释一下带问号的命令?这些命令代表什么意思?这样我们就可以更容易地记住它们了。

  • nop(调试用-无操作)
  • newobj(似乎是在堆上创建新对象)
  • stloc.0 ?
  • ldloc.0 ?
  • ret ?
  • ldarg.0 ?
  • ldstr ?
  • stfld ?
  • ldc.i4.s ?
  • .ctor - 构造函数

理解IL很重要,因为它揭示了特定编译器如何生成代码并在特定情况下执行的方式。

但是,我找不到一个很好的文档,其中包含有关IL的示例。CLR与C#3.0是一本不错的书,但最终它不是一本IL书,因此它并没有解释关于IL的所有内容。

编辑:

我已经找到了规范,它们说明了以下内容:感谢@usr。

  • nop(调试用-无操作)
  • newobj - 创建新对象
  • stloc.0 - 从堆栈弹出值到本地变量
  • ldloc.0 ? - 将本地变量加载到堆栈上
  • ret - 从方法返回
  • ldarg.0 - 加载参数0到堆栈上。
  • ldstr - 加载文字字符串
  • stfld - 存储在对象的字段中
  • ldc.i4.s - 将数字作为int32推送到堆栈上,短格式。
  • .ctor - 构造函数

2
下载ILSpy并切换到显示反编译的IL代码。您可以单击所有操作码,ILSpy将引导您进入Microsoft文档。 - mslot
+1 @mslot,感谢您在ILSpy中提到的有关IL代码的评论。我使用过ILSpy,但没有使用过IL部分。单击操作码并阅读相关文档非常有帮助。 - yantaq
4个回答

13

10

微软标准化了CLR并发布了这些标准。第三部分包含大量关于IL/CIL的信息,非常适合学习,是一份优秀的文档。

您还可以通过示例来学习IL。在C#中编译几个简单的方法,并查看反射器中的IL(它有一个IL模式)。


这就是我所做的,但我也很想知道这些命令代表什么。 - Tarik
是的,我建议你看一下规范。第三部分分区实际上看起来相当不错且易于理解。 - usr
实际上我在这个PDF文件的规格说明中找到了我要寻找的内容:http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf - Tarik
ECMA-335的链接是:https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf - SUN Jiangong

9
  • nop - 无操作
  • newobj - 创建一个对象并调用其构造函数。
  • stloc.0 - 从堆栈中弹出一个值,并将其存储在第一个本地变量中
  • ldloc.0 - 将第一个本地变量推送到堆栈上
  • ret - 返回
  • ldarg.0 - 将第一个参数(在实例方法中为this)推送到堆栈上
  • ldstr - 将字符串推送到堆栈上
  • stfld - 使用堆栈上的数据设置字段。
  • ldc.i4.s - 将指定的数字作为int推送。
  • .ctor - 构造函数

我建议您找到一个好的文档来源来了解这些操作码(尽管维基百科可能是最好的 :( )。System.Reflection.Emit的文档对操作码有相当详细的说明。

最重要的是,编写小程序并检查IL输出。这是学习的最佳方式。


8
如果您想要每个操作码的简短概述,那么检查 System.Reflection.Emit 命名空间可能是个不错的选择。
例如,有一个OpCodes类,其中包含每个操作码的静态字段。其中每个操作码都会进一步详细描述其堆栈行为。例如,Ldarg_0

ldarg.0 指令将索引为 0 的参数推送到计算堆栈上。可以使用 ldarg.0 指令从传入的参数中复制值类型或基元类型的值,将其加载到堆栈上。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接