徹底理解鏈接器

ps:本文轉(zhuǎn)載自(確切來說,是關(guān)于其中4篇鏈接器博文的整理)
https://blog.csdn.net/github_37382319/article/details/82749205?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-4.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-4.control

什么是鏈接器(Linker)

??首先是鏈接器的本質(zhì),鏈接器本質(zhì)上也是一個程序,本質(zhì)上和我經(jīng)常使用的普通程序沒什么不同。
?? 最后是鏈接器的輸出,鏈接器在將目標(biāo)文件打包處理后,生成或者可執(zhí)行文件或庫等。
??鏈接器的作用有點類似于我們經(jīng)常使用的壓縮軟WinRAR(Linux下是tar),壓縮軟件將一堆文件打包壓縮成一個壓縮文件,而鏈接器和壓縮軟件的區(qū)別在于鏈接器是將多個目標(biāo)文件打包成一個文件而不進(jìn)行壓縮

鏈接器的工作過程

??首先,鏈接器對給定的目標(biāo)文件或庫的集合進(jìn)行符號決議以確保模塊間的依賴是正確的。
??其次,鏈接器將給定的目標(biāo)文件集合進(jìn)行拼接打包成需要的庫或最終可執(zhí)行文件。
??最后,鏈接器對鏈接好的庫或可執(zhí)行文件進(jìn)行重定位。

符號決議

??符號決議有時候也被叫做符號綁定,名稱決議;決議更傾向于靜態(tài)鏈接,而綁定更傾向于動態(tài)鏈接。在這個過程當(dāng)中,鏈接器需要做的工作就是確保所有目標(biāo)文件中的符號引用都有唯一的定義。

目標(biāo)文件里有什么

  • 代碼部分:指的是計算機可以執(zhí)行的機器指令,也就是源文件中定義的所有函數(shù)。
  • 數(shù)據(jù)部分:源文件中定義的全局變量。

那為什么局部變量沒有放到目標(biāo)文件的數(shù)據(jù)段當(dāng)中呢?

??這是因為局部變量是函數(shù)私有的,局部變量只能在該函數(shù)內(nèi)部使用,所以函數(shù)私有的局部變量被放在了代碼段中,作為機器指令的操作數(shù)。
??編譯器在遇到外部定義的全局變量或者函數(shù)時只要能在當(dāng)前文件找到其聲明,編譯器就認(rèn)為編譯正確。而尋找使用變量定義的這項任務(wù)就被留給了鏈接器。鏈接器的其中一項任務(wù)就是要確定所使用的變量要有其唯一的定義。但為了讓鏈接器工作的輕松一點編譯器還是多做了一點工作的,這部分工作就是 符號表(Symbol table)。

符號表(Symbol table)

??編譯器在編譯過程中每次遇到一個全局變量或者函數(shù)名都會在符號表中添加一項,最終編譯器會統(tǒng)計一張符號表。

static用法:如果你認(rèn)為一個變量只應(yīng)該被當(dāng)前文件使用而不暴露給外部,那么你就可以使用static關(guān)鍵字修飾一下。

本質(zhì)上整個符號表只是想表達(dá)兩件事:

  • 我能提供給其它文件使用的符號
  • 我需要其它文件提供給我使用的符號

符號表存放在哪里

obj file

靜態(tài)鏈接下可執(zhí)行文件的生成

objToExe

??可執(zhí)行文件區(qū)別于目標(biāo)文件的地方在于,可執(zhí)行文件有一個入口函數(shù),這個函數(shù)也就是我們在C語言當(dāng)中定義的main函數(shù),main函數(shù)在執(zhí)行過程中會用到所有可執(zhí)行文件當(dāng)中的代碼和數(shù)據(jù)。main函數(shù)被操作系統(tǒng)調(diào)用執(zhí)行。

