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