《程序員自我修養》--動態鏈接

定義和基本思想

動態鏈接英文是Dynamic Linking需要解決空間浪費和更新困難這兩個問題最簡單的辦法就是把程序的模塊相互劃分開來,形成獨立的文件,而不再將他們靜態的鏈接在一起。簡單地講,就是不對那些組成程序的目標文件進行鏈接,等到程序要運行時才進行鏈接。也就是說,把鏈接這個過程推遲到了運行時再進行,這就是動態鏈接(Dynamic Linking)的基本思想

補充理解

普通可執行程序和動態鏈接庫都包含指令和數據,在使用動態鏈接庫時,程序被劃分爲了主要模塊(Program1)和動態鏈接庫(lib.so),實際上他們都可以看成一個程序的以各模塊,提到程序模塊,可以指的是程序主模塊,也可以指動態鏈接庫。

編譯鏈接的小例子

/*Program1.c*/
#include "lib.h"
int main()
{
    foobar(1);
    return 0;
}

/*Program2.c*/
#include "lib.h"
int main()
{
    foobar(2);
    return 0;
}

/*lib.c*/
#include <stdio.h>
void foobar(int i)
{
    printf("libc : %d\n",i);
}

/*lib.h*/
#ifndef LIB_H
#define LIB_H
void foobar(int i);

#endif

lib.c編譯命令:

gcc -fPIC -shared -o lib.so lib.c

編譯鏈接Program1.c和Program2.c的命令:

gcc -o Program1 Program1.c ./lib.so
gcc -o Program2 Program2.c ./lib.so

動態鏈接過程如下
這裏寫圖片描述

地址無關代碼

由於共享對象在編譯時不能假設自己在進程虛擬地址空間中的位置,但是可執行文件基本可以確定自己在進程虛擬地址空間中的起始位置因此需要考慮共享對象在任意地址加載。
解決問題的辦法1:裝載時重定位
在鏈接時,對所有絕對地址的引用不作重定位,把這一步推到裝載時再完成。但是動態模塊被裝載映射到虛擬空間後,指令部分在多個進程之間共享,但是裝載時重定位的做法會修改指令,沒辦法同一個指令多個進程共享,因爲指令被重定位以後,對於每個進程時不同的,失去節省內存的優勢。
優點:運行速度快
缺點:無法共享代碼

解決問題的辦法2:產生地址無關代碼
這需要將4中引用方式都實現地址無關性,4種引用方式是:
1 模塊內部的函數調用和調轉
2 模塊內部的數據訪問
3 模塊外部的函數調用調轉
優點:代碼段在各個進程之間共享
缺點:運行速度稍慢

延遲綁定

基本思想:程序開始執行時,模塊間的函數調用都沒有進行綁定(符號查找,重定位等),在函數第一次被用到時候才進行綁定。

動態鏈接的基本步驟

動態鏈接器自舉
自舉:是一段具有條件限制的啓動代碼,條件是:
1.動態鏈接器作爲一個共享對象,不依賴於任何其他共享對象
2.動態鏈接器本身所需要的全局和靜態變量的重定位由它自己完成。

裝載共享對象
完成自舉以後,動態鏈接器將可執行文件和鏈接器本身的符號表都合併到一個符號表當中,稱他爲全局符號表。在鏈接器尋找可執行文件所依賴的共享對象的時候,一般按照廣度優先,找出所有依賴的共享對象,當一個新的共享對象被裝載進來時候,他的符號表會被合併到全局符號表中。

重定位和初始化
當動態鏈接器擁有進程的全局符號表時,鏈接器重新遍歷可執行文件和每個共享對象的重定位表,將它們在GOT中的每一個需要重定位的位置進行修正。
重定位完成以後,如果共享對象擁有”.init”段,動態鏈接器會執行這段代碼實現共享對象特有的初始化特徵。

開始執行
當重定位和初始化完成以後,所有的準備工作都宣告完成,所需要的共享對象也都已經裝載並且鏈接完成,此時,進程的控制權轉交給程序的入口並且開始執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章