直接使用汇编编写 .NET Standard 库

前言

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.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]

这是因为我们并没有声明我们引入的库 mscorlibSystem.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[蓝桥杯2016决赛]七星填数-next_permutation枚举

题目描述 如下图所示&#xff1a; 在七角星的14个节点上填入1~14 的数字&#xff0c;不重复&#xff0c;不遗漏。要求每条直线上的四个数字之和必须相等。 图中已经给出了3个数字。请计算其它位置要填充的数字&#xff0c;答案唯一。 填好后&#xff0c;请提交绿色节点的4个数…

python的argsort函数_python——argsort函数

numpy中argsort函数用法&#xff0c;有需要的朋友可以参考下。在Python中使用help帮助>>> import numpy>>> help(numpy.argsort)Help on function argsort in module numpy.core.fromnumeric:argsort(a, axis-1, kindquicksort, orderNone)Returns the indic…

第三个一千行+500行总结-数据结构C复习--知识点总结3--七到九章

第七章 (接知识点总结2) 图 图的遍历: //深度优先搜索 #define OK 1 #define True 1 #define Error -1 #define False 0 typedef enum{DG, DN, UDG. UDN}Graph; int visited[MAX]; //Graph代表图的一种存储结构比如邻接表,邻接矩阵 void TranverseGraph(Graph g){ int…

系统蓝屏的几种姿势,确定不了解下么?

前言在 蓝屏&#xff08;BSOD&#xff09;转储设置&#xff0c;看本文就够了&#xff01;这篇文章里比较详细的介绍了蓝屏转储设置。做好设置后&#xff0c;我们就可以在需要的时候使系统蓝屏了。本文介绍几种使系统蓝屏的办法&#xff0c;当然肯定还有其它办法&#xff0c;如果…

最长公共子串-dp

题目: 给定两个字符串&#xff0c;求出它们之间最长的相同子字符串的长度。 公共子串和公共子序列不同&#xff0c;公共子序列不要求连续&#xff0c;但公共子串必须是连续的。如: A “helloworld” B “loop” A和B的最长公共子序列是"loo",但最长公共子串是&quo…

the python challenge_The Python Challenge 谜题全解(持续更新)

Python Challenge(0-2)是个很有意思的网站&#xff0c;可以磨练使用python的技巧&#xff0c;每一关都有挑战&#xff0c;要编写相应的代码算出关键词&#xff0c;才可以获取下一关的url&#xff0c;还是很好玩的QAQLEVEL 0显然是计算图片中的\(2^{38}\)&#xff0c;结果为2748…

智能对话引擎:两天快速打造疫情问答机器人

01微软AI技术开源知识库疫情机器人近一个月来&#xff0c;“新冠肺炎疫情”成了所有人的热点话题&#xff0c;抗击疫情的战役在全国紧张有序地进行着。随着全国各地的企业陆续复工&#xff0c;怎样防范、保护自己和家人成了当下每个人的焦点。为了配合奋战在一线的医护人员打赢…

数码管

题目背景 小明的单片机上面的LED显示屏坏掉了&#xff0c;于是他请你来为他修显示屏。 屏幕上可以显示0~9的数字&#xff0c;其中每个数字由7个小二极管组成&#xff0c;各个数字对应的表示方式如图所示&#xff1a; 题目描述 为了排除电路故障&#xff0c;现在你需要计算&am…

fh 幅频特性曲线怎么画fl_初学者怎么练习线条?教你如何画出流畅线条的技巧...

初学者怎么练习线条&#xff1f;怎样才能画出流畅线条&#xff1f;画出流畅线条有哪些技巧&#xff1f;想必这些问题都是绘画初学者们比较伤脑筋的问题&#xff0c;那么到底怎样才能画出流畅线条呢&#xff1f;今天灵猫课堂老师就在网络上收集整理了关于初学者怎么练习线条&…

.NET Core开发实战(第12课:配置变更监听)--学习笔记

