轉(zhuǎn)載鏈接:https://xiaozhuanlan.com/topic/2395487601
1. 預(yù)編譯(Prepressing)
clang -E main.m -o main.i
處理源代碼文件中的以"#"開(kāi)頭的預(yù)編譯指令。規(guī)則如下:
- "#define"刪除并展開(kāi)對(duì)應(yīng)宏定義。
- 處理所有的條件預(yù)編譯指令。如#if/#ifdef/#else/#endif。
- "#include/#import"包含的文件遞歸插入到此處。
刪除所有的注釋"http://或/**/"。- 添加行號(hào)和文件名標(biāo)識(shí)。如“# 1 "main.m"”,編譯調(diào)試會(huì)用到。
2. 編譯(Compilation)
clang -S main.i -o main.s
把上面得到的.i文件進(jìn)行:詞法分析、語(yǔ)法分析、靜態(tài)分析、優(yōu)化生成相應(yīng)的匯編代碼,得到.s文件。
- 詞法分析:源代碼的字符序列分割成一個(gè)個(gè)token(關(guān)鍵字、標(biāo)識(shí)符、字面量、特殊符號(hào)),比如把標(biāo)識(shí)符放到符號(hào)表(靜態(tài)鏈接那篇,重點(diǎn)講符號(hào)表)。
- 語(yǔ)法分析:生成抽象語(yǔ)法樹(shù) AST,此時(shí)運(yùn)算符號(hào)的優(yōu)先級(jí)確定了;有些符號(hào)具有多重含義也確定了,比如“*”是乘號(hào)還是對(duì)指針取內(nèi)容;表達(dá)式不合法、括號(hào)不匹配等,都會(huì)報(bào)錯(cuò)。
- 靜態(tài)分析:分析類(lèi)型聲明和匹配問(wèn)題。比如整型和字符串相加,肯定會(huì)報(bào)錯(cuò)。
- 中間語(yǔ)言生成:CodeGen根據(jù)AST自頂向下遍歷逐步翻譯成 LLVM IR,并且在編譯期就可以確定的表達(dá)式進(jìn)行優(yōu)化,比如代碼里t1=2+6,可以?xún)?yōu)化t1=8。(假如開(kāi)啟了bitcode,)
- 目標(biāo)代碼生成與優(yōu)化:根據(jù)中間語(yǔ)言生成依賴(lài)具體機(jī)器的匯編語(yǔ)言。并優(yōu)化匯編語(yǔ)言。這個(gè)過(guò)程中,假如有變量且定義在同一個(gè)編譯單元里,那給這個(gè)變量分配空間,確定變量的地址。假如變量或者函數(shù)不定義在這個(gè)編譯單元,得鏈接時(shí)候,才能確定地址。
3. 匯編(Assembly)
clang -c main.s -o main.o
匯編就是把上面得到的.s文件里的匯編指令一一翻譯成機(jī)器指令。得到.o文件,也就是目標(biāo)文件,后面會(huì)重點(diǎn)講的Mach-O文件。
3. 鏈接(Linking)
clang main.o -o main
遠(yuǎn)古時(shí)代,一個(gè)程序只有一個(gè)源代碼文件,導(dǎo)致程序的維護(hù)非常困難?,F(xiàn)在程序都是分模塊組成,比如一個(gè)App,對(duì)應(yīng)有多個(gè)源代碼文件。每個(gè)源代碼文件匯編成目標(biāo)文件,根據(jù)上面流程A目標(biāo)文件訪問(wèn)B目標(biāo)文件的函數(shù)或者變量,是不知道地址的,鏈接就是要解決這個(gè)問(wèn)題。鏈接過(guò)程主要包括地址和空間分配、符號(hào)決議和重定位。
鏈接就是把目標(biāo)文件(一個(gè)或多個(gè))和需要的庫(kù)(靜態(tài)庫(kù)/動(dòng)態(tài)庫(kù))鏈接成可執(zhí)行文件。后面會(huì)分別講靜態(tài)鏈接和動(dòng)態(tài)鏈接。