??你可以把可執(zhí)行文件生成的過程想象成裝訂一本書,一本書中通常有好多章節(jié),這些章節(jié)是你自己寫的,且一本書不可避免的要引用其它著作。靜態(tài)鏈接這個過程就好比不但要裝訂你自己寫的文章,而且也把你引用的其它人的著作也直接裝訂進(jìn)了你的書里。這些工作完成后,只需要按一下訂書器,一本書就制作完成啦。
??在這個比喻中,你寫的各個章節(jié)就好比你寫的代碼,引用的其它人的著作就好比使用其它人的靜態(tài)庫,裝訂成一本書就好比可執(zhí)行文件的生成。

動態(tài)庫

??將靜態(tài)鏈接生成可執(zhí)行文件的過程比作了裝訂一本書,靜態(tài)鏈接將引用的其它人的著作也裝訂到了書里,而動態(tài)鏈接可以想象成作者僅僅在引用的地方寫了一句話,比如引用了“xxx”,那么作者就在引用的地方寫上“此處參考“xxx”,那么讀者在讀到該處就會自行查找相應(yīng)內(nèi)容,其該過程就是動態(tài)鏈接的基本思想了。
??因此我們就可以知道helloworld程序中的printf函數(shù)到底是在哪里定義的,答案就是該函數(shù)是在libc.so當(dāng)中定義的,Linux下編譯鏈接生成可執(zhí)行文件時會默認(rèn)動態(tài)鏈接libc.so(Windows同理),使用ldd命令可查看可執(zhí)行文件的依賴項(libc.so)。因此雖然你從沒有看到過printf的定義也可以正確的使用這個函數(shù)。

動態(tài)鏈接

??動態(tài)鏈接可以在兩種情況下被鏈接使用,分別是load-time dynamic linking(加載時動態(tài)鏈接) 以及 run-time dynamic linking(運行時動態(tài)鏈接)。
??把加載理解為程序從磁盤復(fù)制到內(nèi)存的過程,加載時動態(tài)鏈接就出現(xiàn)在這個過程Windows下比較常見的啟動錯誤問題,就是因為沒有找到依賴的動態(tài)庫,如下圖:


can't find dll
加載時動態(tài)鏈接
  • 階段一,將動態(tài)庫信息寫入可執(zhí)行文件。在編譯鏈接生成可執(zhí)行文件時,需要將使用的動態(tài)庫加入到鏈接選項當(dāng)中;
  • 階段二,加載可執(zhí)行文件時依據(jù)動態(tài)庫信息進(jìn)行動態(tài)鏈接。

??為加深對加載時動態(tài)鏈接這個過程的理解,類比一下:沿用前幾節(jié)讀書的例子,我們正在讀的書中引用了“xxx”,那么加載時動態(tài)鏈接就好比讀者開始準(zhǔn)備讀這本書的時候(還沒有真正的讀)就把所有該書當(dāng)中引用的資料著作都找齊放到一旁準(zhǔn)備查看。在這個類比當(dāng)中,開始讀書前的準(zhǔn)備工作就好比加載時動態(tài)鏈接。

運行時動態(tài)鏈接

??不需要在編譯鏈接時提供動態(tài)庫信息,也就是說,在可執(zhí)行文件被啟動運行之前,可執(zhí)行文件對所依賴的動態(tài)庫信息一無所知,只有當(dāng)程序運行到需要調(diào)用動態(tài)庫所提供的代碼時才會啟動動態(tài)鏈接過程。
??運行時動態(tài)鏈接就好比直接拿起一本書開始看,看到有引用的參考文獻(xiàn)時再去找該資料。運行時動態(tài)鏈接更像是我們平時讀書時的樣子。
PS:在編譯鏈接過程中,可以同時使用動態(tài)庫以及靜態(tài)庫。這兩種庫的使用并不沖突,那么在這種情況下生成的可執(zhí)行文件中,可執(zhí)行文件中包含了靜態(tài)庫的數(shù)據(jù)和代碼,以及動態(tài)庫的必要信息。

動態(tài)庫vs靜態(tài)庫

動態(tài)庫優(yōu)點:

