Segment Type
Program loading 時期會處理以下 4 種 segment:
#define PT_LOAD 1 /* Loadable program segment */ #define PT_DYNAMIC 2 /* Dynamic linking information */ #define PT_INTERP 3 /* Program interpreter */ #define PT_PHDR 6 /* Entry for header table itself */
簡要說明這 4 種 segment 的作用如下。
PT_LOAD:即 text segment 或 data segment。
PT_DYNAMIC:若 ELF 有此 segment,則表示 .dynamic section 會獨立成一個實體的 segment。PT_DYNAMIC 僅包含 .dynamic section。
PT_INTERP:紀錄 program interpreter 路徑與檔名的 segment。PT_INTERP 僅包含 .interp section。
PT_PHDR:Program header table segment。
Segment type 是很重要的資訊,kernel 的 ELF loader 與 program interpreter(ld.so)都是透過 segment type 來判斷 segment 的用途。
Kernel 與 program interpreter 對不同的 segment 都有不同的處理方式。
另外,PT_NOTE 並不是一個必要的 segment,實際上它也沒有重要的作用,因此忽略不做討論。
更多有關 Kernel ELF Loader
回到 Jollen's blog「Process Creation」專欄所談論到的「Program 載入流程」裡,相當重要的一個觀念就是 "exec" system call。程式的載入涉及 exec systegm call service,exec system call 是 machine-dependent 的實作,以 x86 來說,此服務實作於 檔案;在 SystemV ABI 規格中,program loading 的章節 被撰寫在 'processor-specific' 文件裡。
在目前所討論的 ELF loading 主題中,我們還看不到與 processor 相關的議題,例如 .got/.plt 節區的用途;相關的技術細節在此較不適合做深入討論,未來會以分享一份技術簡報的方式來呈現,並且以 ARM 做為討論標地。
Exec system call 叫用 kernel space 的 ELF loader 載入 ELF image,此觀念請參考 Jollen's Blog「Process Creation, #6:Exec System Call 的觀念」。我們整理過 kernel 的 ELF loader 所做的工作有:
1. 讀取 ELF image 的 segment 資訊(由 program header table)。
2. 讀出 segment 內容。
3. 判斷是否有 program interpreter;若有,再讀取 program interpreter。
4. 將讀取到的 segment 內容取代掉 current 的內容,這個動作就是 exec system call 的重要特性。
項目 1. 是由 ELF image 的 program header table 分析執行檔的 segment 資訊,並判斷 segment type;ELF loader 會依照 segment 的類型來做不同的處理,說明如後(項目 2.)。
項目 3. 則是在找到 PT_INTERP segment 時,呼叫 interpreter loader 將 interpreter 載入。Program interpreter 我們先前曾提過,就是 ld.so(dynamic loader/linker),這個部份將在下一篇日記再做說明。
PT_INTERP
若 ELF loader 判斷此 segment 為 PT_INTERP,則叫用 interpreter 的 loading 來做載入的工作,在標準的 GNU/Linux 系統底下,由於 program interpreter 也是 ELF 格式,因此叫用 kernel 的load_elf_interp() API(fs/binfmt_elf.c)將 program interpreter 讀取並載入。
PT_LOAD
若 ELF loader 判斷此 segment 為 PT_LOAD,表示這是 text segment 或 data segment,也就是最主要的程式碼與資料區段,此時便透過 do_mmap() 來將 text/data segment mapping 到記憶體。
依照 ELF 規格,text segment 會由 virtual address 0x0804_8000 的地方開始 mapping,data segment 則是緊接在 text segment 之後。
另外,text segment 與 data segment 都是 PT_LOAD 類型,因此需要根據 Flg 欄位的屬性來知道該 segment 是 text segment 還是 data segment。詳見前一篇日記有關 segment permissions 的說明。
小結
總結本專欄到目前為止所得到的資訊如下。灰色部份是 interpreter 負責的工作,將在下篇日記討論。
請注意,本日記是針對 kernel 本身所做的 ELF loading 工作來做整理,即 kernel-space 的 program loading。另外一半的 program loading 是由 user-space 的 dynamic loader/linker 所完成,這裡所提的 dynamic loader/linker 就是我們不斷提到的 program interpreter。
Segment | Type | Kernel or Program Interpreter |
00 | PHDR | Interpreter 用來計算 base address 用。 |
01 | INTERP | Kernel ELF loader 用來載入 interpreter,並交由 interpreter 做 shared library 的 dynamic linking。 |
02 | LOAD | Kernel 將此 segment mapping 為新的 text segment 或 data segment。 |
03 | LOAD | Kernel 將此 segment mapping 為新的 text segment 或 data segment。 |
04 | DYNAMIC | 由 interpreter 處理 |
--jollen