Linux内核分析——可执行程序的装载
鏈接的過程
- 首先運行C預處理器cpp,將C的源程序(a.c)翻譯成ASCII碼的中間文件(a.i)
- 接著C編譯器ccl,將a.i翻譯成ASCII匯編語言文件a.s
- 接著運行匯編器as,將a.s翻譯成可重定位的目標文件a.o
- 最終完全鏈接成可執行文件a.out
目標文件
- 目標文件有三種形式:
- 可重定位的目標文件
- 可執行目標文件
- 共享目標文件
ELF格式的可重定位目標文件的結構如下:
- .text:已編譯程序的機器代碼
- .rodata:只讀數據
- .data:已初始化的全局C變量
- .bss:未初始化的全局C變量.在目標文件中這個節不占實際空間,僅是一個占位符.
- .sysmtab:一個符號表,存放在程序中被定義和引用的函數和全局變量的信息.
- .rel.text:當鏈接器把這個目標文件和其他文件結合時,.text節中的許多位置都需要修改.一般而言,任何調用外部函數或者引用全局變量的指令都要修改.另一個方面,調用本地函數的指令則不需要修改.
- .rel//.data:被模塊定義或引用的任何全局變量的信息.
- .debug:一個調試符號表
- .line:原始C源程序中的行號和.text節中機器指令之間的映射.
- .strtab:一個字符串表,其中內容包括.symtab和.debug節中的符號表,以及節頭部中的節名字.
可以通過readelf -h process查看的ELF文件的頭部信息
如何將新程序的數據保存下來
通過shell程序調用execve將命令行參數和環境參數傳遞給可執行程序的main函數中。而后execve在創建新的用戶態堆棧時,則將main函數中參數壓入堆棧中。最終執行sys_execve來真正實現在系統下參數的傳遞。
當新的可執行文件被調用的時候,則舊的可執行文件所占有的空間會被新的可執行文件所占用,從而execve返回時,返回的并非為舊的可執行文件所產生的數據,而是新加載進來的可執行文件的返回數據,從而使新的可執行文件可以被執行。
可執行文件的相關點
start_thread通過修改內核堆棧中EIP的值作為新程序的起點
根據靜態鏈接的可執行文件elf_entry就是可執行文件頭中的起點entry,多為main函數對應的位置
若需要依賴動態鏈接庫的話,則elf_entry則指向動態鏈接器的起點,即將CPU控制權交給ld來加載依賴庫并完成動態鏈接
新的可執行程序被調用前,需要通過修改int 0x80壓入內核堆棧的EIP
elf可執行文件會被默認映射到0x8048000這個地址上
execve在內核中的執行過程
- execve運行可執行程序的主要步驟:
刪除已存在的用戶區域:刪除當前可執行文件所占有的用戶部分中的堆棧空間
隱藏私有區域:為新程序的文本、數據和堆棧創建新的他空間,而這些空間是新的可執行文件所私有的,并且是寫時拷貝的。
映射共享區域:如果ELF文件與共享目標連接,就需要動態鏈接,并映射至用戶虛擬地址空間中的共享區域。
- 設置程序計數器:設置EIP,使其指向新的可執行文件的入口地址
如下圖:
execve函數在內核中執行流程
在用戶態中調用execve(),引發系統中斷,在內核態中執行對應的函數sys_execve
sys_execve函數調用do_execve函數,該函數會讀入可執行文件。
接下來系統會調用search_binary_handler,根據可執行文件的類型查找到相應的處理函數。根據每種文件創建一個struct linux_binfmt的結構體,并將其連接到一個鏈表智商,執行時候系統就會遍歷這個鏈表,從而找到相應的結構。
從而調用對應的load_binary函數開始加載可執行文件。系統是通過load_elf_binary來加載elf類型的可執行文件。該函數會先讀入ELF文件的頭部,根據ELF文件的頭部信息讀入各種數據。
如果存在動態鏈接庫,則需要將動態鏈接映射到共享區域之中。此時就需要使用load_elf_interp來加載映像,并把返回的入口地址設置為load_elf_interp的動態鏈接器的入口
如下圖所示:
實驗部分
實驗目的
使用gdb跟蹤sys_execve內核函數的處理過程,分析exec*函數對應的系統調用處理過程,理解Linux內核如何裝載和啟動一個可執行程序。
實驗過程
執行MenuOS,其中裝載了execve
設置斷點
裝載和運行一個可執行文件的順序為:
sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()
總結
當linux內核或程序(例如shell)用fork函數創建子進程后,子進程往往要調用一種exec函數以執行另一個程序。
當進程調用一種exec函數時,該進程執行的程序完全替換為新程序,而新程序則從其main函數開始
池彬寧 + 原創作品轉載請注明出處 + 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
轉載于:https://www.cnblogs.com/Spr1ngxx/p/5361615.html
總結
以上是生活随笔為你收集整理的Linux内核分析——可执行程序的装载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 妖精的尾巴作者是谁啊?
- 下一篇: 中华民国开国纪念币价格值多少钱?