Linux內核如何裝載和啟動一個可執行程序


實驗七:Linux內核如何裝載和啟動一個可執行程序

姓名:李冬輝

學號:20133201

注: 原創作品轉載請注明出處 + 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000

 

雲課堂筆記:

(1)可執行文件的創建

C代碼(.c) - 經過編譯器預處理,編譯成匯編代碼(.asm) - 匯編器,生成目標代碼(.o) - 鏈接器,鏈接成可執行文件(.out) - OS將可執行文件加載到內存里執行。如圖

 

1. 預處理

gcc -E -o hello.cpp hello.c -m32    預處理(文本文件)
預處理負責把include的文件包含進來及宏替換等工作

2. 編譯
gcc -x cpp-output -S -o hello.s hello.cpp -m32    編譯成匯編代碼(文本文件)

3. 匯編
gcc -x assembler -c hello.s -o hello.o -m32    匯編成目標代碼(ELF格式,二進制文件,有一些機器指令,只是還不能運行)

4. 鏈接
gcc -o hello hello.o -m32    鏈接成可執行文件(ELF格式,二進制文件)
在hello可執行文件里面使用了共享庫,會調用printf,libc庫里的函數
gcc -o hello.static hello.o -m32 -static    靜態鏈接
把執行所需要依賴的東西都放在程序內部

 

(2)ELF三種主要的目標文件

1.可重定位:保存代碼和適當數據,用來和其他的object文件一起創建可執行/共享文件,主要是.o文件

2.可執行文件:指出了exec如何創建程序進程映像,怎么加載,從哪里開始執行

3.共享object文件:保存代碼和適當數據,用來被下面的兩個連接器鏈接。

(1)連接editor,連接可重定位、共享object文件。即裝載時鏈接。

(2)動態鏈接器,聯合可執行、其他共享object文件創建進程映像。即運行時鏈接。


(3)可執行程序的執行環境

  • 命令行參數和shell環境,一般我們執行一個程序的Shell環境,我們的實驗直接使用execve系統調用。

    • $ ls -l /usr/bin 列出/usr/bin下的目錄信息

    • Shell本身不限制命令行參數的個數, 命令行參數的個數受限於命令自身

      • 例如,int main(int argc, char *argv[])

      • 又如, int main(int argc, char *argv[], char *envp[])

    • Shell會調用execve將命令行參數和環境參數傳遞給可執行程序的main函數

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

      • 庫函數exec*都是execve的封裝例程

 

命令行參數和環境串都放在用戶態堆棧中

 

 

(4)可執行程序的裝載

  • 命令行參數和shell環境,一般我們執行一個程序的Shell環境,我們的實驗直接使用execve系統調用。

    • Shell本身不限制命令行參數的個數, 命令行參數的個數受限於命令自身

      • 例如,int main(int argc, char *argv[])

      • 又如, int main(int argc, char *argv[], char *envp[])

    • Shell會調用execve將命令行參數和環境參數傳遞給可執行程序的main函數

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

      • 庫函數exec*都是execve的封裝例程

  • sys_execve內部會解析可執行文件格式

 

實驗步驟:

 

1、先把menu刪掉,在克隆一個,用test_exec.c覆蓋掉test.c。

 

2、打開test.c。發現增加了一句MenuConfig。

 

3、打開Makefile,首先靜態編譯了hello.c,生成根文件系統時把init和hello都放入rootfs image里面,這樣執行exec的時候就自動的幫我們加載hello這個文件。

 

4、執行結果hello world! 是新加載的一個可執行程序輸出的。

 

5、-S -s單步調試,窗口被凍結。

 

6、設置三個斷點:sys_execve,load_elf_binary,start_thread。

 

7、list列出來跟蹤, 輸入s可以進入do_execve的內部。按c繼續執行,跑到load_elf_binary。list查看代碼,輸入n一句一句跟蹤,nnnc,追蹤到start_thread。

 

8、觀察hello這個可執行程序的入口,發現也是0x8048d0a,和new_ip的位置一樣。new_ip是返回到用戶態第一條指令的地址。

 

