代碼是怎么一步步變成可執(zhí)行文件的?

這篇文章是關(guān)于Sunny大神在MDCC 2016 的 topic 《把玩編譯器,Clang有意思》的學(xué)習(xí)筆記及實踐。
相關(guān)鏈接:視頻 PPT

Apple 編譯器采用的是 Clang-LLVM 架構(gòu),Clang 作為編譯器前端,LLVM 作為編譯器后端,整體的架構(gòu)如圖:

編譯器架構(gòu).png

采用這樣的架構(gòu)是因為,如果只有一個整體的編譯過程,面對程序員編寫的 M 種高級語言,面對不同機器所對應(yīng)的 N 種可執(zhí)行文件,我們需要 M*N 種編譯器……
若是分為前后端,我們可以將 M 種高級語言編譯為一個機器無關(guān)的中間代碼,作為前后端的橋接語言,再交給不同編譯器后端生成各種機器所需要的目標(biāo)機器代碼,大大簡化了編譯過程。

現(xiàn)在,我們來看代碼是怎么一步步變成可執(zhí)行文件的。

1.Preprocess - 預(yù)處理

處理‘#’開頭的預(yù)處理指令,包括 import 頭文件(將頭文件內(nèi)容逐字替換 import 語句)、macro(宏) 展開、條件預(yù)處理指令,刪除注釋,添加行號和文件名標(biāo)識。

現(xiàn)在嘗試預(yù)處理一個文件,看看是什么樣子:

$clang -E main.m
預(yù)處理

...lots of codes


預(yù)處理

相關(guān)問題:每個頭文件中都 import 基礎(chǔ)庫(Foundation等)或第三方庫頭文件,這些文件重復(fù)編譯,代碼量非常大,如上圖而且不夠整潔優(yōu)雅。
優(yōu)化:

  • 可用pch文件將這些庫文件預(yù)編譯,加快編譯速度。
  • 或是當(dāng)引入蘋果自己的庫時,可采用 @import 關(guān)鍵字引用這些庫,告訴編譯器去使用 modules 的引用形式。蘋果已經(jīng)將一些基礎(chǔ)庫進(jìn)行了封裝,生成一個已編譯的 modules 文件列表,我們編譯時,會首先從已編譯文件里面尋找,若已存在這個編譯文件,直接使用;若沒有,再添加進(jìn)來進(jìn)行編譯。
使用@import關(guān)鍵字時

2.Lexical Analysis - 詞法分析

將預(yù)處理后的代碼文本拆成 Token 流,并不進(jìn)行語義校驗。

$clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m 
//執(zhí)行到詞法分析這一步,并將 -dump-tokens 透傳給編譯器前端,將token打出來
代碼被拆成了一個個Token

3.Semantic Analysis - 語法分析

由 Clang 中 Parser 和 Sema 配合完成

  • 驗證語法是否正確
  • 提示各種錯誤警告提示
  • 根據(jù)設(shè)置語言的語法,形成語義結(jié)點,并將所有節(jié)點組合形成抽象語法樹AST
$clang -fmodules -fsyntax-only -Xclang -ast-dump main.m 
//生成抽象語法樹
生成了花花綠綠的語法樹

另外,這步之后,在我們Run一個工程時,如果選擇Analyze,這里會進(jìn)行

Static Analysis - 靜態(tài)分析

找出一些非語法性錯誤、若需要隱式轉(zhuǎn)換,會在語法樹中插入相應(yīng)的轉(zhuǎn)換節(jié)點。

Analyze
非語法性錯誤

這里,我試圖用 copy 修飾一個可變對象,這樣會造成這個屬性雖然叫 “mutableArray”,但是它存儲著一個不可變的對象。

4.CodeGen - IR 代碼生成

