直接使用汇编编写 .NET Standard 库
前言
Common Language Runtime(CLR)是一个很强大的运行时,它接收 Common Intermediate Language(CIL) 的输入并最终产生机器代码并执行。CIL 在 CLR 上相当于 ASM 汇编代码的存在。
CLR 之上的语言 C#、F#、VB.NET 等语言的类型系统固然设计得不错,但是有的时候我们需要一些操作绕过类型系统的检查,或者有的时候语言本身并不能满足我们的需求。
需要使用 CIL 的常见场景:
我们需要绕过类型系统,在类型系统上面 “开洞”。
我们需要优化程序的性能,直接使用 CIL 编程可以如同使用汇编一样完全的控制程序的逻辑,对程序进行人肉优化。
直接利用 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.json
和 nuget.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.Class1extends [System]System.Object
{.method public hidebysig static int32 Hello(int32) cil managed{.maxstack 4ldstr "Hello World!"call void [System.Console]System.Console::WriteLine(string)ldarg.0ret}
}
以上代码中,.assembly
标识了程序集名称,.module
标识了模块名称,一般来说这两个名字和项目名称保持一致。
然后我们创建了一个 class
Class1
,位于 MyILProject
这个 namespace
下,该 class
为 public 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 .) cil managed
{.maxstack 8ldarg.0call instance void [System.Private.CoreLib]System.Object::.ctor()nopret
}
然后就可以成功调用了!
添加引用
你会发现一个问题,上述代码虽然能正常运行,但是编译的时候却存在警告:
CopyClass.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]
这是因为我们并没有声明我们引入的库 mscorlib
,System.Console
和 System.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
直接使用汇编编写 .NET Standard 库相关推荐
- 全国计算机等级考试office无纸化考试,全国计算机等级考试真题汇编与专用题库 二级MS Office高级应用 2016年无纸化考试专用...
<全国计算机等级考试真题汇编与专用题库--二级MS Office高级应用>是依据教育部考试中心颁布的<全国计算机等级考试大纲>及无纸化真考题库编写而成的.<全国计算机等级 ...
- 全国计算机等级考试无纸化专用题库二级c语言答案,全国计算机等级考试真题汇编与专用题库二级C语言2017年无纸化考试专用...
内容提要 < 2017年 全国计算机等级考试真题汇编与专用题库 二级C语言>依据教育部考试中心新颁布的<全国计算机等级考试大纲>及新无纸化真考题库编写而成.本书内容充分考虑考生 ...
- (2356)忙里偷闲-封装汇编编写的水波特效供C++调用
忙里偷闲-封装汇编编写的水波特效供C++调用 ---汇编语言与C++语言联编 考研复习呀!时间好紧张!很久没碰编程了,心痒难搔!呵呵 业余时间在看罗云彬的windows 32位汇编语言程序设计,苦于没 ...
- c调用python第三方库_用 Python ctypes 来调用 C/C++ 编写的第三方库
看到一篇简洁的文章--如何用Python ctypes调用C++(ctypes调用C没这么多麻烦事),不敢独享... 如果需要用 Python 调用 C/C++ 编写的第三方库(这些第三方库很可能就是 ...
- 从零开始编写深度学习库(四)Eigen::Tensor学习使用及代码重构
从零开始编写深度学习库(四)Eigen::Tensor学习使用及代码重构 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce qq:1393852684 一. ...
- 从零开始编写深度学习库(三)ActivationLayer网络层CPU实现
从零开始编写深度学习库(三)ActivationLayer网络层CPU实现 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce qq:1393852684 一 ...
- 从零开始编写深度学习库(二)FullyconnecteLayer CPU编写
从零开始编写深度学习库(二)FullyconnecteLayer CPU编写 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce qq:1393852684 ...
- 从零开始编写深度学习库(一)SoftmaxWithLoss CPU编写
从零开始编写深度学习库(一)SoftmaxWithLoss CPU编写 博客:http://blog.csdn.net/hjimce 微博:黄锦池-hjimce qq:1393852684 一.C ...
- 一步一步使用标c编写跨平台图像处理库_让一个图像变成反向图像
接着上一章 一步一步使用标c编写跨平台图像处理库,本章将介绍如何让一个图像变成反向图,其原理非常简单,只需要让每个像素点上的RGB三色取反即可!变成负数! 其公式为: s = -r-g-b 转换方式: ...
最新文章
- 前端一HTML:四: html标签
- linux命令之修改系统允许进程打开文件描述符限制-ulimit
- 面试被问到“零拷贝”!你真的理解吗?
- JVM学习笔记(一):Java内存区域
- 汇编语言-004(LABEL 、间接寻址、变址操作数、指针使用、TypeDef、LOOP、DWORD变量交换高位低位字)
- 文本数据增强+NER
- 补习系列(6)- springboot 整合 shiro 一指禅
- Git应用之eclipse解决冲突代码
- rbac权限管理5张表_thinkphp框架下基于rbac的后台程序
- IRQL(转载 自 windows内核情景分析)
- jq数组清空_JQuery 数组获取和删除元素
- 怎么在wps里做计算机,解决方案:如何在wps中制作电子小报
- 项目1-员工(绩效)信息管理系统
- 软件测试工程师岗位职责、岗位要求
- win10专业版本激活
- matlab solve和subs,【MATLAB】matlab中的subs()函数和solve()函数用法
- 闲谈寻址-DNS的多级缓存系统
- 期末题库是Excel版的,如何转化成Word版
- 2021-9.15基于目标速度的汽车 ACC系统油门控制策略研究-童宝锋
- ZStack云平台云主机管理
热门文章
- 如何查看自己的ubutu系统是32位的还是64位的
- 在程序员的道路上,义无反顾的努力,有思想的人,很多,好的想法,需要学习。(以此共勉)...
- c语言编写程序计算行列式值,新手作品:行列式计算C语言版
- Teams数据统计 - 聊天消息
- pcu tps_Mac版Microsoft Office 2011重新定义您的TPS报告体验
- 找到特定ip地址 修改ip_您如何找到网站的IP地址?
- 亚马逊的vps多少钱一个月_如何查看您在亚马逊上花了多少钱
- laravel graphql php,结合 Laravel 初步学习 GraphQL
- JavaScript 使用random()生成随机数
- python之路day10-命名空间和作用域、函数嵌套,作用域链、闭包