链接器(linker)的作用——CSAPP第7章读书笔记
首先說說我為什么要去讀這一章。這個(gè)學(xué)期開OS的課,在Morden Operating System上讀到和Process有關(guān)的內(nèi)容時(shí)看到這樣一句話:“Process is fundamentally a container that holds all the information needed to run a program.”當(dāng)時(shí)瞬間就想到了之前在csapp上看的模棱兩可的“目標(biāo)可執(zhí)行文件”這個(gè)概念,于是重新又把它的第7章給讀了一遍。
?
要理解linker的作用,首先要搞明白他在整個(gè)計(jì)算機(jī)系統(tǒng)中處于一個(gè)什么樣的位置。
關(guān)于一個(gè)程序是怎樣從碼農(nóng)們手撕的代碼變成內(nèi)存中能跑起來的程序這個(gè)過程就不再過多的敘述,這篇文章只是著重的去講一下有關(guān)linker的這一部分。我們可以看到,linker的接受的輸入是若干個(gè).o文件,簡(jiǎn)單的說就是經(jīng)過匯編器編譯后生成的機(jī)器碼,學(xué)名叫“relocatable object file(可重定位的目標(biāo)文件)”,概念相近的稱呼也有“module(模塊)”。而匯編器的輸出,是一個(gè)名叫“executable object program(目標(biāo)可執(zhí)行文件)”的二進(jìn)制文件,這個(gè)文件的特征就是可以直接拷貝到內(nèi)存中不需做任何的更改便可以運(yùn)行。那么我們研究linker的作用是什么就可以從這里入手——為了構(gòu)造最終的目標(biāo)可執(zhí)行文件,他需要對(duì)輸入若干可重定位的目標(biāo)文件做哪些事情?
linker的作用主要有兩個(gè):
(1)符號(hào)解析(symbol resolution):將每個(gè)符號(hào)的定義和每個(gè)符號(hào)的引用聯(lián)系起來。(就是讓系統(tǒng)明白,當(dāng)這個(gè)程序run的時(shí)候,遇到的具體的變量或函數(shù)名,他們到底來自哪個(gè)文件的定義?是自己這個(gè)?還是其他一起輸入linker的文件?)
(2)重定位(relocation):把每個(gè)符號(hào)定義與存儲(chǔ)器中的一個(gè)具體位置聯(lián)系起來,然后修改所有對(duì)這些符號(hào)的引用,使得它們指向這個(gè)存儲(chǔ)器的位置,從而重定位這些節(jié)。(在取得了每個(gè)符號(hào)的引用和定義的連接之后,要把符號(hào)的定義在存儲(chǔ)器中綁定一個(gè)具體的地址)
書中對(duì)符號(hào)的解釋不是太清楚,至少我一開始的時(shí)候沒太理解這個(gè)概念,在這兒結(jié)合書本的內(nèi)容我用自己的話來概括下我對(duì)這個(gè)概念的理解。“符號(hào)”可以分為3類:
1、由該模塊定義的并且能被其他模塊引用的“全局符號(hào)”。這里的“全局符號(hào)”對(duì)應(yīng)于C語言中的非靜態(tài)的函數(shù)和全局變量。
2、由其他模塊定義的由該模塊引用的“全局符號(hào)”。解釋同上
3、由該模塊定義的并且不能被其他模塊引用的“全局符號(hào)”。對(duì)應(yīng)于C語言中的靜態(tài)變量,即static變量。static關(guān)鍵字相當(dāng)于C語言中的“private”,即只能被自己這個(gè)文件(模塊)使用的全局變量。
應(yīng)當(dāng)注意的是這里的變量全是全局變量而不是函數(shù)內(nèi)部的私有變量,私有變量由運(yùn)行時(shí)stack存儲(chǔ)管理,linker對(duì)她并不感興趣:)
?
那么在了解了符號(hào)的概念之后,要想具體的了解linker對(duì)可重定位的目標(biāo)可執(zhí)行文件做的一些事情,就要了解relocatable object file的一些結(jié)構(gòu)(他是怎么記錄自身的各種符號(hào)信息的?)對(duì)不對(duì)?
大家第一次看到這個(gè)圖不要害怕,其實(shí)這就是匯編器(Assembler)將編譯器處理的源代碼文件進(jìn)行進(jìn)一步的編譯或者說匯編之后形成的可重定位的目標(biāo)可執(zhí)行文件。這個(gè)文件的一個(gè)個(gè)小格子就是一個(gè)個(gè)的“節(jié)(section)”,他們存放該program的各種信息,在這里我只會(huì)解釋幾個(gè)我認(rèn)為對(duì)理解linker作用很有必要的section。
.text:已編譯程序的機(jī)器代碼。
.data:已初始化的全局C變量。
.bss:未初始化的全局C變量。在這里符號(hào)只是一個(gè)占位符,它不占用任何的內(nèi)存空間。
.symtab:一個(gè)符號(hào)表,存放在程序中定義和引用的函數(shù)和全部全局變量的信息。
.rel.test:存放代碼的重定位條目(relocation entry)。
.rel.data:存放數(shù)據(jù)的重定位條目。
?
以上都屬于本章的基礎(chǔ)知識(shí)鋪墊部分,理解了上述內(nèi)容,就可以很容易的理解linker對(duì)可重定位的目標(biāo)可執(zhí)行文件所做的操作了。
1、符號(hào)解析
linker解析符號(hào)的方法是將每個(gè)符號(hào)的引用與所有輸入的relocatable object file中的.symtab節(jié)中所有的符號(hào)定義中確定的一個(gè)聯(lián)系起來。
1.1鏈接器如何解釋多重定義的全局符號(hào)?
對(duì)于定義和引用都在一個(gè)module中的符號(hào),linker的操作很簡(jiǎn)單,不需要指來指去改來改去;而真正要深入探討的操作是對(duì)定義和引用不在同一個(gè)文件中的符號(hào),尤其是當(dāng)尋找到的符號(hào)定義有重名時(shí)。對(duì)此linker的做法是:
(1)定義強(qiáng)符號(hào)和弱符號(hào)的概念。函數(shù)和已初始化的變量是強(qiáng)符號(hào),為初始化的變量是弱符號(hào)。
(2)設(shè)定規(guī)則。當(dāng)有多重定義沖突的時(shí)候,linker遵循的規(guī)則是:
one:不允許有多個(gè)強(qiáng)符號(hào)定義
two:如果有一個(gè)強(qiáng)符號(hào)和多個(gè)弱符號(hào)定義,那么選擇強(qiáng)符號(hào)定義
three:如果有多個(gè)弱符號(hào)定義,那么隨便選擇一個(gè)
1.2與靜態(tài)庫鏈接
為什么會(huì)有“靜態(tài)庫”(static libraries)這個(gè)概念?
首先在C語言編程中,我們需要實(shí)現(xiàn)豐富的功能,就要使用各種各樣的函數(shù)接口。以ANSI C為例,它定義了一組廣泛的標(biāo)準(zhǔn)I/O、字符串操作和整數(shù)數(shù)學(xué)函數(shù),例如atoi、printf、scanf、strcpy、rand。他們?cè)趌ibc.a庫中,對(duì)每個(gè)C程序來說都是可用的。如果不使用靜態(tài)庫,我們看看編程開發(fā)人員可以用什么其他的辦法來向用戶提供這些函數(shù)。
一種實(shí)現(xiàn)的方法是讓編譯器直接辨認(rèn)出對(duì)函數(shù)的調(diào)用,并直接生成相應(yīng)的代碼——這顯然是不可行的,C語言中有大量的函數(shù),這樣做顯然會(huì)使得編譯器的設(shè)計(jì)變得相當(dāng)復(fù)雜,每次添加、修改、刪除一個(gè)函數(shù)時(shí),都需要一個(gè)新的編譯器版本。雖然對(duì)于編程人員而言這樣是十分方便的,因?yàn)樗械臉?biāo)準(zhǔn)函數(shù)都是直接可用的。
另一種實(shí)現(xiàn)的方法是將所有的這些函數(shù)放到一個(gè)單獨(dú)的可重定位的目標(biāo)可執(zhí)行文件中,它的優(yōu)點(diǎn)是將編譯器的設(shè)計(jì)與標(biāo)準(zhǔn)函數(shù)的實(shí)現(xiàn)分離開來,在一定程度上仍然便利編程人員。但是這樣做的缺點(diǎn)卻是每次運(yùn)行程序的時(shí)候都要將該裝載函數(shù)的rof文件copy到內(nèi)存中去,而這樣是很浪費(fèi)內(nèi)存空間的。而且同樣將這么一大批函數(shù)賽到一個(gè)文件中,每次的維護(hù)都要重現(xiàn)編譯整個(gè)源文件,這又是相當(dāng)大的一個(gè)工作量。
何為靜態(tài)庫?
在Unix中,靜態(tài)庫以archive這種特殊的文件格式存在于磁盤中,是一組連接起來的relocatable object file的集合。
1.3鏈接器如何使用靜態(tài)庫來解析引用
維護(hù)一個(gè)基于(U,E,D)三個(gè)集合的算法
?
2、重定位
在這個(gè)過程中,將合并模塊并為每個(gè)符號(hào)分配運(yùn)行時(shí)的地址。重定位由兩個(gè)步驟組成:
在這里有一個(gè)需要理解的概念是重定位條目(relocation entry)。在匯編器生成一個(gè)可重定位的目標(biāo)模塊時(shí),當(dāng)遇到UNDEFINED的符號(hào),即不知道該數(shù)據(jù)或代碼最終該存放到存儲(chǔ)器的什么位置時(shí),它就會(huì)為該符號(hào)生成一個(gè)重定位條目,即之前介紹的可重定位目標(biāo)文件中的.rel.text和.rel.data兩個(gè)表所記錄的內(nèi)容。
轉(zhuǎn)載于:https://www.cnblogs.com/immortal-worm/p/5819036.html
總結(jié)
以上是生活随笔為你收集整理的链接器(linker)的作用——CSAPP第7章读书笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LinkedList模拟队列和堆栈
- 下一篇: 域名解析的记录类型区别