天天看點

裝載和動态連結

一、程序虛拟位址空間

1、程式是一個靜态的概念,他是一些預編譯好的指令資料集合的一個檔案。程序是一個動态的概念是一個程式運作時的過程。​

每個程序都有自己獨立的虛拟位址空間,虛拟位址空間的大小有硬體平台決定,具體說是CPU位數決定的,32位的平台具體大小是0-4G。從程式員的角度來講可以通過判斷指針所占用的空間來判斷虛拟位址空間,C語言指針大小的位數與虛拟位址空間的位數相同。

2、裝載

程式運作​時所需要的資料和指令必須要在記憶體中才能夠正常運作。動态裝載就是程式用到那部分資料就将其裝載到記憶體中,不用的部分留在磁盤中。動态裝載的方法分為覆寫裝入和頁映射。思想是程式用到那個子產品就将哪個子產品裝入記憶體,如果不用就暫時不裝入,存放在磁盤中。

覆寫裝載是将資料和指令按照記憶體使用的大小進行分類,将不需要同時使用的部分裝入相同的記憶體位址同時根據大小合理分類保證程式能正常運作。

頁映射是将記憶體和磁盤中的資料按照頁為機關劃分為若幹頁,以後裝載和操作的機關就是頁​

二、程序的建立

1、建立一個獨立的虛拟位址空間

通過一組頁映射函數将虛拟位址空間的各個頁映射至實體位址空間。​

2、讀取可執行檔案的檔案頭,并建立可執行檔案和虛拟位址空間的映射關系。

建立虛拟位址空間和可執行檔案之間的關系。​

3、将CPU的指令寄存器設定成CPU的可執行程式的入口位址,啟動程式。

​至此程序建立完成,程式開始執行。但是可執行程式的指令和資料并沒有真正的裝載到記憶體中,作業系統隻是根據可執行檔案頭部的資訊建立了可執行檔案和程序的虛拟記憶體之間的關系而已。随着程式的執行,頁錯誤不斷産生,作業系統會配置設定實體記憶體來滿足程序執行的需要。

6、核心加載ELF檔案的過程

(1)檢查ELF可執行檔案格式的有效性,比如魔數,程式頭表中的段的數量

(2)尋找動态連結的“.interp”段,設定動态連結器路徑

(3)根據ELF可執行檔案的程式頭表的描述,對ELF檔案進行映射,比如代碼,資料,隻讀資料

(4)初始化ELF程序環境,比如寄存器的位址

(5)将系統調用的傳回位址修改成ELF可執行檔案的入口點,靜态連結的ELF檔案就是檔案頭中的e_entry所指位址,動态連結的檔案程式的入口點就是動态連結器。

Q:可執行檔案啟動時如何确定動态檔案時動态連結還是動态加載?

A:對可執行檔案而言,隻需要檢視“.interp”“.dynamic”段确定需要動态連結的庫。若沒有這個段,作業系統在執行時會認為這個檔案不需要動态連結器的支援,因而會直接執行這個檔案。通過下面的指令可以檢視fun.so檔案沒有.interp段:

​readelf -l ./fun.so | grep interp

動态連結器的的位置由ELF檔案中的“.interp”段決定,而段“.dynamic”為動态連結提供了:依賴哪些共享對象、動态連結符号表的位置,動态連結重定位表的位置、共享對象初始化代碼的位址等。動态連結過程需要動态符号表來确定函數的定義和引用關系,還需要重定位表來修正導入符号的引用。初始化完成後堆棧中儲存了動态連接配接器所需要的一些輔助資訊數組(其中包括程式入口位址,程式表頭位址,程式表頭項數及大小)。動态連結庫最後被映射到程序位址空間的共享庫區域段。完成重定位和初始化後,所有準備工作結束,所需要的共享對象也都已經裝載并且連結完成。最後将程序的控制權轉交給dyn程式的入口并開始執行。至于編譯可執行檔案時如何判斷是否需要生産“.interp”“.dynamic”段,則根據代碼中符号的引用,是否有導入符号。是以動态連結的可執行檔案編譯時需要有動态庫的頭檔案對庫中的符号進行聲明,然後編譯時需要指明庫的路徑(當然也可以讓編譯器在預設路徑查找)。調用動态加載的動态庫在編譯可執行檔案時不需要頭檔案也不需要指明庫路徑。

PS:靜态庫連結時按目标檔案進行的(.o檔案),因為靜态庫隻是一些目标檔案.o檔案的集合,而動态庫連結時整個庫都會先映射到進行虛拟位址空間的共享庫區域段。

三、程序虛拟位址空間的配置設定

1、将目标檔案連結成可執行檔案的時候,連結器會把相同權限屬性的段連結在同一空間,在ELF中這些屬性相同的有連在一起的段(section)叫做“segment”​,而系統正是按照segment來映射可執行檔案的。從連結角度看,ELF檔案按“section”存儲的,從裝載的角度看,ELF檔案又可以按“segment”劃分,連結器會盡量把相同權限屬性的section配置設定到同一個segment中。

​使用readelf -S a 可以檢視可執行檔案a的段section

​使用readelf -l a 可以檢視可執行檔案a的段segment​

在ELF檔案裝載是段指segment,在其他時候段​指section

2、堆與棧

在Linux下面可以通過cat /proc檢視程序的虛拟空間配置設定,如(123為程序号):

cat /proc/123/maps

​ps

PID USER       VSZ STAT COMMAND

    1 root      1388 S    init

    2 root         0 SW   [kthreadd]

    3 root         0 SW   [ksoftirqd/0]

    。。。

    264 root      5952 S    /home/process/daemon_fsp_app

    268 root     22660 S    /home/process/net_process

    281 root      305m S    /home/davinci

# cat /proc/281/maps

00008000-00bea000 r-xp 00000000 00:0d 1062       /home/davinci (deleted)---代碼段

00bf2000-00cc5000 rwxp 00be2000 00:0d 1062       /home/davinci (deleted)---資料段

00cc5000-011c2000 rwxp 00000000 00:00 0

022cb000-024e1000 rwxp 00000000 00:00 0          [heap]

40013000-40014000 rwxp 00000000 00:00 0

4008e000-40095000 r-xp 00000000 00:01 164        /lib/ld-uClibc-0.9.32.1.so

4009c000-4009d000 rwxp 00006000 00:01 164        /lib/ld-uClibc-0.9.32.1.so

4009d000-400b7000 r-xp 00000000 00:0d 1056       /home/applib/libfsp_base.so

400b7000-400be000 ---p 00000000 00:00 0

400be000-400bf000 rwxp 00019000 00:0d 1056       /home/applib/libfsp_base.so

...

4023a000-402d2000 r-xp 00000000 00:0d 1052       /home/applib/libsjkd.so (deleted)---代碼段

402d2000-402d8000 rwxp 00098000 00:0d 1052       /home/applib/libsjkd.so (deleted)---資料段

402d8000-402da000 rwxp 00000000 00:00 0

...

be9ce000-be9ef000 rwxp 00000000 00:00 0          [stack]

ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

#

檢視程序的虛拟位址空間是如何使用的。

該檔案有6列,分别為:

位址:庫在程序裡位址範圍

權限:虛拟記憶體的權限,r=讀,w=寫,x=,s=共享,p=私有;

偏移量:庫在程序裡位址範圍

裝置:映像檔案的主裝置号和次裝置号;

節點:映像檔案的節點号;

路徑: 映像檔案的路徑

每項都與一個vm_area_struct結構成員對應

轉載于:https://www.cnblogs.com/luiz/p/6828808.html

繼續閱讀