一、編譯鏈結
在編寫linux驅動程式時,時常會發現鏈結出錯,當時往往不知道錯誤在哪。現在了解到鏈結器的工作原理之後,明白當時為什麼出錯了。對於以後有效率地編寫驅動程式有很大幫助。
乙個c語言程式,經過諸如gcc之類的編譯器編譯成可執行檔案一般會經歷4個處理過程,這個大部分的linux入門書籍都有講到過,如果沒有扔掉它,:)!
分別經過c預處理器(cpp)、c編譯器(cl)、c彙編器(as)、c鏈結器(ld)。這個怎麼處理生成什麼不是本文的主要內容。
ascii原始檔.c----》ascii中間檔案.i(由cpp處理後生成)------》組合語言檔案.s(由cl處理後生成)-----》可重定位目標檔案.o(由as處理後生成)-----》可執行目標檔案(由ld處理後生成)。
說到可執行目標檔案和可重定位目標檔案不得不講講elf(可執行可鏈結格式),但elf在大部分書籍都有講到,且網上資料相當多。
elf定義了可重定位和可執行目標檔案構成的大致框架。可重定位目標檔案中,有很多的節,每個節都是固定大小的條目。這些條目儲存了**、符號、引用、變數和除錯等等相關資訊。
乙個點c檔案會引用其他檔案的函式或者是全域性變數,那麼經過上面那一套走下來,會生成點o檔案也就是可重定位目標檔案。在可重定位目標檔案中也會儲存引用的函式或全域性變數資訊,而這些引用的函式和全域性變數在鏈結生成可執行程式時都要進行重定位。
典型地,.rel.text:**段需要重定位的資訊。主要是一些引用的函式;.rel.data:資料段需要重定位的資訊。主要是本模組引用的全域性變數等。
那麼,當我們將所有的可重定位目標檔案和靜態庫生成完成之後,輸入命令:
gcc ex1.c ex2.o ex3.o ex4.a會發生什麼呢?
可以很簡單的說,就是ld鏈結器會將它們連線生成可執行檔案。具體有兩個步驟:1是符號解析,2是重定位。
那麼符號到底是如何解析的呢?
還是上面的例子:
gcc ex1.c ex2.o ex3.o ex4.a
這其中ex4.a包含ex5.o和ex6.o
不失一般性,我們假設有:
ex1.c引用了ex2.o中的符號2.a,ex3.o中的3.a,ex4.a裡面的ex5.o中5.a,ex7.o中的7.a;
ex2.o則是ex3.o中的3.b
ex3.o則是ex2.o中的2.b,
就是如下圖所示的鏈結器命令引用例項:
當然此處假設ex2.o定義了2.a和2.b符號,ex3.o和ex5.o 、ex6.o與此類似。
在符號解析階段鏈結器從左往右按照命令列的順序來掃瞄可重定位目標檔案和靜態庫。它維持了三個集合e、u和d。
e是可重定位目標檔案的集合
u是未解析符號的集合
d為已定義的符號的集合
鏈結器按照如下步驟來解析符號:
步驟1步驟2
步驟3步驟4
eex1.o
ex1.o ex2.o
ex1.o ex2.o ex3.o
ex1.o ex2.o ex3.o ex5.o
u2.a 3.a 5.a
3.a 5.a 3.b
5.a空
d2.a 2.b
2.a 2.b 3.a3.b
2.a 2.b 3.a 3.b 5.a
鏈結器符號解析演算法小解以及靜態庫鏈結順序等等問題
在編寫linux驅動程式時,時常會發現鏈結出錯,當時往往不知道錯誤在哪。現在了解到鏈結器的工作原理之後,明白當時為什麼出錯了。對於以後有效率地編寫驅動程式有很大幫助。乙個c語言程式,經過諸如gcc之類的編譯器編譯成可執行檔案一般會經歷4個處理過程,這個大部分的linux入門書籍都有講到過,如果沒有扔...
鏈結器,符號解析與重定位 概念
符號解析。將每個符號引用剛好和乙個符號定義聯絡起來。符號分為四類 匯出符號 export,本地符號 匯入符號 import,外部符號 靜態符號 本地符號 區域性符號 本地符號,不出現在符號表中 匯出符號,在本模組定義,能夠被其他模組引用的符號。非static全域性函式,非static全域性變數。匯入...
鏈結器,符號解析與重定位 概念
符號分為四類 匯出符號 export,本地符號 匯入符號 import,外部符號 靜態符號 本地符號 區域性符號 本地符號,不出現在符號表中 匯出符號,在本模組定義,能夠被其他模組引用的符號。非static全域性函式,非static全域性變數。匯入符號,在其他模組定義,被本模組引用的符號。exter...