楔子:
本篇是承繼前面三篇文章而來,分別為:
.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技術,這個過程搞得復雜多了。但是用起來似乎以前方便了些,只不過封裝太多了。