直接使用汇编编写 .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个数…

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

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

最长公共子串-dp

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

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

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;这就是跟踪配置的关键方法接着使用上…

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

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

.net 微服务实践

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

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

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

【译】来看看WebWindow,一个跨平台的.NET Core webview 库

本文翻译自 ASP.NET 项目组的 Steve Sanderson 的博客&#xff0c;发表于 2019 年 11 月 18 日。Steve Sanderson 是 Blazor 最早的创造者。它类似于 Electron&#xff0c;但没有捆绑 Node.js 和 Chromium&#xff0c;也没有大部分 API。我的上一篇文章研究了如何用 web 渲染的…

sql if 和insert_拼多多面试:Mybatis是如何实现SQL语句复用功能的?

在工作中&#xff0c;往往有这样的需求&#xff0c;对于同一个sql条件查询&#xff0c;首先需要统计记录条数&#xff0c;用以计算pageCount&#xff0c;然后再对结果进行分页查询显示&#xff0c;看下面一个例子。<sql id"studentProperties"><!--sql片段-…

代码演示C#各版本新功能

代码演示C#各版本新功能C#各版本新功能其实都能在官网搜到&#xff0c;但很少有人整理在一起&#xff0c;并通过非常简短的代码将每个新特性演示出来。代码演示C#各版本新功能C# 2.0版 - 2005泛型分部类型匿名方法可以为null的值类型迭代器协变和逆变C# 3.0版 - 2007自动实现的…

《C++ Primer》1.52节练习

练习1.23 #include <iostream> #include "Sales_item.h"using namespace std;int main() {Sales_item trans1, trans2;cout << "请输入若干销售记录:" << endl;if (cin >> trans1) {int num 1;while (cin >> trans2)if (t…

ASP.NET Core 反向代理部署知多少

引言最近在折腾统一认证中心&#xff0c;看到开源项目[IdentityServer4.Admin&#xff1a;https://github.com/skoruba/IdentityServer4.Admin]集成了IdentityServer4和管理面板&#xff0c;就直接拿过来用了。在尝试Nginx部署时遇到了诸如虚拟目录映射&#xff0c;请求头超长、…

函数传参string_JavaScript 高阶函数入门浅析

原文&#xff1a;https://www.freecodecamp.org/news/a-quick-intro-to-higher-order-functions-in-javascript-1a014f89c6b/译者&#xff1a;jingruzhang校对者&#xff1a;acusp高阶函数高阶函数可以接收函数作为参数&#xff0c;同时也可以返回一个新的函数。高阶函数之所以…

.NET Core开发实战(第13课:配置绑定:使用强类型对象承载配置数据)--学习笔记...

13 | 配置绑定&#xff1a;使用强类型对象承载配置数据要点&#xff1a;1、支持将配置值绑定到已有对象2、支持将配置值绑定到私有属性上继续使用上一节代码首先定义一个类作为接收配置的实例class Config {public string Key1 { get; set; }public bool Key5 { get; set; }pub…

工业互联网白皮书_发布|《工业互联网平台安全白皮书(2020)》发布

12月4日&#xff0c;2020年中国工业信息安全大会暨全国工控安全深度行(京津冀站)在北京国际会议中心举行。大会由国家工业信息安全发展研究中心、工业信息安全产业发展联盟主办&#xff0c;以“贯彻总体国家安全观&#xff0c;把牢工控安全基准线”为主题。会上&#xff0c;国家…

ASP.NET Core Razor 视图预编译、动态编译

0x01 前言ASP.NET Core在默认发布情况下&#xff0c;会启动预编译将试图编译成xx.Views.dll,也许在视图中打算修改一处很细小的地方我们需要再重新编译视图进行发布。下面我将从 ASP.NET Core 3 之前版本到 ASP.NET Core 3X 之后版本的一个配置列下下方供大家参考。0x02 预编译…