9、將new_ip和new_sp賦值,並設了一個新堆棧。

 

實驗截圖:

 

 

 

 

 

 

 

Linux系統加載可執行程序所需處理過程的理解

1. 新的可執行程序是從哪里開始執行的?

當execve()系統調用終止且進程重新恢復它在用戶態執行時,執行上下文被大幅度改變,要執行的新程序已被映射到進程空間,從elf頭中的程序入口點開始執行新程序。

如果這個新程序是靜態鏈接的,那么這個程序就可以獨立運行,elf頭中的這個入口地址就是本程序的入口地址。

如果這個新程序是動態鏈接的,那么此時還需要裝載共享庫,elf頭中的這個入口地址是動態鏈接器ld的入口地址。

2 .為什么execve系統調用返回后新的可執行程序能順利執行?

新的可執行程序執行,需要以下: 
1. 它所需要的庫函數。 
2. 屬於它的進程空間:代碼段,數據段,內核棧,用戶棧等。 
3. 它所需要的運行參數。 
4. 它所需要的系統資源。 
如果滿足以上4個條件,那么新的可執行程序就會處於可運行態,只要被調度到,就可以正常執行。我們一個一個看這幾個條件能不能滿足。 
條件1:如果新進程是靜態鏈接的,那么庫函數已經在可執行程序文件中,條件滿足。如果是動態鏈接的,新進程的入口地址是動態鏈接器ld的起始地址,可以完成對所需庫函數的加載,也能滿足條件。 
條件2:execve系統調用通過大幅度修改執行上下文,將用戶態堆棧清空,將老進程的進程空間替換為新進程的進程空間,新進程從老進程那里繼承了所需要的進程空間,條件滿足。 
條件3:我們一般在shell中,輸入可執行程序所需要的參數,shell程序把這些參數用函數參數傳遞的方式傳給給execve系統調用,然后execve系統調用以系統調用參數傳遞的方式傳給sys_execve,最后sys_execve在初始化新程序的用戶態堆棧時,將這些參數放在main函數取參數的位置上。條件滿足。 
條件4:如果當前系統中沒有所需要的資源,那么新進程會被掛起,直到資源有了,喚醒新進程,變為可運行態,條件可以滿足。 
綜上所述,新的可執行程序可以順利執行。

3. 對於靜態鏈接的可執行程序和動態鏈接的可執行程序execve系統調用返回時會有什么不同?

execve系統調用會調用sys_execve,然后sys_execve調用do_execve,然后do_execve調用do_execve_common,然后do_execve_common調用exec_binprm,在exec_binprm中:

對於ELF文件格式,fmt函數指針實際會執行load_elf_binary,load_elf_binary會調用start_thread,在start_thread中通過修改內核堆棧中EIP的值,使其指向elf_entry,跳轉到elf_entry執行。 
對於靜態鏈接的可執行程序,elf_entry是新程序的執行起點。對於動態鏈接的可執行程序,需要先加載鏈接器ld, 
elf_entry = load_elf_interp(…) 
將CPU控制權交給ld來加載依賴庫,再由ld在完成加載工作后將CPU控制權還給新進程。

 

總結:

可執行文件是一個普通的文件,它描述了如何初始化一個新的執行上下文,也就是如何開始一個新的計算。可執行文件類別有很多,在內核中有一個鏈表,在init的時候會將支持的可執行程序解析程序注冊添加到鏈表中,那么在對可執行文件進行解析時,就從鏈表頭開始找,找到匹配的處理函數就可以對其進行解析。 
在shell中啟動一個可執行程序時,會創建一個新進程,它通過覆蓋父進程(也就是shell進程)的進程環境,並將用戶態堆棧清空,獲得需要的執行上下文環境。 
命令行參數和環境變量會通過shell傳遞給execve,excve通過系統調用參數傳遞,傳遞給sys_execve,最后sys_execve在初始化新進程堆棧的時候拷貝進去。 
load_elf_binary->start_thread(…)通過修改內核堆棧中EIP的值作為新程序的起點。 

 

 



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM