連接是將各類代碼和數據部分收集起來並組合成爲一個單一文件的過程,這個文件可被加載(貨被拷貝)到存儲器並執行。數據結構
連接的時機函數
連接器使分離編譯稱爲可能。編碼
大部分編譯系統提供編譯驅動程序:表明用戶在須要時調用語言預處理器、編譯器、彙編器和連接器。spa
(1)運行C預處理器:源程序main.c->ASCII碼中間文件main.i (2)運行C編譯器:main.i->ASCII碼彙編語言文件main.s (3)運行彙編器:main.s->可重定位目標文件
靜態連接器以一組可重定位目標文件和命令行參數做爲輸入,生成一個徹底連接的能夠加載和運行的可執行目標文件做爲輸出。輸入的可重定位目標文件由各類不一樣的代碼和數據節(section)組成。指令在一個節中,初始化的全局變量在另外一個節中,而未初始化的變量又在另一個節中。操作系統
爲了構造可執行文件,連接器必須完成兩個任務:符號解析,重定位
1.一個典型的ELF可重定位目標文件的格式:命令行
每一個可重定位目標模塊m都有一個符號表,包含m所定義和引用的符號的信息。
在連接器的上下文中,三種不一樣的符號:
1.由m定義並能被其餘模塊引用的全局符號。全局連接器對應於非靜態的C函數以及被定義爲Cstatic 屬性的全局變量。
2.由其餘模塊定義並被模塊m以引用的全局符號——外部符號,對應於定義在其餘模塊中的C函數和變量
3.只被模塊m定義和引用的本地符號。
在編譯時,編譯器向彙編器輸出每一個全局符號,或者是強或者是弱,而彙編器把這個信息隱含地編碼在可重定位目標文件的符號表裏。翻譯
函數和已初始化的全局變量時強符號;debug
未初始化的全局變量是弱符號。 unix
根據強弱符號的定義,Unix連接器使用下面的規則來處理多重定義的符號:調試
規則1:不容許有多個強符號。
規則2:若是有一個強符號和多個弱符號,那麼選擇強符號。
規則3:若是有多個弱符號,那麼從這些弱符號中任意選擇一個。
全部的編譯系統都提供一種機制,將全部相關的目標模塊打包成爲一個單獨的文件,稱爲靜態庫。
一旦連接器完成了符號解析這一步,它就是把代碼中的每一個符號引用和肯定的一個符號定義(即它的一個輸入目標模塊中的一個符號表條目)聯繫起來。
重定位由兩步組成:
當彙編器生成一個目標模塊時,它並不知道數據和代碼最終存放在存儲器中的什麼位置。它也不知道這個模塊引用的任何外部定義的函數或者全局變量的位置。因此,不管什麼時候彙編器遇到對最終位置位置的目標引用,它就會生成一個重定位條目,告訴連接器在將目標文件合併成可執行文件時如何修改這個引用。代碼的重定位條目放在.rel.text中。 已初始化的數據的重定位條目放在.rel.data中。
可執行目標文件的格式相似於可重定位目標文件的格式。ELF頭部描述文件的整體格式。它還包括程序的入口點,也就是當程序運行時要執行的第一條指令的地址。.text 、.rodata和.data 節和可重定位目標文件中的節是類似的,除了這些節已經被重定位到它們最終的運行時存儲器地址之外。.init節定義了一個小函數,叫作_init,程序的初始化代碼會調用它。由於可執行文件是徹底連接的(已被重定位了),因此它再也不須要.rel節。
加載器將可執行目標文件中的執行代碼和數據從磁盤拷貝到存儲器中,而後經過跳轉到程序的第一條指令或入口點來運行該程序。這個將程序拷貝到存儲器並運行的過程叫作加載。
要運行可執行目標文件p,能夠在Unix外殼的命令行中輸入它的名字:
unix> ./p
共享庫是一個目標模塊,在運行時,能夠加載到任意的存儲器地址,並和一個在存儲器中的程序連接起來。這個過程稱爲動態連接,是由一個叫作動態連接器的程序來執行的。
共享庫也稱爲共享目標,在Unix系統中一般用.so後綴來表示。微軟的操做系統大量地利用了共享庫,它們稱爲DLL(動態連接庫)。