標籤:
可執行程式的裝載
20135109 高藝桐
《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
一、預先處理、編譯、連結和目的檔案的格式
1、1可執行程式是怎麼得來的
- C代碼經過編譯器的預先處理編譯、編譯成彙編代碼、編譯器將其編譯成目標代碼、連結成可執行檔。
- 預先處理負責把include的檔案包含進來及宏替換等工作。
- 預先處理之後的檔案編譯成彙編代碼。
- 彙編代碼.s編譯成.o
1、2目標檔案格式的ELF
- 常見的檔案格式:A.out(最古老的)、 COFF 、PE(windows系統)、 ELF(Linux系統)
- ABI應用程式二進位介面,在目標檔案中已經是二進位相容的格式了,目標檔案適應到某一種CPU體繫結構
- ELF格式中主要有3種可執行檔:可重定位檔案.o,可執行檔,共用目標檔案
- 一個ELF頭在檔案的開始,儲存了路線圖,描述了檔案的組織情況
- 當建立或增加一個進程映像的時候,系統在理論上將拷貝一個檔案的段到一個虛擬記憶體段
1、3靜態連結的ELF可執行檔和進程的地址空間
- 可執行檔ELF載入到記憶體中去:代碼的資料載入到記憶體中去
- 預設ELF檔案載入到0x8048000
- 程式的實際入口0x8048300(啟動一個剛載入過可執行檔之後開始執行的進入點)
- 一般靜態連結會將所有代碼放在一個程式碼片段
- 動態連結的進程會有多個程式碼片段
二、可執行程式、共用庫和動態連結
2、1裝載可執行程式之前的動作
- 可執行程式的執行環境:shell命令列、main函數的參數和execve參數
- shell本身不限制命令列參數的個數,命令列參數的個數受限於命令本身
- shell會調用execve將命令列參數和環境傳遞給可執行程式的main函數
- 命令列參數和環境變數是如何儲存和傳遞的:execve將原來的執行環境覆蓋掉了,shell程式——>execve——>sys_execve然後在初始化新程式堆棧時拷貝進去
- 先函數調用參數傳遞,再系統調用參數傳遞
2、2裝載動態連結和運行時動態連結應用舉例
- 動態連結分為:可執行程式裝載時動態連結和運行時動態連結
- 準備.so檔案——>編譯成libshlbexample.so檔案(共用庫)——>編譯成libdllibexample.so檔案(動態裝載)
- 編譯main:只提供了shellexample的-L和-l,並沒有提供dllibexample的相關資訊,只指明了-ldl
三、可執行程式的裝載
3、1可執行程式的裝載相關關鍵問題分析
- execve和fork都是特殊的系統的調用,當前程式執行到execve系統調用時陷入到核心態,execve載入可執行檔把當前進程的可執行檔覆蓋掉,execve返回新的可執行程式的執行起點。
- int execve(把命令列和環境參數載入進來)
- sys_execve內部會解析可執行檔格式:do_execve->do_execve_common->exec_binprm
- search_binaty_handle符合檔案格式,對應的解析模組(根據檔案頭部資訊尋找對應的檔案格式處理模組)
- 對於ELF格式的可執行檔執行的是load_elf_binary
- elf_format和init_elf_binfnt是觀察者模式中的觀察者
- 尋找標頭檔是被觀察者,出現elf格式檔案,觀察者自動執行elf_format模組
- execve系統調用返回使用者態從哪裡開始執行:load_elf_binary->start_thread(通過修改核心堆棧中EIP的值作為新程式的起點)
3、2sys_execve的內部處理過程
- search_binary_handler(尋找可執行檔的處理函數) fmt->load_binary(載入可執行程式的處理函數)
- register_binfmt(註冊結構體變數)
- kernal_read讀取檔案資訊
- ELF可執行檔會被預設映射到0x8048000這個地址
- 需要動態連結的可執行檔先載入連接器ld這個共用庫 load_elf_interp(載入動態連接器的起點)
- 如果是靜態連結直接進入elf_entry,elf_entry是新程式的起點
- start_thread(如果是靜態連結,直接指向main8048000;如果可執行檔是動態連結程式庫,指向動態連接器的起點
3、3使用gdb跟蹤sys_execve的內部處理過程
自己實驗:
(1)複製,覆蓋test.c
test.c檔案代碼:
(2)產生根檔案系統時,將init hello放入rootfs地址中,這樣在執行exec檔案時,就自動載入hello檔案
(二)使用gdb跟蹤sys_execve核心功能的處理過程
1、載入符號表,並串連到連接埠1234
2、設定斷點:b sys_execve(可以先停在sys_execve然後再設定其他斷點),b load_elf_binary,b start_thread。
3、執行
4、輸入c繼續執行,輸入指令exec,list查看,按s可以跟蹤進行到do_execve的內部。
3、4可執行程式的裝載與莊生夢蝶的故事
- 莊周(調用execve的可執行程式)
- 入睡(調用execve陷入核心)
- 醒來(系統調用execve返回使用者態)
- 發現自己是蝴蝶(被execve載入的可執行程式)
3、5淺析動態連結的可執行程式裝載
四、總結
1、可執行程式的產生:
C語言代碼-->編譯器預先處理-->編譯成彙編代碼-->彙編器編譯成目標代碼-->連結成可執行檔,再由作業系統載入到記憶體中執行。
2、ELF格式中主要有3種可執行檔:可重定位檔案.o,可執行檔,共用目標檔案。
3、ELF可執行檔會被預設映射到0x8048000這個地址。
4、命令列參數和環境變數是如何進入新程式的堆棧的?
Shell程式-->execve-->sys_execve,然後在初始化新程式堆棧時拷貝進去。
先函數調用參數傳遞,再系統調用參數傳遞。
5、當前程式執行到execve系統調用時陷入核心態,在核心中用execve載入可執行檔,把當前進程的可執行檔覆蓋掉,execve系統調用返回到新的可執行程式的起點。
6、動態連結程式庫的裝載過程是一個圖的遍曆過程,
ELF格式中的.interp和.dynamic需要依賴動態連結器來解析,entry返回到使用者態時不是返回到可執行程式規定的起點,返回到動態連結器的程式入口。
可執行程式的裝載