語法樹從頂至下遍歷,翻譯成LLVM 中間代碼,作為前后端的橋接語言,是Clang 編譯器前端的輸出,LLVM 編譯器后端的輸入。
中間代碼一般已經(jīng)非常接近目標(biāo)代碼了,但跟目標(biāo)機器和運行時環(huán)境無關(guān)。
同時,一個重要的作用是與 OC Runtime 進(jìn)行橋接

  • 內(nèi)存結(jié)構(gòu)的生成:
    • Class/Meta Class/Protocol/Category 生成并存放在指定section中,_DATA 或 _objc_classrefs
    • Method/Ivar/Property 生成
    • 組成method_list/ivar_list/property_list 并填入Class
  • 為每個 Ivar 合成偏移值常量,其地址為對象的基地址 + 偏移量
  • 將語法樹中的ObjCMessageExpr翻譯成相應(yīng)objc_msgSeng,對super關(guān)鍵字的調(diào)用翻譯成objc_msgSendSuper
  • 根據(jù)修飾符strong/weak/copy/atomic 合成@property,自動實現(xiàn)setter/getter,處理@synthesize
  • 生成block_layout數(shù)據(jù)結(jié)構(gòu)
    變量的capture _block _weak
    生成_block_invoke 函數(shù)
  • 分析對象引用關(guān)系,插入ARC代碼
    自動調(diào)用[super dealloc]
    為每個擁有ivar 的 Class 合成 .cxx_destructor 方法來自動釋放類的成員變量
    自動釋放池的管理,將ObjcAutoreleasePoolStmt 轉(zhuǎn)譯成 objc_autoreleasePoolPush/Pop
$clang -S -fobjc-arc -emit-llvm main.m -o main.ll
//生成中間代碼
中間代碼的生成

這里我們可以看到一些熟悉的身影,比如 @objc_msgSend...

5.Optimize - 優(yōu)化

$clang -O3 -S -fobjc-arc -emit-llvm main.m -o main.ll
//可采用不同優(yōu)化級別優(yōu)化中間代碼
可以看到,優(yōu)化后代碼量減少
在Xcode中可以設(shè)置優(yōu)化級別

LLVM Bitcode - 生成字節(jié)碼

字節(jié)碼是一種包含執(zhí)行程序、由一序列 op 代碼/數(shù)據(jù)對組成的二進(jìn)制文件,但與特定機器碼無關(guān),需要直譯器轉(zhuǎn)譯后才能生成機器碼,可以看作是包含一個執(zhí)行程序的二進(jìn)制文件。

$clang -emit-llvm -c main.m -o main.bc
//形成二進(jìn)制流
二進(jìn)制流

6. Assemble - 生成 Target 相關(guān)匯編

$clang -S -fobjc-arc main.m -o main.s
//生成匯編代碼
匯編代碼

Assemble - 生成Target相關(guān)Object(Mach-o)

$clang -fmodules -c main.m -o main.o    
//Mach-o 是蘋果系統(tǒng)的目標(biāo)文件
生成的main.o文件

可以看到,生成的目標(biāo)文件有 Mach Header 頭部、Load Commands 加載命令、Section 區(qū)域、 Relocations 重定位信息、Symbol 符號表、String字符串表等。

  • 一個 mach_header 標(biāo)記一些元信息,比如架構(gòu)、CPU、大小端等信息

  • 多個 Load Command 表示如何加載每個段的信息

  • 多個 Segment 及 Section 包含每個段自身的信息,包括數(shù)據(jù)、代碼等

    • Common Segments 段包含
      __PAGEZERO : Catch 訪問NULL指針的非法操作段
      __TEXT : 只讀數(shù)據(jù),只讀常量,C strings
      __DATA : 全局/靜態(tài)變量
      __LINKEDIT : 包含需要被動態(tài)連接器使用的信息,包括符號表、字符串表、重定位表項

可以用MachoView來打開 .o 文件
MachoView GitHub

7. Link - 鏈接,生成 Executable 可執(zhí)行文件

$clang main.m -o main
$./main

//TODO

Clang-LLVM編譯過程.png

經(jīng)過這一步步,我們用各種高級語言編寫的代碼就轉(zhuǎn)換成了機器可以看懂可以執(zhí)行的目標(biāo)代碼了??????

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容