http://www.cnblogs.com/li0803/archive/2011/02/03/1948924.html
惶恐中放上小弟的第一篇分析作品,水平有限,文筆不濟,希望各位見諒並提出意見和建議
一、啟動之前
VS的便捷同時也掩蓋了一個作業系統從原始碼轉換到二進位檔案的中間流程,所以首先先從原始碼編譯下手,先弄清楚VS是怎麼把C#原始碼編譯成可引導啟動的二進位代碼。
本人使用的原始碼包為cosmos-72205.zip
對於VS需要額外安裝:VS2010 SDK
二、MSBuild
編譯過LINUX的朋友應該都熟悉Make檔案吧,在這裡面可以清楚的看到使用編譯器把C原始碼編譯成二進位原始碼的流程,中間幹了什麼都可以看得清清楚楚。但使用VS的朋友一般都是程式寫好之後直接F5就運行了,後面做了什麼我們根本不知道。對於COSMOS的原始碼,VS只是幫我們編譯成了IL的代碼,這種代碼無法在沒有CLR環境中運行,所以VS還需要把IL代碼編譯為針對CPU的本地代碼。這中間的流程都由MSBuild這個東西控制。對於MSBuild的介紹可以直接查看MSDN文檔。總的來說這個東西的地位就相當於Make檔案的地位吧,指導VS如何產生可以引導的二進位檔案。
三、編譯Cosmos
首先先運行解壓出來的檔案夾裡的Build/VSIP/install.bat檔案,這個好像是用安裝Cosmos的編譯環境的。安裝完成,直接雙擊Source/cosmos.sln檔案(在這之前需要先安裝VS2010 SDK)即可瀏覽全部原始碼檔案(激動~~~~選擇COSMOS的主要原因)。開啟之後得先改一下啟動項目,在次修改成Breakpoints這個項目 。然後就可以直接F5啟動了。COSMOS編譯完成後會直接使用內建的Bulid目錄下的虛擬機器來載入編譯產生的ISO檔案,並運行,運行介面如下:
四、Cosmos的編譯過程
我們可以看到,工程裡面設為啟動項的不是常規的C#項目,而已Cosmos項目,該項目在運行Build/VSIP/install.bat時安裝到VS2010中,編譯的時候會調用該項目自己的MSBuild檔案來控制其編譯過程,可以在C:\Program Files\MSBuild\Cosmos該目錄下找到一個Cosmos.target檔案,用寫字板開啟,找到如下內容:
<UsingTask TaskName="Cosmos.Build.MSBuild.IL2CPU" AssemblyFile="$(VSIPDir)\Cosmos.Build.MSBuild.dll" />
<UsingTask TaskName="Cosmos.Build.MSBuild.NAsm" AssemblyFile="$(VSIPDir)\Cosmos.Build.MSBuild.dll" />
<UsingTask TaskName="Cosmos.Build.MSBuild.MakeISO" AssemblyFile="$(VSIPDir)\Cosmos.Build.MSBuild.dll" />
<UsingTask TaskName="Cosmos.Build.MSBuild.Ld" AssemblyFile="$(VSIPDir)\Cosmos.Build.MSBuild.dll" />
<UsingTask TaskName="Cosmos.Build.MSBuild.ReadNAsmMapToCosmosMap" AssemblyFile="$(VSIPDir)\Cosmos.Build.MSBuild.dll" />
<UsingTask TaskName="Cosmos.Build.MSBuild.ExtractMapFromElfFile" AssemblyFile="$(VSIPDir)\Cosmos.Build.MSBuild.dll" />
個人理解,Cosmos項目編譯時間應該會載入這些dll進記憶體,在Cosmos項目原始碼中到對應的項目,可以發現它們都直接或者間接繼承自AppDomainIsolatedTask類,查看MSDN,這個繼承該類的類都重寫了execute方法,MSBuild在載入這些程式集時應該是會自動運行這些程式集中繼承自AppDomainIsolatedTask的類重寫的execute方法(啟動並執行順序似乎並不是按照上面的書寫順序),那麼接下來我們只要再分析每一個execute方法,就可以明白這個C#原始碼程式是如何編譯成為本地CPU二進位格式的檔案了(以上各個類運行時需要的參數可以在該檔案的接下來的部分找到,如下:
<IL2CPU DebugMode="$(DebugMode)"
TraceAssemblies="$(TraceAssemblies)"
DebugCom="1"
UseNAsm="true"
References="@(ReferencePath)"
OutputFilename="$(TargetDir)$(MSBuildProjectName).asm"
EnableLogging="true"
EmitDebugSymbols="$(DebugSymbols)"/>
<NAsm InputFile="$(TargetDir)$(MSBuildProjectName).asm"
OutputFile="$(TargetDir)$(MSBuildProjectName).obj"
IsELF="$(IsELF)"
ExePath="$(NasmFile)" />
<!--ELF only-->
<Ld CosmosBuildDir="$(CosmosDir)\Build"
WorkingDir="$(TargetDir)"
Arguments="-Ttext 0x500000 -Tdata 0x200000 -e Kernel_Start -o '$(TargetDir)$(MSBuildProjectName).bin' '$(TargetDir)$(MSBuildProjectName).obj'"
Condition="$(IsELF) == 'true'"/>
<Delete Files="$(TargetDir)$(MSBuildProjectName).obj" Condition="$(IsELF) == 'true'"/>
<ExtractMapFromElfFile InputFile="$(TargetDir)$(MSBuildProjectName).bin"
DebugInfoFile="$(TargetDir)$(MSBuildProjectName).cpdb"
WorkingDir="$(TargetDir)"
CosmosBuildDir="$(CosmosDir)\Build"
Condition="$(IsELF) == 'true'"/>
<CreateItem Include="$(TargetDir)$(MSBuildProjectName).bin" Condition="$(IsELF) == 'true'">
<Output TaskParameter="Include"
ItemName="TempFilesToCopy"/>
</CreateItem>
<Copy SourceFiles="@(TempFilesToCopy)"
DestinationFiles="@(TempFilesToCopy->'$(TargetDir)\%(Filename).obj')"
Condition="$(IsELF) == 'true'"/>
<Delete Files="$(TargetDir)$(MSBuildProjectName).bin" Condition="$(IsELF) == 'true'"/>
<!--End of ELF only-->
<!--binary only-->
<ReadNAsmMapToCosmosMap InputBaseDir="$(TargetDir)"
DebugInfoFile="$(TargetDir)$(MSBuildProjectName).cpdb"
Condition="$(IsELF) == 'false'"/>
<!--end of binary only-->
<!--todo: update cxdb to cxdbg-->
<MakeISO InputFile="$(TargetDir)$(MSBuildProjectName).obj"
OutputFile="$(TargetDir)$(MSBuildProjectName).iso"
CosmosBuildDir="$(CosmosDir)\Build" />)。
4.1先從Cosmos.Build.MSBuild.IL2CPU開始。
Cosmos.Build.MSBuild.IL2CPU繼承自AppDomainIsolatedTask類,並重寫了execute方法,在execute方法中調用執行個體化的IL2CPUTask的類的execute方法。而IL2CPUTask類的execute方法則負責擷取程式的進入點,讀取所有的組件檔。對於小弟的這個水平來說,由於IL2CPU編譯器過於複雜,以下分析請大家以批判的角度來閱讀:讀取組件檔後,應該是調用IL2CPU編譯器來把IL代碼轉換成本地CPU的彙編代碼檔案.asm和支援調試用的符號檔案.cpdb。到此Cosmos.Build.MSBuild.IL2CPU的任務就算完成了。接下來的事情交由Cosmos.Build.MSBuild.Nasm處理。
4.2 Cosmos.Build.MSBuild.Nasm
Cosmos.Build.MSBuild.Nasm直接繼承自Cosmos.Build.MSBuild.BaseToolTask,而BaseToolTask又直接繼承自AppDomainIsolatedTask,查看他們的execute方法內的代碼,可以知道Nasm這個類應該是使用Nasm.exe(一個跨平台的彙編開發工具)這個工具把上一步轉換出來的.asm檔案編譯成elf格式的(關於elf格式可以參看小弟空間裡的另一篇文章http://www.cnblogs.com/li0803/archive/2010/11/15/1877961.html)目標檔案.obj,到此Cosmos.Build.MSBuild.Nasm這個類的工作也結束了,接下來由Cosmos.Build.MSBuild.MakeISO這個類來處理
4.3 Cosmos.Build.MSBuild.MakeISO
Cosmos.Build.MSBuild.MakeISO與Nasm類似,直接繼承自BaseToolTask,間接繼承自AppDomainIsolatedTask,並重寫了自己的execute方法,作用是使用mkisofs.exe這個工具把上一步產生的.obj檔案連結成一個ISO檔案,以便虛擬機器可以載入運行。其實到這一步已經得到了一個可以啟動並執行COSMOS作業系統檔案了,但得到的只是一個ISO檔案,只能再虛擬機器種運行,無法安裝到實際的機子上,也不方便進行調試,所以COSMOS還要MSBuild做了一下的事情來產生一個.bin的二進位檔案和一些支援在VS下對作業系統的原始碼進行調試的.cpdb符號表檔案。不知道這麼說對不對,大家就僅當參考吧,不對的話也希望大家可以幫忙指正一下,呵呵:)
4.4 Cosmos.Build.MSBuild.Ld
Cosmos.Build.MSBuild.Ld也與Nasm類似,直接繼承自BaseToolTask,間接繼承自AppDomainIsolatedTask,並重寫了自己的execute方法,作用是把Nasm類中編譯出來的.obj目標檔案使用ld.exe工具鏈接成一個獨立的ELF格式的本地CPU可啟動並執行二進位檔案(有關編譯和連結的只是可以參考《程式員的自我修養》),然後再交由Cosmos.Build.MSBuild.ExtractMapFromElfFile繼續處理
4.5 Cosmos.Build.MSBuild.ExtractMapFromElfFile
Cosmos.Build.MSBuild.ExtractMapFromElfFile同樣是直接繼承自BaseToolTask,間接繼承自AppDomainIsolatedTask,並重寫了自己的execute方法,在execute方法中調用objdump.exe來處理上一步產生的檔案。小弟也是第一次接觸objdump.exe這個東西,從原始碼上看應該是分析上一步編譯出來的.bin檔案,然後產生支援調試用的.cpdb符號表檔案吧,接著再交由Cosmos.Build.MSBuild.ReadNAsmMapToCosmosMap做最後的處理。
4.6 Cosmos.Build.MSBuild.ReadNAsmMapToCosmosMap
Cosmos.Build.MSBuild.ReadNAsmMapToCosmosMap類直接繼承自AppDomainIsolatedTask類,並重寫自己的execute方法,作用是從Nasm任務中產生的.obj檔案中讀取出地址符號映射表(什麼東西?不太懂),寫入內建的firebird資料庫中,便於原始碼跟蹤調試用。
到此,COSMOS的編譯工作就算是全部完成了