高級C/C++編譯技術之讀書筆記(二)之庫的概念

                                                                                    

  最近有幸閱讀了《高級C/C++編譯技術》深受啓發,該書深刻淺出地講解了構建過程(編譯、連接)中的各類細節,從多個角度展現了程序與庫文件或代碼的集成方法,提出了面向代碼複用和系統集成的軟件架構設計方法,以及系統開發過程當中疑難問題的解決方案。
  如下將回頭記錄下其中的關鍵要點,以便後面查閱。

本節思惟導圖

1. 位置無關代碼(PIC)

  首先,須要理解加載域與運行域的概念。加載域是代碼存放的地址,運行域是代碼運行時的地址。爲何會產生這2個概念?這2個概念的實質意義又是什麼呢?linux

  在一些場合,一些代碼並不在儲存這部分代碼的地址上執行地址,好比說,放在norflash中的代碼可能最終是放在RAM中運行,那麼中norflash中的地址就是加載域,而在RAM中的地址就是運行域。架構

  在彙編代碼中咱們經常會看到一些跳轉指令,好比說b、bl等,這些指令後面是一個相對地址而不是絕對地址,好比說b main,這個指令應該怎麼理解呢?main這裏到底是一個什麼東西呢?這時候就須要涉及到連接地址的概念了,連接地址實際上就是連接器對代碼中的變量名、函數名等東西進行一個地址的編排,賦予這些抽象的東西一個地址,而後在程序中訪問這些變量名、函數名就是在訪問一些地址。通常所說的連接地址都是指連接這些代碼的起始地址,代碼必須放在這個地址開始的地方纔能夠正常運行,不然的話當代碼去訪問、執行某個變量名、函數名對應地址上的代碼時就會找不到,接着程序無疑就是跑飛。可是上面說的那個b main的情形有點特殊,b、bl等跳轉指令並非一個絕對跳轉指令,而是一個相對跳轉指令,什麼意思呢?就是說,這個main標籤最後獲得的只並非main被連接器編排後的絕對地址,而是main的絕對地址減去當前的這個指令的絕對地址所獲得的值,也就是說b、bl訪問到的是一個相對地址,不是絕對地址,所以,包括這個語句和main在內的代碼段不管是否放在它的運行域這段代碼都能正常運行。這就是所謂的位置無關代碼。函數

  由上面的論述能夠得知,若是你的這段代碼須要實現位置無關,那麼你就不能使用絕對尋址指令,不然的話就是位置有關了。工具

2. 靜態庫的建立

  靜態庫是經過編譯器編譯源代碼文件並將生成的目標文件打包生成後的歸檔文件,咱們經過名爲歸檔器(archiver)的工具來生成靜態庫spa

  $ gcc -c first.c second.c 架構設計

  $ ar rcs libstaticlib.a first.o second.o設計

  ar工具還能夠完成如下任務:xml

  (1)從庫文件中刪除一個或多個目標文件blog

  (2)從庫文件中替換一個或多個目標文件接口

  (3)從庫文件中提取一個或多個目標文件

3. 丟失符號可見性和惟一性的可能性

  連接器將靜態庫的節拼接到客戶二進制文件,當連接完成後,靜態庫的節將與客戶二進制文件中的原有的目標文件節進行無縫連接,靜態庫中的符號成爲客戶二進制文件符號列表中的一部分,而且保留了其原有的可見性,靜態庫中的全局符號成爲客戶二進制文件的全局符號,一樣地,靜態庫的局部符號也稱爲客戶二進制文件的局部符號。

  可是,當客戶二進制文件是一個動態庫時,上面的原則仍是不是同樣呢?

  動態庫的設計原則規定只提供(即接口可見性)知足與外部通訊的接口,採用該設計原則最終會影響到靜態庫符號的可見性,靜態庫的符號不會做爲全局可見的符號保留,而是會變成私有符號或被忽略(即動態庫的符號表中沒有這個靜態庫符號)

  另一個很是重要的特性是:動態庫可以徹底自主管理其局部符號,實際狀況是會有許多動態庫被加載到相同的進程中,一個動態庫會包含與其它動態庫具備相同名稱的局部符號,而連接器可以避免命名的衝突。

4. 靜態庫使用禁忌

(1)當連接一個靜態庫須要多個動態庫時,可能不該該使用靜態庫,選擇使用動態庫可能會比較有利

(2)應該使用同一庫的已存在的動態庫版本

(3)若是你所實現的功能須要存在一個類的單個示例中,最好使用動態庫

5. 靜態庫連接的具體規則

在linux下連接靜態庫須要遵循下列規則

(1)依次連接靜態庫,每次一個靜態庫

(2)連接靜態庫從傳遞給鏈接器的靜態庫列表的最後一個靜態庫開始(經過命令或者makefile),且會反方向逐個連接,直到列表中的第一個位置

(3)連接器會對靜態庫進行詳細的檢索,在全部的目標文件中,只有包含客戶二進制文件實際所需符號的目標文件纔會進行連接

  因爲這些特定的規則,咱們有時須要在傳遞給連接器的靜態列表屢次添加同一個靜態庫,當一個靜態庫同時提供了多種徹底不一樣的功能時,就會遇到這種屢次添加的狀況

6. 將靜態庫轉換成動態庫

(1)使用打包工具(ar)來提取全部靜態庫中的目標文件

  $ ar -x <static library>.a

  執行該命令會把靜態庫中的目標文件集合提取到當前目錄

(2)連接器使用提取出來的目標文件集合來構建動態庫

7. 靜態庫在64位linux平臺上的問題

  在64位linux平臺上使用靜態庫會遇到一個很是特殊的狀況

(1)將靜態庫連接到可執行文件與在32位linux上進行操做沒有任何區別

(2)可是,靜態庫連接到共享庫則要求靜態庫須要用-fPIC或-mcmodel-large編譯器選項來進行構建(編譯器在編譯過程當中的錯誤打印輸出時的建議)

靜態庫的導入選擇條件:當客戶端二進制文件連接靜態庫時不會把整個靜態庫的內容連接進來,智慧連接目標文件中必要的符號
動態庫的導入選擇條件:當客戶端二進制文件連接動態庫時,選擇的條件是在符號表這個層面上,只會選擇包含在符號表中實際須要的動態庫符號,可是在其它全部方面,這個選擇條件其實是不存在的,不管動態庫功能中具體須要的代碼有多少都會將整個動態庫連接進來。
相關文章
相關標籤/搜索