參考:《程序員的自我修養》第4章
鏈接器:把目標文件按一定的方式鏈接到一起,形成一個可執行文件。
鏈接器命令
ld file1.o file2.o -e main -o exc // 將目標文件file1.o,file2.o以main函數作爲程序入口,合成可執行文件exc。
注:如果在鏈接的時候出現 undefined reference to "__stack_chk_fail"的錯誤,可在編譯的時候加上選項 -fno-stack-protector。
即gcc -c -fno-stack-protector file1.cfile2.c
objdump -r file.o // 查看文件的重定位表
ar -t libfile.a // 查看庫文件中包含了哪些目標文件
ld -verbose // 查看ld默認鏈接腳本
1. 鏈接
通常按照相似段合併的方式來分配空間。該鏈接過程分爲兩步:
- 空間和地址分配:掃描所有目標文件,獲取各個段的長度、屬性和位置,並將輸入目標文件中的符號表中的所有符號定義和符號引用收集起來,放到全局符號表。該過程鏈接器獲得了所有目標文件的段長度,將其合併,計算出輸出文件中各個段合併後的長度和位置,並建立映射關係。
- 符號解析與重定位:根據上一步收集的段信息、重定位信息,進行符號解析和重定位,調整代碼的地址。
2. 空間與地址分配
鏈接前後地址分配情況:
- VMA:Virtual Memory Address,虛擬地址。在鏈接前,由於未分配虛擬地址,所以虛擬地址全是0。
- LMA:Load Memory Address,加載地址。
3. 符號解析與重定位
目標文件反彙編
可執行文件反彙編
重定位表:記錄了重定位相關信息
- 重定位入口:每個要被重定位的地方。
- 重定位入口偏移:對於可重定位文件而言,表示該重定位入口要修正的位置的第一個字節相對於段的起始偏移。對於可執行或共享文件而言,表示該重定位入口要修正的位置的第一個字節的虛擬地址。
- 重定位入口的類型和符號
4. COMMON塊
由於編譯器允許不同類型的弱符號存在,但是鏈接器無法判斷各個符號類型是否一致,因此需要COMMON塊機制。
- COMMON塊機制:當不同的目標文件需要的COMMON塊大小不一致時,以最大塊爲準。
- COMMON塊機制是針對弱符號而言的。若其中一個符號爲強符號,按照強弱符號規則,則符號所佔空間與強符號相同。如果連接過程中有弱符號大小大於強符號,則鏈接器會報warning。
編譯器爲什麼不直接把未初始化的全局變量當作未初始化的局部靜態變量,爲它在BSS段分配空間,而是標記爲COMMON類型的變量?
- 因爲編譯器無法確定弱符號的所佔空間的大小。
如何將未初始化的全局變量以非COMMON塊處理?
- gcc選項 -fno-common
- 使用__attribute__,即__attribute__((nocommon))
5. 靜態庫鏈接
靜態庫,多個目標文件經過壓縮打包後形成的文件。
鏈接器在鏈接靜態庫時,是以目標文件爲單位。
6. 鏈接過程控制
鏈接器默認鏈接規則是對目標文件進行鏈接。但對於某些特殊的程序,如操作系統內核、沒有操作系統的程序(引導程序Boot Loader,嵌入式程序)等,需要指定輸出文件各個段的虛擬地址、段名稱、段存放順序等。
可使用鏈接腳本控制鏈接過程。
- ld -T link.script:指定某個腳本爲連接腳本。
- ld使用手冊