天天看點

ELF檔案的連結視圖(obj)和執行視圖(exe)

先來說一下obj檔案和exe檔案的組成格式吧。

ELF檔案的連結視圖(obj)和執行視圖(exe)
obj檔案組成格式
           

ELF Header一共52個位元組,用16進制表示0X34。通過readelf -h main.o指令可以檢視檔案頭(-s 可以檢視所有的段)。可以發現檔案的開始先是ELF Header檔案頭,它包含了整個檔案的基本屬性(ELF檔案版本、目标機器型号、程式入口等)。接着就是.text段,放的是目前檔案編譯以後得到的指令,從0x34處開始偏移的,意思代碼段緊接着ELF檔案頭,以4位元組對齊。接着就是資料段記錄每個資料的初始值。.bss段不用記錄,直接給0,它存放沒有初始化的資料或者值初始化為0的資料,節省的是檔案空間。你查詢段表,發現data段下來的是.comment段,就說明.bss省的是檔案空間。這些段完了之後就是段表,段表描述了ELF檔案包含的所有段的資訊,比如每個段的段名,段的長度,在檔案中的偏移,讀寫權限及段的其他屬性。把段表在檔案中的偏移量就記錄在ELF檔案頭。還有符号表,記錄程式中的符号及其對應的位址。obj檔案最後一個段的偏移量加上它本身的大小就是檔案的大小。obj檔案的構成主要就是這些,而exe檔案的組成格式:

ELF檔案的連結視圖(obj)和執行視圖(exe)

exe檔案的組成格式

exe和obj組成格式差不多,exe檔案開始也是ELF Header,但是它.text開始處不再是0x34了,而是變成了0x94,因為它多了一個program headers,占96個位元組(52位元組+96位元組表示成十六進制就0X94)。這也是exe和obj檔案最大的差別,就是它比obj檔案多了一個program headers,program headers是按頁面運作的,它有兩個LOAD頁面,這兩個LOAD頁面就訓示了加載器,把目前程式的哪些東西加載到記憶體上。program headers向作業系統描述了程式load和execute所需要的一切資訊,它描述的是系統準備程式運作所需要的一個段和其他資訊。正是因為exe檔案有program headers,是以它才能加載到記憶體上,兩個LOAD頁面訓示加載到記憶體上的隻有代碼段和資料段。舉一個例子:

可執行檔案從磁盤到實體記憶體:

ELF檔案的連結視圖(obj)和執行視圖(exe)

可執行檔案的兩個LOAD頁面存放在磁盤上,這兩個頁面通過mmap的映射方式将磁盤上的頁面映射到虛拟位址空間(mmap用來開辟虛拟記憶體),虛拟空間中以頁面為機關,它以多級頁表的映射方式将虛拟空間中的頁面映射到實際的實體頁面上去。

一個程式的運作,就是你輸入了./a.out之後會發生什麼?

1、建立虛拟位址空間到實體記憶體的映射(建立核心位址映射結構體),建立頁目錄和頁表。

2、加載代碼段和資料段。

3、把可執行檔案真的入口位址寫到CPU的PC寄存器中。

obj檔案和exe檔案的差別

說了這麼多,其實obj檔案和exe檔案的差別可以總結為以下幾點?

1.首先obj和exe組成格式開始都是ELF Header,但是exe的ELF Header中的程式入口位址不再是0,而且一個main函數的位址。

2.exe的ELF Header也是52個位元組,和obj一樣,但是exe中.text起始位址不再是0x34了,exe比obj多了一塊空間,在ELF和.text中間,叫做program headers.

3.exe本身還是按照段進行存儲的,不過兩個LOAD項指明了哪些段要放在一個頁面上。

這些差別也說明了為什麼exe檔案可以運作,而obj檔案不能運作。

ELF檔案頭包括程式入口位址,obj的入口位址是0,0位址不能通路,而且obj檔案是可重定位檔案,他在編譯後生成,而編譯的時候是不給符号配置設定位址的,記憶體位址是在連結的時候配置設定,符号解析完以後就配置設定虛拟記憶體位址。其實說到底就是因為obj檔案沒有program headers,就沒有兩個LOAD頁面,LOAD頁面就訓示了加載器,把目前程式的哪些東西加載到記憶體上。