前言

Common Language Runtime(CLR)是一个很强大的运行时,它接收 Common Intermediate Language(CIL) 的输入并最终产生机器代码并执行。CIL 在 CLR 上相当于 ASM 汇编代码的存在。

CLR 之上的语言 C#、F#、VB.NET 等语言的类型系统固然设计得不错,但是有的时候我们需要一些操作绕过类型系统的检查,或者有的时候语言本身并不能满足我们的需求。

需要使用 CIL 的常见场景:

  1. 我们需要绕过类型系统,在类型系统上面 “开洞”。
  2. 我们需要优化程序的性能,直接使用 CIL 编程可以如同使用汇编一样完全的控制程序的逻辑,对程序进行人肉优化。
  3. 直接利用 C#、F# 等语言编译成的 CIL 有其独特的模式,容易被反编译软件从 CIL 还原为源代码,而如果直接采用 CIL 编程则很容易避开编译器生成代码的固有模式,使得代码无需进行任何混淆即可让所有反编译器失效。

需要注意:CLR 的 JIT 部分优化依赖于 CIL 的特定模式,直接采用 CIL 进行编程而不利用 C# 等语言的编译器生成特定模式的 CIL 可能导致优化失效,如向量化、模式匹配缓存和常数时间优化等,因此在直接使用 CIL 进行编程时最好对 CLR 的 JIT 有一定了解,以规避潜在的性能问题,JIT 的源代码在 https://github.com/dotnet/runtime/tree/master/src/coreclr/src/jit。

准备工作

首先我们创建一个 .NET Standard 项目:

mkdir MyILProject
cd MyILProject
dotnet new classlib

然后创建 global.jsonnuget.config 文件用于配置 SDK:

dotnet new global
dotnet new nuget

global.json 的内容修改为如下,添加 IL SDK 来源:

{
"msbuild-sdks": {
"Microsoft.NET.Sdk.IL": "3.0.0-preview-27318-01"
}
}

然后打开 nuget.config,将内容修改如下,添加 .net core myget 源:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
</packageSources>
</configuration>

之前创建的为 C# 类库项目,但是我们此时需要的是 IL 类库项目,因此将 MyILProject.csproj 文件重命名为 MyILProject.ilproj

打开 MyILProject.ilproj 文件,引入 IL SDK,并添加一系列的属性(如:输出类型、优化选项、工具链等):

<Project Sdk="Microsoft.NET.Sdk.IL">

  <PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.1</TargetFramework>
<DebugOptimization>IMPL</DebugOptimization>
<DebugOptimization Condition="'$(Configuration)' == 'Release'">OPT</DebugOptimization>
<MicrosoftNetCoreIlasmPackageVersion>3.0.0-preview-27318-01</MicrosoftNetCoreIlasmPackageVersion>
</PropertyGroup> </Project>

至此,万事俱备

第一个文件

我们删除掉原有的 C# 代码文件 Class1.cs,创建代码文件 Class1.il,并添加以下 CIL 代码并保存:

.assembly MyILProject
{
.ver 1:0:0:0
} .module MyILProject.dll .class public auto ansi sealed MyILProject.Class1
extends [System]System.Object
{
.method public hidebysig static int32 Hello(int32) cil managed
{
.maxstack 4 ldstr "Hello World!"
call void [System.Console]System.Console::WriteLine(string) ldarg.0
ret
}
}

以上代码中,.assembly 标识了程序集名称,.module 标识了模块名称,一般来说这两个名字和项目名称保持一致。

然后我们创建了一个 class Class1,位于 MyILProject 这个 namespace 下,该 classpublic sealed 的,且继承自 System.Object

最后我们添加了一个静态方法 int Hello(int),该方法调用 System.Console.WriteLine 输出字符串 Hello world!,然后加载参数的值后返回该值。

测试代码

我们在上级目录创建一个测试项目试试:

cd ..
mkdir Test
cd Test
dotnet new console
dotnet add reference ../MyILProject

然后修改 Program.cs

using System;
using MyILProject; namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Class1.Hello(25));
}
}
}

运行

dotnet run

可以看到输出为:

Hello world!
25

与我们所期望的一致。

然后我们试一下实例化 Class1

var x = new Class1();

却发现报错:

Program.cs(10,28): error CS1729: 'Class1' does not contain a constructor that takes 0 arguments [...\Test.csproj]

这是因为,我们没有为这个类创建构造方法,那么很简单,我们只需要加一个构造方法即可,要注意构造方法特有的方法名为 .ctor

.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
.maxstack 8 ldarg.0
call instance void [System.Private.CoreLib]System.Object::.ctor()
nop
ret
}

然后就可以成功调用了!

添加引用

