LLVM、Clang 、dyld

LLVM

LLVM是iOS目前的構(gòu)架編譯器的框架系統(tǒng),LLVM會對每個文件進行編譯,生成 Mach-O(可執(zhí)行文件);鏈接器會將項目中的多個 Mach-O 文件合并成一個。
編譯的幾個主要過程:
1.LLVM 會預(yù)處理你的代碼,比如把宏嵌入到對應(yīng)的位置
2.預(yù)處理完后,LLVM 會對代碼進行詞法分析和語法分析,以及代碼優(yōu)化,然后生成 AST 。AST 是抽象語法樹,結(jié)構(gòu)上比代碼更精簡,遍歷起來更快,所以使用 AST 能夠更快速地進行靜態(tài)檢查,同時還能更快地生成 IR(中間表示)
3.最后 AST 會生成 IR,IR 是一種更接近機器碼的語言,區(qū)別在于和平臺無關(guān),通過 IR 可以生成多份適合不同平臺的機器碼。對于 iOS 系統(tǒng),IR 生成的可執(zhí)行文件就是 Mach-O

Clang

Clang基于LLVM、發(fā)布于LLVM BSD許可證下的C/C++/Objective-C/Objective-C++編譯器

dyld

  • dyld(the dynamic link editor)是蘋果的動態(tài)鏈接器,最主要作用就是將符號綁定到地址上。

Mach-O 文件里面的內(nèi)容,主要就是代碼和數(shù)據(jù):代碼是函數(shù)的定義;數(shù)據(jù)是全局變量的定義,包括全局變量的初始值。不管是代碼還是數(shù)據(jù),它們的實例都需要由符號將其關(guān)聯(lián)起來。 因為 Mach-O 文件里的那些代碼,比如 if、for、while 生成的機器指令序列,要操作的數(shù)據(jù)會存儲在某個地方,變量符號就需要綁定到數(shù)據(jù)的存儲地址。你寫的代碼還會引用其他的代碼,引用的函數(shù)符號也需要綁定到該函數(shù)的地址上。 而鏈接器的作用,就是完成變量、函數(shù)符號和其地址綁定這樣的任務(wù)。而這里我們所說的符號,就可以理解為變量名和函數(shù)名。

  • 鏈接器對代碼做了什么

1.去項目文件里查找目標代碼文件里沒有定義的變量

  1. 掃描項目中的不同文件,將所有符號定義和引用地址收集起來,并放到全局符號表中

3.計算合并后長度及位置,生成同類型的段進行合并,建立綁定

  1. 對項目中不同文件里的變量進行地址重定位。

你在項目里為某項需求寫了一些功能函數(shù),但隨著業(yè)務(wù)的發(fā)展,一些功能被下掉了或者被其他負責的同事在另一個文件里用其他函數(shù)更新了功能。那么這時,你以前寫的那些函數(shù)就沒有用武之地了。日長月久,無用的函數(shù)越來越多,生成的 Mach-O 文件也就越來越大。 這時,鏈接器在整理函數(shù)的符號調(diào)用關(guān)系時,就可以幫你理清有哪些函數(shù)是沒被調(diào)用的,并自動去除掉。那這是怎么實現(xiàn)的呢? 鏈接器在整理函數(shù)的調(diào)用關(guān)系時,會以 main 函數(shù)為源頭,跟隨每個引用,并將其標記為 live。跟隨完成后,那些未被標記 live 的函數(shù),就是無用函數(shù)。然后,鏈接器可以通過打開 Dead code stripping 開關(guān),來開啟自動去除無用代碼的功能。并且,這個開關(guān)是默認開啟的。

  • 動態(tài)庫鏈接

在真實的 iOS 開發(fā)中,你會發(fā)現(xiàn)很多功能都是現(xiàn)成可用的,不光你能夠用,其他 App 也在用,比如 GUI 框架、I/O、網(wǎng)絡(luò)等。鏈接這些共享庫到你的 Mach-O 文件,也是通過鏈接器來完成的。 鏈接的共用庫分為靜態(tài)庫和動態(tài)庫:靜態(tài)庫是編譯時鏈接的庫,需要鏈接進你的 Mach-O 文件里,如果需要更新就要重新編譯一次,無法動態(tài)加載和更新;而動態(tài)庫是運行時鏈接的庫,使用 dyld 就可以實現(xiàn)動態(tài)加載。 Mach-O 文件是編譯后的產(chǎn)物,而動態(tài)庫在運行時才會被鏈接,并沒參與 Mach-O 文件的編譯和鏈接,所以 Mach-O 文件中并沒有包含動態(tài)庫里的符號定義。也就是說,這些符號會顯示為“未定義”,但它們的名字和對應(yīng)的庫的路徑會被記錄下來。運行時通過 dlopen 和 dlsym 導(dǎo)入動態(tài)庫時,先根據(jù)記錄的庫路徑找到對應(yīng)的庫,再通過記錄的名字符號找到綁定的地址。 dlopen 會把共享庫載入運行進程的地址空間,載入的共享庫也會有未定義的符號,這樣會觸發(fā)更多的共享庫被載入。dlopen 也可以選擇是立刻解析所有引用還是滯后去做。dlopen 打開動態(tài)庫后返回的是引用的指針,dlsym 的作用就是通過 dlopen 返回的動態(tài)庫指針和函數(shù)符號,得到函數(shù)的地址然后使用。

簡單來說, dyld 做了這么幾件事兒:
1.先執(zhí)行 Mach-O 文件,根據(jù) Mach-O 文件里 undefined 的符號加載對應(yīng)的動態(tài)庫,系統(tǒng)會設(shè)置一個共享緩存來解決加載的遞歸依賴問題;
2.加載后,將 undefined 的符號綁定到動態(tài)庫里對應(yīng)的地址上;
3.最后再通過runtime處理 +load 方法,main 函數(shù)返回后運行 static terminator。

dyld開源地址:dyld開源地址
dyld更詳細參考:dyld詳解

最后編輯于
?著作權(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)容