鏈結、裝載與庫
在linux下,當我們使用
gcc來編譯
helloword
程式時,只需要
$gcc hello.c
$./a.out
hello world
其中實際包括4步驟:預處理(prepressinng),編譯(compliation),彙編(assembly),鏈結(linking)
1.預編譯
$ gcc -e hello.c -o hello.i
或 $ cpp helloc > hello.i
預編譯主要處理源**檔案中一「#」開始的預編譯指令
。(刪除#define,處理預編譯指令,刪除
// /* */,
新增檔案行號和檔名標示,保留所有編譯器指令
#progam
)。經過預編譯的
.i檔案不包含任何巨集定義,(所有巨集定義已經展開)。
2.編譯
編譯就是把預編譯完的檔案進行一系列詞法分析,語法分析,語義分析以及優化後生成相應彙編**檔案。
$ gcc -s hello.c -o hello.s
詞法分析 -- 識別單詞
,確認詞類;比如
int i;
知道int
是乙個型別,
i是乙個關鍵字以及判斷
i的名字是否合法
語法分析 -- 識別短語和句型的語法屬性;
語義分析 -- 確認單詞、短語和句型的語義特徵;
**優化 -- 修辭、文字編輯;
**生成 -- 生成譯文。
內聯函式的替換就發生在這一階段
3.彙編
彙編過程就是根據彙編指令和機器指令對照表進行翻譯,生成目標機器指令。
在最終的目標檔案中除了擁有自己的資料和二進位制**之外,還要至少提供2個表:未解決符號表和匯出符號表,分別告訴鏈結器自己需要什麼和能夠提供什麼。
編譯器把乙個cpp編譯為目標檔案的時候,除了要在目標檔案裡寫入cpp裡包含的資料和**,還要至少提供3個表:未解決符號表,匯出符號表和位址重定向表。
未解決符號表
提供了所有在該編譯單元裡引用但是定義並不在本編譯單元裡的符號及其出現的位址。
匯出符號表提供了本編譯單元具有定義,並且願意提供給其他編譯單元使用的符號及其位址。
位址重定向表提供了本編譯單元所有對自身位址的引用的記錄。
$ g++ –c main.s –o main.o
4.鏈結
將生成的目標檔案和系統庫檔案進行鏈結,最終生成了可以在特定平台執行的可執行檔案。
1.位址和空間分配
從輸入目標檔案獲取各段長度,屬性以及位置,並將輸入目標檔案中符號表所有符號定義和符號引用收集同意放到全域性符號表。
2.符號解析與重定位
符號解析。將每個符號引用剛好和乙個符號定義聯絡起來。使用的表:
符號表
重定位。鏈結器把每個符號定義與乙個虛擬位址聯絡起來,然後修改所有對這些符號的引用,使得它們指向這個儲存位置,從而重定位這些節。使用的表:
重定位表,符號表
當鏈結器進行鏈結的時候,首先決定各個目標檔案在最終可執行檔案裡的位置。然後訪問所有目標檔案的地址重定義表,對其中記錄的位址進行重定向(加上乙個偏移量,即該編譯單元在可執行檔案上的起始位址)。然後遍歷所有目標檔案的未解決符號表,並且在所有的匯出符號表裡查詢匹配的符號,並在未解決符號表中所記錄的位置上填寫實現位址。最後把所有的目標檔案的內容寫在各自的位置上,就生成乙個可執行檔案。
靜態庫鏈結
在這種鏈結方式下,函式的**將從其所在地靜態鏈結庫中被拷貝到最終的可執行程式中。這樣該程式在被執行時這些**將被裝入到該程序的虛擬位址空間中。靜態鏈結庫實際上是乙個目標檔案的集合,其中的每個檔案含有庫中的乙個或者一組相關函式的**。
動態庫鏈結
在此種方式下,函式的**被放到稱作是動態鏈結庫或共享物件的某個目標檔案中。鏈結程式此時所作的只是在最終的可執行程式中記錄下共享物件的名字以及其它
少量的登記資訊。在此可執行檔案被執行時,動態鏈結庫的全部內容將被對映到執行時相應程序的虛位址空間。動態鏈結程式將根據可執行程式中記錄的資訊找到相
應的函式**。
對於可執行檔案中的函式呼叫,可分別採用動態鏈結或靜態鏈結的方法。使用動態鏈結能夠使最終的可執行檔案比較短小,並且當共享物件被多個程序使用時能節約
一些記憶體,因為在記憶體中只需要儲存乙份此共享物件的**。但並不是使用動態鏈結就一定比使用靜態鏈結要優越。在某些情況下動態鏈結可能帶來一些效能上損害。
全域性符號(強/弱)
編譯器編譯原始檔時會把原始檔的全域性符號(global symbol)分成強(strong)和弱(weak)兩類傳給彙編器,而隨後彙編器則將強弱資訊編碼並儲存在目標檔案的符號表中。那麼何謂強弱呢?編譯器認為函式與初始化了的全域性變數都是強符號,而未初始化的全域性變數則成了弱符號。
規則1: 不允許強符號被多次定義(即不同的目標檔案中不能有同名的強符號);
規則2: 如果乙個符號在某個目標檔案中是強符號,在其它檔案中都是弱符號,那麼選擇強符號;
規則3: 如果乙個符號在所有目標檔案中都是弱符號,那麼選擇其中任意乙個;
全域性構造與析構
一般c/c++程式從main()函式開始,隨著main()結束而結束。其實在main()函式被呼叫前,為了程式順利執行,要初始化程序執行環境。
1.隊分配初始化
2.執行緒子系統
3.c++全域性物件建構函式
應用程式二進位制介面
描述了應用程式和作業系統之間,乙個應用和它的庫之間,或者應用的組成部分之間的低介面。
裝載 鏈結與庫
第五部分 windows動態鏈結 1 dll函式和變數必須在檔案顯示是匯入還是匯出,declspec dllexport 匯出,declspec dllimport 匯入.建立dll檔案的時候.c檔案生成.dll,lib,exp檔案,然後用.lib檔案和exe中的.c檔案生成的目標檔案鏈結在一起,生...
鏈結 裝載與庫
二 編譯和鏈結 1 預處理過程主要處理那些以 開始的預編譯命令如 include define if 等,但保留 pragma 編譯器指令 因為編譯器要使用它們 另外刪除所有注釋,新增行號和檔名標識。gcc e test.c test.i cpp test.c test.i 2 編譯,生成 s 的彙...
鏈結裝載庫
一般應用程式記憶體空間有如下區域 棧棧儲存了乙個函式呼叫所需要的維護資訊,常被稱為堆疊幀 stack frame 或活動記錄 activate record 一般包含以下幾方面 堆堆分配演算法 段錯誤 segment fault 或 非法操作,該記憶體位址不能 read write 典型的非法指標解...