??方便了程序升級和bug修復(fù)。如果我們修改了動態(tài)庫的代碼,只需重新編譯動態(tài)庫即可,因為可執(zhí)行文件當(dāng)中僅僅保留了動態(tài)庫的必要信息,重新編譯動態(tài)庫后這些必要都信息不會改變(只要不修改動態(tài)庫的名字和動態(tài)庫導(dǎo)出的供可執(zhí)行文件使用的函數(shù)),編譯好新的動態(tài)庫后只需要簡單的替換原有動態(tài)庫,下一次運行程序時就可以使用新的動態(tài)庫了。我們平時使用都客戶端程序,比如:QQ,輸入法,播放器,都利用了動態(tài)庫的這一優(yōu)點,原因就在于方便升級以bug修復(fù),只需要更新相應(yīng)的動態(tài)庫就可以了。
??插件的實現(xiàn)。我們知道動態(tài)鏈接可以出現(xiàn)在運行時(run-time dynamic link),動態(tài)鏈接的這種特性可以用于擴展程序能力,那么如何擴展呢?你肯定聽說過一樣神器,沒錯,就是插件。你有沒有想過插件是怎么實現(xiàn)的?實現(xiàn)插件時,我們只需要實現(xiàn)幾個規(guī)定好的幾個函數(shù),我們的插件就可以運行了,可這是怎么做到的呢,答案就在于運行時動態(tài)鏈接,可以將插件以動態(tài)的都方式實現(xiàn)。我們知道使用運行時動態(tài)鏈接無需在編譯鏈接期間告訴鏈接器所使用的動態(tài)庫信息,可執(zhí)行文件對此一無所知,只有當(dāng)運行時才知道使用什么動態(tài)庫,以及使用了動態(tài)庫中哪些函數(shù),但是在編譯鏈接可執(zhí)行文件時又怎么知道插件中定義了哪些函數(shù)呢,因此所有的插件實現(xiàn)函數(shù)必須都有一個統(tǒng)一的格式,程序在運行時需要加載所有插件(動態(tài)庫),然后調(diào)用所有插件的入口函數(shù)(統(tǒng)一的格式),這樣我們寫的插件就可以被執(zhí)行起來了。
??多語言編程。我們知道使用Python可以快速進(jìn)行開發(fā),但Python的性能無法同C/C++相比(因為Python是解釋型語言),有沒有辦法可以兼具Python的快速開發(fā)能力以及C/C++的高性能呢,答案是可以的,我們可以將C/C++代碼編譯鏈接成動態(tài)庫,這樣python就可以直接調(diào)用動態(tài)庫中的函數(shù)了。不但Python,Perl以及Java等都可以通過動態(tài)庫的形式調(diào)用C/C++代碼。動態(tài)庫的使用使得同一個項目不同語言混合編程成為可能,而且動態(tài)庫的使用更大限度的實現(xiàn)了代碼復(fù)用。

動態(tài)庫缺點:

??動態(tài)庫中的代碼是地址無關(guān)代碼(Position-Idependent Code,PIC,因此在使用動態(tài)庫中的代碼時程序要多做一些工作。
??動態(tài)鏈接下的可執(zhí)行文件不可以被獨立運行(這里討論的是加載時動態(tài)鏈接,load-time dynamic link),換句話說就是,如果沒有提供所依賴的動態(tài)庫或者所提供的動態(tài)庫版本和可執(zhí)行文件所依賴的不兼容,程序是無法啟動的。動態(tài)庫的依賴問題會給程序的安裝部署帶來麻煩。

靜態(tài)庫優(yōu)點:

??靜態(tài)鏈接下的可執(zhí)行文件由于不依賴任何庫,因為部署非常方便,僅僅用一個新的可執(zhí)行文件進(jìn)行覆蓋就可以了,因此極大的簡化了系統(tǒng)部署以及升級。

靜態(tài)庫缺點:

??會導(dǎo)致可執(zhí)行文件過大,且多個程序靜態(tài)鏈接同一個靜態(tài)庫的話會導(dǎo)致磁盤浪費的問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容