楔子:
本篇是承继前面三篇文章而来,分别为:
.Net 7 的 AOT 和 CLR有什么区别?
.Net 7 的 R2R,Crossgen2是什么?
.Net 7 的AOT原理简析
通过以上三篇的基础,本篇来彻底解析下AOT这门技术的底层原理。
AOT此终,不再叙。
.Obj(win) OR .O(Linux)目标文件
AOT的第一步就是比较重要的,通过ILC AOT编译器把托管代码的动态链接库(DLL)编译成二进制的机器码。
这个编译的过程基本上如下:
1.通过Roslyn把.Net代码编译成托管DLL
2.通过ILC AOT编译器调用JIT编译器
3.通过JIT编译器把托管DLL编译成二进制机器码
4.返回ILC编译器,把二进制的机器码写入目标文件.Obj OR .O。
写入目标文件之后,所有的代码需要运行的全局变量,局部变量,函数机器码,函数头,重定位偏移,符号结构,变量段偏移,全部被目标目标文件包含。
下一步就是进行目标机器的链接。
Link(win) OR ld(Linux)
win下面可以通过link.exe来链接目标文件。而linux下面可以通过ld -o命令来链接gcc或者ilc编译的目标文件.o 。
然后得到的exe或者ELF就是AOT最终的文件形态。
过程:
以上经过提炼的过程看似非常简单,里面实则极其复杂,而且晦涩。简单的看看。不会太深入。
以下1,2以__managed__Main AOT入口函数为例,它里面调用的是C#的Main函数。3以函数System.Runtime.TypeCast.LdelemaRef的重定位为例。4看链接之后。
1.入口:
第二列00269F60是__managed__Main函数在段.managedcode$I的偏移地址,也就是函数头地址。
2.内容:
3.局部变量
以下是.data段,
上图红色框的东西到底什么呢?为何也放入目标文件?
跟踪下发现是函数LdelemaRef重定位处的注释。以下是部分代码
蓝色框为重定位,所以值是0,红色框就是上面需要找的内容。
4.链接之后的可执行文件。
无论是win还是linux,链接之后的可执行文件,体积变小了,省略了函数名。可以对照下。
前:
后:
PE和ELF的加载器,LD和Link的中间连接过程。JIT,ILC,CL一个不落的全用上了。分支则在链接用了开源和不开源的链接器。
结果
微软为了支持社区提出的AOT技术,这个过程搞得复杂多了。但是用起来似乎以前方便了些,只不过封装太多了。