前言:繼續(xù)程序的鏈接
之前我們說(shuō)過(guò)了「符號(hào)解析」,「符號(hào)解析」的本質(zhì)就是:建立定義與引用之間的聯(lián)系
而「重定位」的本質(zhì)就是:合并所有程序,程序中引用符號(hào)的地址修改為定義符號(hào)的絕對(duì)地址
重定位的全部流程
- 合并相同的節(jié)(數(shù)據(jù)節(jié)和代碼節(jié))
-
對(duì)「定義的符號(hào)」進(jìn)行重定位
確定「定衣的符號(hào)」在虛擬空間的絕對(duì)地址逼龟,完成這一步以后,每個(gè)全局或局部變量都可確定地址
-
對(duì)「引用符號(hào)」進(jìn)行重定位
用上述「定義符號(hào)」的絕對(duì)地址修改 .text 和 .data 節(jié)中「引用符號(hào)」的地址
鏈接器是如何完成重定位的
一切要從匯編階段說(shuō)起:
當(dāng)程序編譯完成后,匯編能夠生成二進(jìn)制可重定位文件。生成的可重定位文件包含兩個(gè)節(jié)(IA-32):
- .rel.text 函數(shù)
- .rel.data 數(shù)據(jù)
每一個(gè)節(jié)都是一個(gè)表彼哼,每一個(gè)表包含多個(gè)表項(xiàng),每個(gè)表項(xiàng)的結(jié)構(gòu)如下:
typedef struct {
Elf32_Addr r_offset;
Elf32_Word r_info;
}ELF32_Rel;
- r_offset 指出當(dāng)前需要重定位的位置相對(duì)于所在節(jié)起始位置的偏移量湘今。若重定位的是變量的位置敢朱,則所在節(jié)是 .data 節(jié),若重定位的是函數(shù)的位置摩瞎,則所在節(jié)是 .text 節(jié)
- r_info 指出當(dāng)前的符號(hào)在符號(hào)表中的索引以及重定位類型蔫饰。前 24 位是符號(hào)索引(r_sym),后 8 位是重定位類型(r_type愉豺,在后面會(huì)指出)
當(dāng)鏈接器符號(hào)解析(靜態(tài))結(jié)束以后,E 集合中就有了所有目標(biāo)文件茫因。只需把需要重地位的符號(hào)修改成定義符號(hào)的地址就好了
在 IA-32 架構(gòu)中有兩種重定位的方式蚪拦,對(duì)應(yīng)著 r_type
R_386_PC32
R_386_32
自己這一塊有一些疑惑,也不細(xì)說(shuō)了冻押。只知道有 PC 的那個(gè)就是根據(jù) PC 重定位驰贷,沒(méi)有 PC 的那個(gè)方法,就是虛擬空間的絕對(duì)地址洛巢。
最后
再完整的說(shuō)一下符號(hào)靜態(tài)鏈接的全過(guò)程
- 對(duì)每一個(gè)輸入文件來(lái)說(shuō)括袒,首先判斷是不是庫(kù)文件。
-
如果不是庫(kù)文件稿茉,就是目標(biāo)文件 f锹锰。就能把目標(biāo)文件放入 E 中,根據(jù) f 中未解析符號(hào)和定義符號(hào)判斷后分別放入 U漓库、D 中
比如說(shuō)恃慧,f 中有一個(gè)未解析符號(hào) k,如果 D 中存在對(duì)它的定義渺蒿,那么就可以建立聯(lián)系痢士。如果沒(méi)有就放入 U 中。
- 如果是庫(kù)文件茂装,會(huì)試圖把所有 U 中的符號(hào)與庫(kù)文件中的符號(hào)匹配怠蹂,匹配上了就從 U 放入 D 中善延。并把匹配上的模塊放入 E 中。一直重復(fù)直到 U D 不再變化城侧。庫(kù)文件剩下的內(nèi)容直接就不管了易遣。
- 如果往 D 中放入了一個(gè)已經(jīng)存在的符號(hào)或者掃描完所有文件后 U 還是非空,則鏈接器會(huì)停止并報(bào)錯(cuò)赞庶。否則將 E 中內(nèi)容經(jīng)過(guò)重定位后合并训挡,生成可執(zhí)行文件
如果我真的去實(shí)現(xiàn)一個(gè)編譯器的時(shí)候。我再完全弄懂里面的道道