《C專家程式設計》:對連結的思考
可執行程式產生的的流程
絕大多數編譯器並不是一個單一的龐大程式。它們通常由多達六七個稍小的程式所組成,這些程式由一個叫“編譯器驅動器”的控制程式來呼叫。這些可以方便的從編譯器中分離的單獨程式包括:前處理器、語法和語義檢查器、程式碼生成器、彙編程式、優化器、連結器。
連結器:
"a linker or link editor is a computer utility program that takes one or more object files generated by a compiler and combines them into a single executable file, library file, or another 'object' file."
連結器是一個將編譯器產生的目標檔案打包成可執行檔案或者庫檔案或者目標檔案的程式。
連結器的本質是一個程式,連結器的輸入是編譯器編譯好的目標檔案,連結器的輸出是將目標檔案打包處理後,生成可執行檔案或者庫或者目標檔案
目標檔案並不能直接執行,它需要載入到連結器中。連結器確認main函式為初始進入點(程式開始執行的地方),把符號引用繫結到記憶體地址,把所有的目標檔案集中在一起,再加上庫檔案,從而產生可執行檔案
可以將一個目標檔案大致分為三段:資料段、程式碼段、符號表
連結器的一個主要任務就是符號決議
符號表給連結器提供了兩種資訊,一個是當前目標檔案可以提供給其它目標檔案使用的符號,另一個其它目標檔案需要提供給當前目標檔案使用的符號。有了這些資訊連結器就可以進行符號決議了。連結器會依次掃描每一個給定的目標檔案,同時連結器還維護了兩個集合,一個是已定義符號集合D,另一個是未定義符合集合U,下面是連結器進行符合決議的過程:
- 對於當前目標檔案,查詢其符號表,並將已定義的符號並新增到已定義符號集合D中。
- 對於當前目標檔案,查詢其符號表,將每一個當前目標檔案引用的符號與已定義符號集合D進行對比,如果該符號不在集合D中則將其新增到未定義符合集合U中。
- 當所有檔案都掃描完成後,如果為定義符號集合U不為空,則說明當前輸入的目標檔案集合中有未定義錯誤,連結器報錯,整個編譯過程終止。
上面的過程看似複雜,其實用一句話概括就是隻要每個目標檔案所引用變數都能在其它目標檔案中找到唯一的定義,整個連結過程就是正確的。
如果你覺得上面的解釋比較晦澀的話,你也可以將連結符號決議這個過程想象成如下的遊戲:
新學期開學後,幼兒園的小朋友們都帶了禮物要和其它的小朋友們分享,同時每個小朋友也有自己的心願單,每個小朋友都可以依照自己的心願單去其它的小朋友那裡拿禮物,整個過程結束後,每個小朋友都能拿到自己想要的禮物。
在這個遊戲當中,小朋友就好比目標檔案,每個小朋友自己帶的禮物就好比每個目標檔案的已定義符號集合,心願單就好比每個目標檔案中未定義符號的集合
靜態連結與動態連結
靜態連結在連結程式時需要使用的每個庫函式的一份拷貝被加入到可執行檔案中,即函式庫的拷貝時可執行檔案的物理組成部分
動態連結允許系統提供一個龐大的函式庫集合,可以提供許多有用的服務。但是,程式將在執行時尋找他們,而不是把這些函式庫的二進位制程式碼作為自身可執行檔案的一部分,在可執行檔案裡只是包含了檔名,讓載入器在執行時能夠尋找程式所需要的函式庫,動態連結可執行檔案比功能相同的靜態連結可執行檔案的體積小,所有動態連結到某個特定函式庫的可執行檔案在執行時共享該函式庫的一個單獨拷貝,動態連結是一種“Just-in-time”連結,這意味著程式在執行時必須能夠找到它們所需要的函式庫