MSIL.docx
- 文档编号:30221225
- 上传时间:2023-08-07
- 格式:DOCX
- 页数:44
- 大小:46.06KB
MSIL.docx
《MSIL.docx》由会员分享,可在线阅读,更多相关《MSIL.docx(44页珍藏版)》请在冰豆网上搜索。
MSIL
MicrosoftIntermediateLanguage(MSIL)
一、HelloWord!
Microsoft.NETFramework和C#ProgrammingLanguage自诞生日起,就一直快速进化着。
越来越庞大的类库,越来越多的封装,还有那越来越简单的语法糖,所有这些让我们离"本质"越来越远。
尽管用这些优雅的语法并不妨碍我们开发出好而实用的产品,但作为一个资深的程序员,我们还是应该了解编译器背后的秘密。
C#并不代表.NETCLR的全部,就好比学习LINQ,如果我们不试图去分析背后的执行流程,就很难理解所谓延迟执行机制。
MSIL是一把锋利的手术刀,只有它能剖开层层"伪装",带我们深入到.NET的底层世界,去了解和结识许许多多被刻意隐藏起来的精灵。
尽管它不是真正意义上的底层汇编语言,不能分析JITComplier编译后的NativeCode,但总算能一窥托管编译器的奥秘。
学习MSIL的目的,并不是真的要用它去编码,而是为了更好地"交流"。
暂时离开那无比强大的VisualStudio.NET,让我们用记事本开始MSIL的探索之旅。
MyApp.il
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.namespaceMyApp
{
.classpublicProgramextends[mscorlib]System.Object
{
.methodstaticprivatevoidMain(string[]args)
{
.entrypoint
ldstr"Hello,World!
"
callvoid[mscorlib]System.Console:
:
WriteLine(string)
nop
callstring[mscorlib]System.Console:
:
ReadLine()
pop
ret
}
}
}
打开VisualStudioCommandPrompt,使用ILASM开始编译。
E:
\Develop\MSIL>ilasmMyApp.il
Microsoft(R).NETFrameworkILAssembler.Version2.0.50727.1378
Copyright(c)MicrosoftCorporation.Allrightsreserved.
Assembling'MyApp.il'toEXE-->'MyApp.exe'
SourcefileisUTF-8
AssembledmethodMyApp.Program:
:
Main
CreatingPEfile
Emittingclasses:
Class1:
MyApp.Program
Emittingfieldsandmethods:
Global
Class1Methods:
1;
Emittingeventsandproperties:
Global
Class1
WritingPEfile
Operationcompletedsuccessfully
执行MyApp.exe,就可看到熟悉的"Hello,World!
"也就意味着我们完成了第一个.NET"汇编"程序。
二、CodeStructure
先了解一下MSIL程序代码最简单的结构组成,以便于我们开始后面的细节研究。
还是以上节的"Hello,World!
"为例。
1、Header
.assemblyexternmscorlib{auto}
定义一个程序集引用信息。
mscorlib.dll包含了.NET的基本类型定义,几乎所有的托管引用程序都会用到它。
当然我们还可以写得更具体一点。
例如:
.assemblyexternmscorlib
{
.ver2:
0:
0:
0
.publickeytoken=(B77A5C561934E089)
}
.assemblyMyApp{}
定义程序集。
当然还可以包含更完整的版本信息。
.assemblyLearn.CUI
{
.ver1:
0:
0:
0
.custominstancevoid[mscorlib]System.Reflection.AssemblyTitleAttribute:
:
.ctor(string)={string('MyApp')}
.custominstancevoid[mscorlib]System.Reflection.AssemblyDescriptionAttribute:
:
.ctor(string)={string('')}
}
.moduleMyApp.exe
定义托管模块。
KingNa注:
这个就相当于C#中的Using或VB中的Imports了,只是IL还要指定程序集名称以及Module名,但我在实验中发现,Module可以不予指定,也就是说除了Module不是必须的以外,其它两个都是必须要指定的。
2、Code
.namespaceMyApp
定义命名空间。
.classpublicProgramextends[mscorlib]System.Object
定义一个继承自System.Object的类,注意语法。
我们还可以为其添加更多的关键词,比如:
.classpublicautoansiextends[mscorlib]System.Object
.methodstaticprivatevoidMain(string[]args)
定义一个私有静态方法。
这个方法是程序的入口点,在C#中我们必须将其命名为Main,但CLR并没有这个要求,真正起作用的是".entrypoint"。
所以我们可以将其改成任何有效名称。
.methodstaticprivatevoidTest(string[]args)
(不要着急研究关键词和指令的具体意义~~~~~
)
三、Module
在C#中,除非我们用Reflection,否则我们不能直接操作
但在实际开发中,我们可以利用
(1)为
(2)定义不依附于任何Class的全局字段或方法(不建议在实际工程中使用)。
我们在第一节中的HelloWord的结尾加入如下代码:
.methodprivatehidebysigspecialnamertspecialnamestaticvoid.cctor()
{
ldstr"
callvoid[mscorlib]System.Console:
:
WriteLine(string)
ret
}
编译该代码,执行后输出。
Hello,World!
GlobalItems
我们可以在Module中定义全局的静态字段和方法。
看下面的演示。
(1)定义全局静态字段x。
(2)在
(3)定义全局静态方法Test()用来显示x。
(4)在Program.Main()中使用call指令进行调用Test()。
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.namespaceMyApp
{
.classpublicProgramextends[mscorlib]System.Object
{
.methodstaticprivatevoidMain(string[]args)
{
.entrypoint
callvoidTest()
nop
callstring[mscorlib]System.Console:
:
ReadLine()
pop
ret
}
}
}
.fieldassemblystaticint32x
.methodprivatehidebysigspecialnamertspecialnamestaticvoid.cctor()
{
ldc.i41234
stsfldint32x
ret
}
.methodpublicstaticvoidTest()
{
ldsfldint32x
callvoid[mscorlib]System.Console:
:
WriteLine(int32)
ret
}
四、Namespace
MSIL的Namespace定义要远比C#灵活得多,让我们看看MyApp.CMS.Program的不同写法。
(1).namespaceMyApp.CMS{}
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.namespaceMyApp.CMS
{
.classpublicProgramextends[mscorlib]System.Object
{
.methodstaticprivatevoidMain(string[]args)
{
.entrypoint
ret
}
}
}
(2).namespaceMyApp{.namespaceCMS{}}
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.namespaceMyApp
{
.namespaceCMS
{
.classpublicProgramextends[mscorlib]System.Object
{
.methodstaticprivatevoidMain(string[]args)
{
.entrypoint
Ret
}
}
}
}
这种嵌套方式更便于组织代码。
.namespaceMyApp
{
.classA...{}
.namespaceCMS
{ .classB...{} }
}
(3).classpublicMyApp.CMS.Program...{}
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.classpublicMyApp.CMS.Programextends[mscorlib]System.Object
{
.methodstaticprivatevoidMain(string[]args)
{
.entrypoint
Ret
}
}
此方式直接将namespace作为ClassFullName的一部分。
但在MSIL编码时,输入类型的完整名称,长长的FullName(namespace+classname)让人感觉太繁琐……此时可以考虑用.typedef定义别名减少输入。
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.typedef[mscorlib]System.ObjectasObject
.typedefmethodvoid[mscorlib]System.Console:
:
WriteLine(string)asPrint
.namespaceMyApp
{
//.classpublicProgramextends[mscorlib]System.Object
.classpublicProgramextendsObject
{
.methodstaticprivatevoidMain(string[]args)
{
.entrypoint
ldstr"Hello,World!
"
//callvoid[mscorlib]System.Console:
:
WriteLine(string)
callPrint
ret
}
}
}
我们还可以直接在.typedef中使用已定义的别名。
.typedef[mscorlib]System.ConsoleasCW
.typedefmethodvoidCW:
:
WriteLine(string)asPrint
五、Class-Flag
无所不在的Class终于出来了~~~~~
.class
Flags
1.Visiblity:
访问修饰符。
private:
相当于C#的internalclass,仅程序集内部可见。
(默认)
public:
可以被程序集外部访问。
nestedpublic:
嵌套类型可以被外部访问。
nestedprivate:
嵌套类型仅可以被enclosingclass访问。
(默认)
nestedfamily:
相当于protected,嵌套类型可以被enclosingclass及其继承类型访问。
nestedassembly:
相当于internal,嵌套类型仅可以被程序集内部成员访问。
nestedfamandassem:
相当于internal&protected,嵌套类型只能被同一程序集的enclosingclass(含继承类型)访问。
nestedfamorassem:
相当于internalprotected,嵌套类型可以被程序集内部成员及enclosingclass(含外部继承类型)访问。
2.Layout:
控制导出到非托管代码时对象内存布局。
auto:
由CLR自动选择排列方式。
(默认)
explicit:
显式控制成员位置(FieldOffsetAttribute)。
sequential:
对象成员按导出次序排列布局。
3.Semantics
interface:
定义接口类型。
abstract:
定义抽象类型。
sealed:
定义密封类型。
specialname:
表明类型名称有特殊含义。
4.Implementaion
import:
该类型由COM导入。
serializable:
该类型可以被序列化。
beforefieldinit:
由JIT决定类型初始化时机,这可能在首次调用其静态成员或实例化之前的某个任意时刻。
5.StringFormatting
ansi:
将字符串转换成ansi格式,以便和nativemethods交互。
(默认)
unicode:
托管代码缺省格式,UTF-16。
autochar:
由底层平台定义字符串格式。
6.Reserved
rtspecialname:
表明该名称由CLR保留,具有特殊含义,且必须和specialname一起使用。
7.Other
value:
值类型,缺省基类是System.ValueType。
enum:
枚举类型,缺省基类是System.Enum。
Demo
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.namespaceMyApp
{
//interface
.classinterfaceITest{}
//enum
.classenumSex{}
//struct
.classvalueMyData{}
.classpublicautoansiProgramextends[mscorlib]System.Object
{
.methodprivatestaticvoidMain(string[]args)
{
.entrypoint
Ret
}
.classnestedfamorassemClass1extends[mscorlib]System.Object
{ }
}
}
编译后代码
.namespaceMyApp
{
.classprivateinterfaceabstractautoansiITest { }
.classprivateautoansiSex
extends[mscorlib]System.Enum { }
.classprivateautoansisealedMyData
extends[mscorlib]System.ValueType { }
.classpublicautoansiProgram extends[mscorlib]System.Object
{
.methodprivatestaticvoidMain(string[]args)cilmanaged
{
.entrypoint
}
.classautoansinestedfamorassmClass1
extends[mscorlib]System.Object { }
}
}
转换成C#
namespaceMyApp
{
internalinterfaceITest { }
internalenumSex { }
internalstructMyData { }
publicclassProgram
{
privatestaticvoidMain(string[]args);
internalprotectedclassClass1 { }
}
}
关于beforefieldinit,我们通过下面的例子看看其具体作用。
.assemblyexternmscorlib{auto}
.assemblyMyApp{}
.moduleMyApp.exe
.namespaceMyApp
{
.class/*beforefieldinit*/MyClass
{
.methodpublicspecialnamestaticvoid.cctor()
{
ldstr"MyClass.cctor..."
callvoid[mscorlib]System.Console:
:
WriteLine(string)
ret
}
.methodpublicspecialnamevoid.ctor()
{
ldarg.0
callinstancevoid.base:
:
.ctor()
ret
}
.methodpublicvoidTest()
{
ldstr"test..."
callvoid[mscorlib]System.Console:
:
WriteLine(string)
ret
}
}
.classpublicautoansiProgramextends[mscorlib]System.Object
{
.methodprivatestaticvoidMain(string[]args)
{
.entrypoint
.localsinit(class[mscorlib]System.Objecto)
ldstr"--------------------------------"
callvoid[mscorlib]System.Console:
:
WriteLine(string)
nop
newobjinstancevoidMyApp.MyClass:
:
.ctor()
stloc.so
ldloc.so
callinstancevoidMyApp.MyClass:
:
Test()
ret
}
}
}
不添加beforefieldinit的输出结果
--------------------------------
MyClass.cctor...
test...
添加beforefieldinit的输出结果
MyClass.cctor...
--------------------------------
test...
第一种输出结果和我们在C#中所了解的一样,静态构造在第一次调用类型静态成员或实例化前被调用。
而一旦添加了beforefieldinit关键字,我们发现在做出任何调用之前,静态构造就被调用。
beforefieldinit的真实含义是告诉CLR能够在任何时候执行静态构造方法(.cctor),只要该方法在第一次访问该类型静态成员或进行实例化之前被执行即可。
换句话说,beforefieldinit为CLR提供了一个执行主动优化的许可。
如果没有beforefieldinit,运行库就必须在某个"精确时间"运行类型构造方法,即恰好在第一次访问该类型的静态成员或实例化之前。
很少有类会真正需要类型构造方法在一个精确的时间执行。
因此在大多数情况下,添加beforefieldinit标志就非常有意义。
缺省情况下C#
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- MSIL