4.1 空間與地址分配
- 問題:當有多個目標文件時劝术,是如何合并到一個文件中的(生成可執(zhí)行文件)寥枝,是在哪個位置插入相關(guān)代碼的井仰?以什么規(guī)則進行插入恶守?
4.1.1 按序疊加
- 最簡單的方式就是按序疊加第献,這樣做非常浪費空間,因為每個段都需要地址和空間對齊兔港,并且按章class 1庸毫,class 2,順序添加衫樊,出現(xiàn).text段重復飒赃,導致內(nèi)存空間中大量碎片
4.1.2 相似段合并
- 將.text段合并
- 鏈接器一般都采用兩步鏈接:
1.空間與地址分配:掃描所有的輸入目標文件,并且獲得他們的各個段的長度科侈,屬性和位置载佳,并且將輸入目標文件中的符號表中所有的符號定義和符號引用收集起來,同意放到一個全局符號表中兑徘,這一步鏈接器能獲取所有目標文件的段長度刚盈,并且將它們合并羡洛,計算出輸出文件中各個段合并后的長度與位置挂脑,并建立映射關(guān)系
2.符號解析與重定位:使用第一步收集到的信息,讀取文件中段內(nèi)容數(shù)據(jù)欲侮、重定位信息崭闲,并且進行符號解析與重定位、調(diào)整代碼中的地址等威蕉,事實上第二步是鏈接過程的核心刁俭,特別是重定位過程
4.2 符號解析與重定位
4.2.1 重定位
- 代碼里面使用的地址都是虛擬地址
4.2.2 重定位表
- 鏈接器需要進行重定位,哪些指令需要重定位韧涨,怎么重定位牍戚,需要依賴重定位表,他在ELF文件中是一個或多個段
虑粥,有時候也叫重定位段 - 通俗的理解a.o文件引用外部的符號如孝,都需要依賴重定位表進行重定位
4.2.3 符號解析
- 重定位過程伴隨著符號解析過程,當鏈接器需要對某個符號進行重定位時娩贷,它就要確定這個符號的目標地址
4.3 COMMON塊
- 鏈接器本身并不支持符號的類型第晰,變量類型對i 鏈接器來說是透明的,它只知道一個符號的名字,并不知道類型是否一致茁瘦,所以會出現(xiàn)多個強符號相同報錯的現(xiàn)象
4.4 C++相關(guān)問題
- C++一些語言特性必須由編譯器和鏈接器共同支持才能完成
4.4.1 重復代碼消除
- C++編譯期提供了一個編譯選項叫函數(shù)級別鏈接品抽,這個選項的作用是所有函數(shù)都單獨保存在一個段里面,當鏈接器需要用到某個函數(shù)時候甜熔,就將它合并到輸出文件中圆恤,對那些沒有用到的函數(shù)則拋棄掉,好處是減少輸出文件的大小纺非,壞處是編譯和鏈接過程會變慢哑了,編譯器會計算各個函數(shù)的依賴關(guān)系
4.4.2 全局構(gòu)造與析構(gòu)
- 一般的C/C++程序是從main開始,main函數(shù)結(jié)束而結(jié)束
- 但是在main函數(shù)調(diào)用之前烧颖,為了讓程序順利執(zhí)行弱左,要初始化進程執(zhí)行環(huán)境
4.4.3 C++與ABI
- ABI:采用同樣的目標文件格式、擁有同樣的符號修飾標準炕淮、變量的內(nèi)存分布方式相同拆火、函數(shù)的調(diào)用方式相同,等等涂圆。其中我們把符號修飾標準们镜、變量內(nèi)存布局、函數(shù)調(diào)用方式等這些跟可執(zhí)行代碼二進制兼容性相關(guān)的內(nèi)容叫ABI
- API往往是源代碼級別的接口润歉,ABI往往是二進制層面的接口
4.5 靜態(tài)庫鏈接
- 靜態(tài)庫可以簡單的看成一組目標文件的集合
4.6 鏈接過程控制
4.6.2 最小的程序
todo