鏈接(static & dynamic)
具體實作方法見《深入理解計算機系統 第三版》 僅僅整理一些思路
靜態連接
連接器的任務
- 符號決議
- 重定位:匯編器生成的代碼和資料節都是從地址0開始,需要將每個符號定義與一個記憶體位置管理,從而重定位 這些節,并修改對這些符號的參考,
三種目標檔案:
- 可重定位目標檔案:與其他可重定位目標檔案結合,創建一個可執行檔案
- 可執行目標檔案
- 共享目標檔案
可重定位檔案格式

部分說明:
.rodata 只讀資料 .symtab 符號表
.rel.text 一個.text節的位置串列,組合時需要修改
.rel.data 被模塊參考或定義的所有全域變數重定位資訊
符號與符號表
符號表包含該檔案(m)定義和參考的符號資訊
- m定義的唄其他模塊參考的全域符號
- 其他模塊定義被m參考的全域符號
- 只被m定義和參考的全域符號 帶static的全域變數和函式
attention:不包含本地非靜態的符號,這些在堆疊中管理

section 用數值表示對應節 1對應.text節,其余同理
處理重定義的全域符號
未初始化—弱變數 初始化----強變數
- 不允許有多個同名的強符號
- 優先選擇強符號
- 如果多個弱符號同名任意選擇一個
靜態庫連接
舊有技術缺點:
- 每個可執行檔案都包含著一份標準函式集合的副本,浪費空間,
- 并且每個運行程式都會將這些函式放在記憶體中,浪費記憶體,
- 如果標準函式一旦改變需要重新編譯整個檔案
靜態庫目的:
- 將相關函式編譯為獨立的模塊,封裝成為單獨的靜態庫檔案
- 鏈接時候,聯結器只需要復制被參考的目標模塊
重定位
STEP1:將所有的相同型別節合并為同一型別的聚合節
STEP2:修改代碼節和資料節中對每個符號的參考,需要使用重定位條目

兩種基本重定位:
R_X86_64_PC32:32位PC地址相對參考
R_X86_64_32:32位絕對地址參考
可執行目標檔案
相比于重定位目標檔案少了.rel節 多了入口點 和.init段 其他相似
動態鏈接
靜態缺點:
- 定期維護或者更新后需要顯式第將程式與庫重新鏈接
- 對于經常使用的函式如I/O,運行時這些代碼會復制到運行行程的文本段,浪費記憶體
共享庫(共享目標檔案)是一個目標模塊,在運行或者加載時候可以加載到任意的記憶體地址,并和一個在記憶體中的程式鏈接起來,這個程序為動態鏈接
Linux中的<dlfcn.h>中有簡單的介面來實作在運行時候來加載和鏈接共享庫
共享庫的一個主要目的是允許多個正在運行的行程共享記憶體中相同的代碼,因此節約了記憶體資源,實作方式:位置無關代碼
位置無關代碼
原理:無論在記憶體中的何處加載一個目標模塊,資料段與代碼段的距離不變(即在運行時候,代碼段中的任何指令和資料段中的任何變數的距離是一個常量),與絕對記憶體位置無關
實作方式GOT(全域偏移量表)+PLT程序鏈接表



小結
連接器的兩個主要任務:
- 符號決議:將目標檔案的每個全域符號系結到一個唯一的定義
- 重定位:確定每個符號的最終記憶體地址,修改對這些目標的參考,
程式中會引起錯誤的地方:
- 全域變數重名
- 連接器從左到右的順序掃描來決議符號參考
使用動態鏈接,共享庫的例程和資料可能仍然未決議,當第一次呼叫時候才會加載(LAZY模式,相對的有NOW模式)
共享庫實作方法:位置無關代碼
符號的最終記憶體地址,修改對這些目標的參考,
程式中會引起錯誤的地方:
- 全域變數重名
- 連接器從左到右的順序掃描來決議符號參考
使用動態鏈接,共享庫的例程和資料可能仍然未決議,當第一次呼叫時候才會加載(LAZY模式,相對的有NOW模式)
共享庫實作方法:位置無關代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/278076.html
標籤:其他
