solc編譯器分析
來自專欄 360區塊鏈實驗室4 人贊了文章
一、solc介紹
以太坊的智能合約經過編譯後運行在虛擬機上,完成整個工作流程,這個過程,需要solc編譯器來完成。雖然說solc是一個編譯器,但是它又有它與主流編譯器不同之處。主要表現在如下幾點:
1、功能簡單
支持的基本數據類型只有有限的幾種,不支持浮點(或者說想支持,但是目前還沒實現)。編譯指令也較其它編譯器少很多,功能單一。在完成編譯時需要藉助一些其它輔助的工具。 換一句話說,以太坊的具體需求,也不需要它提供類似於c++等編譯器的強大的編譯能力。
2、安全性弱
雖然solc也支持各種處理數據私有的方法,但是由於區塊鏈的先天性問題,部署到鏈上後,可以通過各種手段來訪問和查看私有數據。而且目前以太坊尚未對數據提供混淆和加密的機制。
3、對主流的異常處理機制支持較弱
當前主流的編譯器基本對異常各種操控都已經非常全面,各種理論和技術也不斷湧現,但是以太坊的編譯器對異常的處理機制支持的比較簡單,流於表面化。
4、不考慮平台相關
這也是一個非常重要的事項,一個主流的編譯器應該是支持市場上主流的操作系統,但是在以太坊上沒有這個要求,因為它只服務於以太坊,甚至說對於升級版本的考慮都很簡單。
二、編譯器流程
目前主流的編譯系統基本分為兩大類,即編譯類型和解釋類型。二者各有各的優勢,所以在近年來,二者融合的趨勢只增不減。但是編譯的原理基本沒有發生革命性的變化。
目前JAVA和C#的編譯器都採用了熱點編譯,普通解釋的方式。(這樣表述其實並不太合理,JAVA其實是編譯成位元組碼後,如果編譯器發現某些代碼被經常執行,就會再次彙編成類似C/C++一樣的目標代碼,更具體的可以參看JAVA虛擬機相關資料)
下面簡要分析一下:
1、編譯方式
最典型的就是C/C++的編譯器了,它們基本分為以下幾個步驟:
- 預編譯:在這個階段主要是使用「預處理程序」來處理源代碼,比如C/C++中會把宏和頭文件處理並包含到編譯單元中。
- 詞法分析:將源代碼分割成不可再分割的單詞,主要針對token進行處理歸類等。
- 語法分析:將提取的單詞連接成單詞序列,並根據編程語言規則驗證其順序是否合理。在這裡會形成大家熟知的抽象語法樹(Abstract Syntax Tree)
- 語義分析:發現符合語法規則的語句是否有實際意義。即遍歷整個語法樹進行判斷。
- 抽象語法樹:這裡即根據前邊的各種條件處理後,最終生成的AST。
- 中間碼生成:不同的目標平台有著不同的運行機制,所以在這裡要根據平台兼容性動態生成中間代碼文件。
- 目標代碼生成:最終的彙編文件。
- 目標代碼優化:在生成彙編文件前的優化。
其實後二者是在一起進行的。在一些強類型的編譯器中,比如編譯c/c++中,可能跳過中間碼的生成而由彙編階段再進行目標代碼的生成和優化。也就是說,不同的編譯器針對不同的環境或者場景,會有自己的優化和側重點,這個不要一概而論。下圖為其基本流程:
2、解釋方式
需要說明一下的是,不要被名字誤導,解釋器也是要進行編譯的,只是它編譯的過程沒有編譯方式那麼複雜,特別是針對在類似JAVA這種虛擬機上執行的代碼,還和虛擬機的相關規範有關係。它主要有以下幾個步驟:
- 預處理
- 詞法分析
- 語法分析
- 生成語法樹
- 生成位元組碼
通過上面來看,基本和編譯方式的差不太多,對,往後面就開始有區別了。首先,JAVA和C#走到這裡,它的整個工作流程基本就走完成了。當然二者的虛擬機都會針對具體的情況在運行時再次進行優化和編譯。但那個就屬於優化的範疇了。 但是JS不同,JavaScript還要進行LLInt(Low Level Interpreter 解釋器)執行位元組碼,最後會用JIT即時編譯器再進行具體的編譯和優化。所以說,不是一談解釋器就把它和編譯器搞成兩個山頭,不是你死就是我活。下圖為解釋器工作的基本流程:
3、WebAssembly
其實提到WebAssembly,就是因為區塊鏈現在好多都在向這個方向上轉,包括以太坊和目前大火的EOS。它的主要流程如下圖:
WebAssembly把編譯分成了前端和後端,它把中間代碼做得非常強大,可以使用相關的工具將IR中間代碼和相關的平台代碼相互轉換,同時還可以實現IR的不同表現形式——JSON或者二進位等,方便人和機器識別,從而使應用更加靈活方便。
三、solc的代碼模塊
solc的代碼主要由libdevcore 、libevmasm、 libjulia 、libsolc、 libsolidity 幾個模塊組成。在主目錄下的CMakeLists.txt中可以看到:
add_subdirectory(libdevcore)add_subdirectory(libevmasm)add_subdirectory(libsolidity)add_subdirectory(libsolc)if (NOT EMSCRIPTEN) add_subdirectory(solc) add_subdirectory(liblll) add_subdirectory(lllc)endif()
libdevcore主要提供了基礎的數據操作和編解碼操作,libjulia是用來處理YUL相關,libsolc和 libsolidity提供了大量的詞法語法分析的類,比如聲明定義、語法檢查、類型檢查等等。並且還提供了ABI生成和bin生成相關的操作。
四、SOLC的簡要分析
在分析solidity的源碼時要注意LLL已經基本廢棄了,所以這部分代碼就不再關注了。在solc的主程序中啟動調用了CommandLineInterface這個介面類,在這個介面類中首先調用parseArguments來確定各種編譯的環境參數。如果出現什麼問題,就直接退出。這點有些類似於LINUX的命令機制。而在主程序的最後,調用actOnInput這個函數來實現編譯結果的文件序列化。
需要注意的是,未來版本的 Solidity 編譯器很可能會將 Yul 用作智能合約編譯的中間語言(IR),因此在solc源碼中會出現很多與其相關的代碼。
其實solc編譯的重點就在processInput這個函數中,主要就是下面三塊:
1、complier
調用掃描和編譯單元等模塊生成AST,並在這個過程中完成各種驗證和詞法語法的分析。編譯過程的主要類圖如下:
2、assembly
彙編成相關的棧文件,包括YUL,EWASM等。這其中會AsmAnalyzer、Assembly等類進行流程的處理和異常的控制。彙編過程的主要類圖如下:
3、link
處理相關的鏈接庫和文件。如果在編譯過程中需要相關庫和文件,就會調用鏈接函數對智能合約需要的資源進行鏈接。其實就是位元組碼的映射過程。
五、總結
solc做為智能合約的編譯器,目前的資料還比較少,拋磚引玉,希望把solc的相關內容引入到EVM的分析中,給大家提供一個新的思路。
360區塊鏈平台部是公司整體區塊鏈的服務部門,涵蓋了區塊鏈底層技術研究,應用探索落地,標準技術輸出等區塊鏈的各大領域方向,為公司全方位業務打造區塊鏈場景提供支撐。
推薦閱讀: