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 來說,此服務實作於
linux/arch/i386/kernel/process.c 檔案;在 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 處理 |