鏈接過程

note:這裏還要補充的可能很多,包含可重定位目標文件的內容等。

符號和符號表

在ld等鏈接器的上下文中,有三種不同的符號。每一個可重定位目標模塊m都有一個符號表,它包含m所定義的和引用的符號的信息。

由m所定義的並且能夠被其他模塊所引用的全局符號。就是非靜態的c函數和被定義爲不帶c static屬性的全局變量。

在其他模塊定義並被模塊m引用的全局符號。對應於定義在其他模塊中的c函數和變量,external。
只在模塊m定義和引用的本地符號。有的就是對應於帶static屬性的c函數和全局變量。這些符號在模塊m中隨處可見,但是不能被其他模塊引用。目標文件中對應於模塊m的節和相應的源文件中的名字都能獲得本地符號。

.symtab中的符號表不包含對應於本地非靜態程序變量的任何符號。這些符號在運行時在棧中被管理,鏈接器對此類符號不感冒。

對於static屬性的本地過程變量是不在棧中管理的,相反,編譯器在.data和.bss中爲每個定義分配空間,並在符號表中創建一個有唯一名字的本地鏈接器符號。

帶有static屬性的全局變量和函數是模塊私有的。相反,則是可以被其他模塊訪問的。

符號表是由彙編器構造的,使用編譯器輸出到彙編語言.s文件中的符號。

關於這個表的每個條目,有name和value。value是表示符號的地址,對於可重定位的模塊來說,value是距定義目標的節的起始位置的偏移。還有size是目標的大小。每個符號都和目標文件的某個節相關聯。.ndx爲1表示.text節,爲3表示.data節。

鏈接器解析符號引用的方法是將每個引用和它輸入的可重定位的目標文件的符號表中的一個確定的符號定義聯繫起來。

符號解析

首先,我對符號表已經有了一定的概念,就是那張可以重定義的符號表(含有對符號的定義)。這個表格第一次接觸到,還是會比較新穎的,現在就成了學習符號解析的利器了。
爲什麼需要解析符號引用?因爲在代碼的其它部分會使用到這些符號。
對於不同類型的符號,符號解析分別有什麼特點?對那些引用和定義在同一個模塊中的符號,符號解析是非常簡單的。
當編譯器遇到一個不是在本地定義的全局符號的時候呢?它當然會假設該符號是在其他模塊中定義的,然後就去查找(這個過程就生成一個鏈接器符號表條目,並把它交給鏈接器處理)如果鏈接器在它的任何模塊都找不到這個被引用的符號,它就輸出一條錯誤信息。。
(note哈哈,下面將要給出一個例子,這個很簡單,也經常遇到。但是在c.learncodethehardway裏面介紹了很多靜態庫和動態庫編譯鏈接的東西,從這裏過去便可以做進一步的理解了。)
在cpp和Java 中由於有重載的存在,所以鏈接器貌似要使用到一種叫做mangling的技術(可以多查找一點資料看看,應該非常有意思)

那麼鏈接器是如何工作的呢?
強弱符號的概念?沒有初始化的全局變量則是弱符號。
鏈接器的邏輯並不複雜。
但是在特殊的情況下,鏈接器是如何解析多重定義的全局符號的?
規則:
1.部允許有多個強符號。
2.如果有一個強符號和多個弱符號,那麼選擇強符號。(這裏輸出一個例子,非常有意思哦)
3.如果有多個弱符號,那麼從這些弱符號中任意選擇一個。(這裏甚至還談到了不同類型的重複符號,可能會產生很大的分歧)

與靜態庫(以前寫好的模塊)鏈接

note:這裏有很多例子和技巧可言(恰好還有這方面的經驗)。
除了靜態庫,還有哪些可以使用到的alternative?
爲什麼系統要支持庫的概念呢?因爲他定義了一組廣泛的標準IO,字符串操作盒證書數學函數等。他們在libc.a中。如果不使用靜態庫,可以由多種方法來做這個工作(比如讓編譯器辨認出對標準函數的調用,並直接生成相應的代碼(這個對編譯器的複雜性將是一個可怕的災難。。。。當然還有別的缺點啦。根本就是不合適的))
另外一個呢?就是把所有的標準C函數都放在一個單獨的可重定位目標模塊中,然後應用程序員就可以把這個模塊連接到他們的可執行文件中啦。。。(和上一種方法相比,至少能夠把編譯器的實現和標準函數的實現分離開來)缺點呢?每個可執行文件都包含着一份標準函數集合的完全拷貝,這對磁盤空間是很大的浪費。更糟糕的是,每個運行的程序都將它自己的這些函數的拷貝放在存儲器中,這又是一個極大地浪費。
當然我們也可以爲每個標準函數創建一個獨立的可重定位文件,把他們放在一個大家都知道的目錄中來解決其中的一些問題。(這需要應用程序員現實的簡介合適的目標模塊到他們的可執行文件)
靜態庫到底是什麼?有什麼好處?
在unix系統中,靜態庫以一種稱爲archive的特殊文件格式存放在磁盤中。存檔文件是一組連接起來的可重定位目標文件的集合(有一個頭部用來描述每個成員目標文件的大小和位置)。存檔文件名由後綴.a標誌。

現在有一個很重要的問題,那就是鏈接器如何使用靜態庫來解析引用?

重定位
是符號解析的下一步,將合併輸入模塊,併爲每個符號分配運行時地址。

可執行目標文件
我們的c程序,開始時是一組ascii 文本文件,已經被轉化爲一個二進制文件,而且這個二進制文件包含加載程序到存儲器並運行它所需的所有信息。

加載可執行目標文件
一個loader的操作系統代碼,來運行可執行程序。

動態鏈接共享庫

從應用程序中加載和鏈接共享庫

p.s.,這些我都講的過於簡略,有空再詳細補充圖片解析和內容

參考文獻

csapp:Computer Systems A Programmer’s perspective
http://c.learncodethehardway.org/book/ex28.html
http://c.learncodethehardway.org/book/ex29.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章