.Net8的AOT是如何被C++操控运行的

开发 前端
C++和AOT无论是Roslyn,或者ILC或者引导程序BootStrap都是通过C++来启动运行的。1.Roslyn的运行实质上是运行在虚拟机CLR上面的2.ILC同上3.BootStrap它本身就是cpp项目而llvm本身就是一套超级底层的C/C++项目,可以看到在一整套的AOT编译运行流程中,C++始终操控C#的运行。

前言

.Net目前有两条线,一条是正宗的.Net虚拟机CLR调用JIT的即时编译,另外一条就是通过ILC编译成本地的机器码也即是AOT。上一篇【C++是如何运行C#/.Net的?】说的是前者,本篇来看下后者。

概括

前情提要:本篇以最新的.Net8 PreView5为蓝本,进行的描述。

1.不同简要比较

AOT相当于一个全新的缩减.Net版本,它和即时编译器也即JIT机器码照样不同,这里举一个例子,比如以下代码:

static void Main(string[] args)
{
    Program pm = new Program();
}

简单的一个对象实例化,即时编译里面:

call        JIT_TrialAllocSFastMP_InlineGetThread (07FFC4C650650h)  
 mov         qword ptr [rbp+20h],rax  
 mov         rcx,qword ptr [rbp+20h]  
 call        Program..ctor() (07FFBECC2C078h)

可以看到它先分配内存,然后调用默认的构造函数.ctor

那么AOT呢?

00007FF72AD459E8 48 8D 0D E1 55 17 00 lea         rcx,[repro_Program::`vftable' (07FF72AEBAFD0h)]  
00007FF72AD459EF E8 9C 0A C9 FF       call        RhpNewFast (07FF72A9D6490h)

它这里很明显用了虚函数表指针作为参数,调用了RhpNewFast。完全是不一样的。

2.整体过程AOT的编译如下:C#源码-》Roslyn(DLL)->ILC(Obj)->Link(Exe)写好了C#源代码之后,Roslyn会接管C#源代码把它编译成中间语言MSIL,存放在托管的动态链接库即DLL里面。ILC会接管托管的DLL把它生成目标文件.Obj,然后用NativeAot的引导程序也即Bootstrap引导Link.exe工具链接.Obj目标文件生成可执行文件。

3.细节生成的目标文件也即Obj依旧是通过开源界三大编译器之一的LLVM来生成的.在Windows/Linux/MaoOS上的动态链接库分别是:

objwriter.dll(pe)/libobjwriter.so(elf)/libobjwriter.dylib(Mach-O)

他们分别封装了各个平台的llvm后端代码生成来完成了Obj目标文件的生成。

4.C++和AOT无论是Roslyn,或者ILC或者引导程序BootStrap都是通过C++来启动运行的。1.Roslyn的运行实质上是运行在虚拟机CLR上面的2.ILC同上3.BootStrap它本身就是cpp项目而llvm本身就是一套超级底层的C/C++项目,可以看到在一整套的AOT编译运行流程中,C++始终操控C#的运行。

5.核心代码

为了更为透彻的了解到ILC调用Objwriter.dll动态链接库操控llvm生成obj目标文件。在WinX64平台上,这里演示一段简单的代码,步骤如下:

一.首先在nuget上面下载一个ILC编译器,也即是:

runtime.win-x64.Microsoft.DotNet.ILCompiler

二.找到nuget目录,里面有个objwriter.dll一般的在如下路径:

C:\Users\Administrator\.nuget\packages\runtime.win-x64.microsoft.dotnet.ilcompiler\7.0.8\tools

三.新建一个C#控制台项目名字Obj,把上面的路径找到的objwriter.dll放入到

Obj项目bin/Debug/net7.0目录下面。

四.Obj项目bin/Debug/net7.0目录下面新建一个Demo.obj目标文件

五.Program.cs里面填写如下代码:

internal class Program
{
    [DllImport("objwriter.dll")]
    private static extern IntPtr InitObjWriter([MarshalAs(UnmanagedType.LPUTF8Str)] string objectFilePath, string triple = null);


    [DllImport("objwriter.dll")]
    private static extern void FinishObjWriter(IntPtr objWriter);


    [DllImport("objwriter.dll")]
    private static extern void EmitIntValue(IntPtr objWriter, ulong value, int size);


    private IntPtr _nativeObjectWriter = IntPtr.Zero;


    static void Main(string[] args)
    {
            IntPtr objectWriter = InitObjWriter("Demo.obj", "x86_64-pc-win32-windows");
            EmitIntValue(objectWriter, 0x10, 4);
            FinishObjWriter(objectWriter);
    }
}

运行这段代码之后,打开Demo.obj可以看到文件里面写入了一段内容,这就是ILC编译器往obj目标文件里面写入被JIT编译后的机器码的核心部分代码的原型。这里因为封装了llvm的细节,又因托管省略了大部分,看起来比较简洁。综合起来实际上的代码高达百万行之巨,暂不赘述此部分。

以上代码GitHub下载地址:

https://github.com/tangyanzhi/jianghupt/releases/download/llvm/Obj.rar

责任编辑:武晓燕 来源: 江湖评谈
相关推荐

2023-07-11 00:17:58

.Net8AOTC

2023-09-11 08:40:34

AOTVMP代码

2023-11-17 13:42:06

.NET8C#工具

2023-07-14 08:29:55

AOT.Net程序

2023-11-17 08:52:32

.NET8C#

2023-11-28 12:01:06

C++STL

2023-10-07 10:16:18

.Net8GC内存

2023-12-15 17:09:28

.NET8Primitives性能

2019-08-28 14:21:39

C++C接口代码

2020-07-31 18:33:56

C++编程语言

2009-10-10 14:07:56

2023-11-06 10:03:01

.Net8类型转换

2024-04-01 08:23:20

代码Javajavascript

2014-09-19 10:46:36

LuaCC++

2010-01-26 15:51:06

C++变量

2023-12-27 07:33:54

2014-04-17 10:37:43

C++.NET Native

2020-09-03 07:17:28

Rus微软C++

2010-01-14 15:29:44

C++编译器

2023-12-06 08:45:01

WasmJavaScript
点赞
收藏

51CTO技术栈公众号