Linux核心分析之簡析載入和啟動乙個可執行程式

2021-07-30 00:06:22 字數 3188 閱讀 3920

《linux

核心分析》mooc課程

實驗內容

實驗要求:

關鍵實驗截圖:

圖一 exec( )

圖二 設定斷點

圖三 檢視具體執行流程

載入和啟動乙個可執行程式

可執行檔案的建立:

.c檔案經過預處理,生成.cpp預處理的中間檔案;經過編譯生成彙編**(.asm檔案);再通過彙編器生成目標**(.o檔案);再通過聯結器,鏈結成可執行檔案(.out檔案)。作業系統將可執行檔案載入到記憶體中執行,示意圖如下所示:

預處理:

gcc -e -o hello.cpp hello.c -m32    預處理(文字檔案) 預處理負責把include的檔案包含進來及巨集替換等工作

編譯:gcc -x cpp-output -s -o hello.s hello.cpp -m32    編譯成彙編**(文字檔案)

彙編:gcc -x assembler -c hello.s -o hello.o -m32    彙編成目標**(elf格式,二進位制檔案,有一些機器指令,只是還不能執行)

gcc -o hello hello.o -m32    鏈結成可執行檔案(elf格式,二進位制檔案) 在hello可執行檔案裡面使用了共享庫,會呼叫printf,libc庫里的函式

gcc -o hello.static hello.o -m32 -static    靜態鏈結把執行所需要依賴的東西都放在程式內部

elf三種主要的目標檔案:

連線editor,連線可重定位、共享object檔案。即裝載時鏈結。

動態鏈結器,聯合可執行、其他共享object檔案建立程序映像。即執行時鏈結。

可執行程式的執行環境:

輸入命令gcc -shared dllibexample.c -o libdlibexample.so -m32生成鏈結庫。

然後在程式中使用#include匯入該標頭檔案即可使用該鏈結庫中的函式。

使用命令gcc main.c -o main -l /path/to/your/dir -l shlibexample -ldl -m32執行。

同樣使用使用上述命令生成動態鏈結庫。

在程式中動態呼叫

首先,我們使用函式dlopen開啟動態鏈結庫,然後定義乙個函式指標,然後通過函式dlsym來找到我們想要的函式並附值給上面定義的函式指標,最後利用函式指標呼叫該函式。

首先在使用者態呼叫exec*函式,對應的核心入口為sys_execve

在sys_execve中呼叫do_execve函式

在do_execve函式中呼叫open_execve開啟可執行檔案

呼叫copy_strings_kernel從系統空間拷貝

呼叫copy_strings從使用者空間拷貝

呼叫exec_binprm函式,然後呼叫search_binary_handler函式

呼叫list_for_each_entry來搜尋formats佇列中的成員來執行

呼叫copy_thread來附值程序控制快的相關資訊

最後呼叫start_thread函式來設定新的程式執行的開始位址

執行過程分析:

execve系統呼叫和fork系統呼叫一樣,都是特殊的系統呼叫。fork系統呼叫返回兩次,一次返回父程序執行,一回返回特定的點ret_from_fork執行,然後返回到使用者態。

execve系統呼叫在核心中將當前的可執行程式覆蓋掉了,當返回時不再是原來的可執行程式了。shell會呼叫execve將命令列引數和環境引數傳遞給可執行程式的main函式:

int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

而所有的庫函式exec*都是execve的封裝例程。

系統呼叫sys_execve會解析可執行檔案格式do_execve->do_execve_common->exec_binprm然後執行search_binary_handler找到符合檔案頭部標明的檔案格式的解析模組。對於linux下的elf檔案,fmt->load_binary(bprm)實際執行的就是static int load_elf_binary(struct linux_binprm *bprm)。

執行start_thread(regs, elf_entry, bprm->p)時,如果是靜態鏈結的,elf_entry就是檔案頭部標明的入口。

進入核心後

start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)

execve在返回前用新的ip和sp更新了程序的ip和sp。對於需要動態鏈結的程式,elf_entry就會載入動態鏈結器ld的入口位址。

總結

在linux環境下,可執行檔案的格式為elf,檔案頭部資訊會標明檔案在載入到記憶體中的相關資訊,隨後的是以段形式存在的**和資料。段的劃分主要依據是載入到記憶體中的讀寫屬性。系統呼叫execve則是負責可執行檔案的排程和執行,先進行相關引數的傳遞和呼叫前環境的設定,然後載入可執行檔案的資訊,查詢相關執行檔案解析模組,對elf格式的可執行檔案,按照格式要求載入到記憶體中相應的位址空間當中。如果是靜態鏈結,則將檔案頭部標明的入口位址作為開始執行的位址,如果是依賴動態鏈結庫的 可執行檔案則需要將動態鏈結器ld的入口位址作為開始的位址。

參考資料

核心**,紛繁複雜,在前人總結的基礎上,勉強可以看懂三分,肯定是會存在理解上的諸多檔案,還請各位大佬多多指教,如有什麼不足,還請不吝賜教!!!

? 核心修煉 Linux啟動過程流程簡析

linux啟動流程分析 這裡僅簡要分析linux的大致啟動流程,更多細節請閱讀原始碼分析。linux核心啟動過程分為解壓和啟動vmlinux。啟動過程從核心映像入口開始執行,解壓 vmlinux 核心自引導 並且轉換到虛擬位址空間,再呼叫統一的核心啟動函式start kernel 從而啟動整個 li...

linux核心分析之fork c

include include include include include 申明外部呼叫函式,驗證位址所指向的頁面是否可寫 extern void write verify unsigned long address 全域性變數,用於產生可用的程序id long last pid 0 對指定起始...

linux核心分析之sys c

include include include include include include include 這個檔案中包含了絕大部分系統呼叫函式的實現,如果系統呼叫在該核心版本中沒實現,就直接返回enosys int sys ftime int sys break int sys ptrace ...