你会发现一个问题,上述代码虽然能正常运行,但是编译的时候却存在警告:

Class.il(9): warning : Reference to undeclared extern assembly 'mscorlib'. Attempting autodetect [...\MyILProject.ilproj]
Class.il(15): warning : Reference to undeclared extern assembly 'System.Console'. Attempting autodetect [...\MyILProject.ilproj]
Class.il(26): warning : Reference to undeclared extern assembly 'System.Private.CoreLib'. Attempting autodetect [...\MyILProject.ilproj]

这是因为我们并没有声明我们引入的库 mscorlibSystem.ConsoleSystem.Provate.CoreLib,所幸的是,因为这些是 .NET Core SDK 中自带的库因此编译器可以自动查找并补上引用,所以没有报错,否则运行的时候会抛出异常: System.IO.FileNotFoundException: Could not load file or assembly xxxxx

如果想消除这些警告,我们可以创建一个头文件引用这些库,然后在 CIL 代码文件的头部 #include 头文件,示例如下:

MyILProject 中新建 include 文件夹,创建一个 include.h:

.assembly extern System.Runtime
{
.publickeytoken = ( B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
} .assembly extern System.Console
{
.publickeytoken = ( B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
} .assembly extern System.Private.CoreLib
{
.publickeytoken = ( B0 3F 5F 7F 11 D5 0A 3A )
.ver 4:0:0:0
}

然后在 Class1.il 头部加一行 #include "include.h" 包含该文件。

最后修改 MyILProject.ilproj,将 include 文件夹加入 INCLUDE 查找目录(-INCLUDE=...):

<Project Sdk="Microsoft.NET.Sdk.IL">

  <PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.1</TargetFramework>
<DebugOptimization>IMPL</DebugOptimization>
<DebugOptimization Condition="'$(Configuration)' == 'Release'">OPT</DebugOptimization>
<MicrosoftNetCoreIlasmPackageVersion>3.0.0-preview-27318-01</MicrosoftNetCoreIlasmPackageVersion>
<IlasmFlags>$(IlasmFlags) -INCLUDE=include</IlasmFlags>
</PropertyGroup> </Project>

这次我们再次尝试编译,就不会报错了。

CLI

上面的内容只简单的使用了一些 CIL 语法,然而 CIL 也是非常强大的,包含有很多内容,具体可以参考 Common Language Infrastructure(CLI),这部分的内容包含在标准 ECMA-355 中,截至本文发布,最新的 CLI 标准版本是 2012 年发布的第六版。

ECMA-355:https://www.ecma-international.org/publications/standards/Ecma-335.htm ,欢迎各位读者前去阅读。

应用案例

.NET BCL 中提供了一个特殊的库:System.Runtime.CompilerServices.Unsafe,这个库允许你无视 C# 的类型系统进行各种类型转换等的骚操作,这是你用 C# 无论如何都不可能写出来的,官方也知道这一点,因此该库完全是直接使用 CIL 编写的,源代码可参考:https://github.com/dotnet/runtime/blob/master/src/libraries/System.Runtime.CompilerServices.Unsafe/src/System.Runtime.CompilerServices.Unsafe.il

最新文章

  1. iOS 根据银行卡号判断银行名称
  2. 视频直播点播nginx-rtmp开发手册中文版
  3. Oracle表解锁语句
  4. What’s the difference between data mining and data warehousing?
  5. GIT(分布式版本控制系统)
  6. codeforces 101C C. Vectors(数学)
  7. 封印术:shadow dom
  8. 定时组件quartz系列&lt;二&gt;quartz的原理
  9. java下实现调用oracle的存储过程和函数
  10. TCP慢启动算法
  11. paip.c++ qt 项目工程互相引用的方法
  12. NuGet管理
  13. 一个Cmake的例子
  14. Wireshark网络抓包(四)——工具
  15. 哥哥我刚学的java---------数组插入法------------
  16. 最新的极光推送服务器端代码(java服务器后台向手机端自定义推送消息)
  17. 将网页封装成苹果APP的牛逼方法,无需发布到appstore,无需越狱即可安装
  18. centos7 mysql5.7安装
  19. hdu3605 Escape 二分图多重匹配/最大流
  20. TZOJ 4871 文化之旅(floyd预处理+dfs剪枝)

热门文章

  1. 深入理解Java虚拟机-类加载连接和初始化解析
  2. Django 信号量
  3. ORM补充文件
  4. 2019杭电多校赛第九场 Rikka with Mista
  5. 【WPF学习】第十九章 控件类
  6. 关于MySQL5.6配置文件my-default.ini不生效问题
  7. mybatis入门案例自定义实现
  8. Vue使用better-scroll左右菜单联动
  9. python学习Day03
  10. 实现一个简易的RPC