12 | 配置变更监听&#xff1a;配置热更新能力的核心这一节讲解如何使用代码来监视配置变化并做出一些动作当我们需要追踪配置发生的变化&#xff0c;可以在变化发生时执行一些特定的操作配置主要提供了一个 GetReloadToken 方法&#xff0c;这就是跟踪配置的关键方法接着使用上…

icoding复习1,2

icoding复习 1 链表 倒数查找 1. 已知一个带有表头结点的单链表, 假设链表只给出了头指针L。在不改变链表的前提下&#xff0c;请设计一个尽可能高效的算法&#xff0c; 查找链表中倒数第k个位置上的结点&#xff08;k为正整数&#xff09;。 函数原型为&#xff1a;int lnk_s…

密电破译-dp

题目背景 墨家家主召集弟子的原因是因为截获了密电并破获了重大情报&#xff0c;“公主薨&#xff0c;国王失踪&#xff0c;墨家即将面临灭顶之灾”。 题目描述 密电是由大小写字母组成字符串&#xff0c;密电之所以能破译是因为墨家掌握了破解方法&#xff0c;密钥是一个整数…

ASP.NET Core Web API基于RESTFul APIs的集合结果过滤和分页

译者荐语&#xff1a;如何在RESTFul APIs中进行集合结果分页&#xff1f;还是用客户端来拼接链接地址么&#xff1f;原文来自互联网&#xff0c;由长沙DotNET技术社区【邹溪源】翻译。如译文侵犯您的版权&#xff0c;请联系小编&#xff0c;小编将在24小时内删除。在ASP.NET Co…

icoding复习6 图

icoding复习6 1. 邻接表1 试在邻接表存储结构上实现图的基本操作 insert_vertex 和 insert_arc&#xff0c;相关定义如下&#xff1a; typedef int VertexType; typedef enum{ DG, UDG }GraphType; typedef struct ArcNode{ int adjvex; InfoPtr *info; stru…

python帮助系统函数_【Python】【基础知识】【内置函数】【help的使用方法】

原英文帮助文档&#xff1a;help([object])Invoke the built-in help system. (This function is intended for interactive use.) If no argument is given, the interactive help system starts on the interpreter console. If the argument is a string, then the string i…

统计二进制数-dp

题目描述 输入一个正整数m,请输出从0到m中每一个数字二进制数中含有1的个数的总和,由于数值较大结果需要模100000. 输入格式 一个m 输出格式 二进制数中含有1的个数的总和s 输入输出样例 输入 2 输出 2 输入 5 输出 7 说明/提示 样例说明 20%的数据 m<500 50%的数据 m<…

.net 微服务实践

l 前言本文记录了我的一次.net core 微服务架构实践经验&#xff0c;以及所用到的技术l 优点每个服务聚焦于一块业务&#xff0c;无论在开发阶段或是部署阶段都是独立的&#xff0c;更适合被各个小团队开发维护&#xff0c;团队对服务的整个生命周期负责&#xff0c;工作在独…

icoding复习3

icoding复习3 1. 不调用库函数&#xff0c;自己实现字符串的比较操作&#xff1a;该操作当比较的两个字符是都是字母&#xff0c;且两个字符互为大小写 &#xff08;如a和A、e和E&#xff09;时认为两个字符相同&#xff0c;否则不同&#xff0c;其比较结果按这两个字符的原值…

redis过期监听性能_基于Redis的延迟处理

延迟处理是一个非常常用的一个功能;例如, 下单成功后,在30分钟内没有支付,自动取消订单;延迟队列便是延迟处理中最常见的实现方式;先一起看下JDK中延迟队列是如何实现的.JUC的DelayQueue在JDK中, 提供了一套延迟队列的实现, 是JUC包中DelayQueue类.在使用时只需要让处理的元素对…

洛谷 P2040 打开所有的灯-dfs

题目背景 pmshz在玩一个益(ruo)智(zhi)的小游戏&#xff0c;目的是打开九盏灯所有的灯&#xff0c;这样的游戏难倒了pmshz。。。 题目描述 这个灯很奇(fan)怪(ren)&#xff0c;点一下就会将这个灯和其周围四盏灯的开关状态全部改变。现在你的任务就是就是告诉pmshz要全部打开这…