20135103王海甯
原創作品轉載請注明出處
《Linux核心分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000
==========================================================================
這周的實驗是關于Linux核心如何加載一個可執行程式的。下面開始實驗:
打開實驗樓環境,重新下載下傳編譯核心,然後小s大s,啟動停止後用gdb打好斷點,開始調試。
下面分析:
1.先看ELF檔案的格式。ELF頭描述了該檔案的組織情況,ELF檔案預設從0x8048000開始加載,檔案頭中Entry point address的内容為程式實際入口,啟動一個剛加載過可執行檔案的程序時就從此處執行。使用指令readelf -h hello可以檢視檔案的elf頭。具體結構定義如下:
ELF在object檔案中有三種主要的目标檔案類型:
1)一個可重定位(relocatable)檔案儲存着代碼和适當的資料,用來和其他的object檔案一起來建立一個可執行檔案或者是一個共享檔案。主要是.o檔案
2)一個可執行(executable)檔案儲存着一個用來執行的程式;該檔案指出了exec(BA_OS)如何來建立程式程序映象。
3)一個共享object檔案儲存着代碼和合适的資料,用來被下面的兩個連結器連結。第一個是連接配接編輯器,可以和其他的可重定位和共享object檔案來建立其他的object。第二個是動态連結器,聯合一個可執行檔案和其他的共享object檔案來建立一個程序映象。主要是.so檔案
2.對于execve系統調用傳回後新的可執行程式能順利執行的原因:新的可執行程式執行需要:所需的庫函數、屬于它的程序空間(代碼段,資料段,核心棧,使用者棧等)、運作參數、所需的系統資源。滿足這4個條件,新的可執行程式就會處于可運作态,隻要被排程到,就可以正常執行。
1)如果新程序是靜态連結的,那麼庫函數已經在可執行程式檔案中,條件滿足。如果是動态連結的,新程序的入口位址是動态連結器ld的起始位址,可以完成對所需庫函數的加載。
2)execve系統調用通過大幅度修改執行上下文,将使用者态堆棧清空,将老程序的程序空間替換為新程序的程序空間,新程序從老程序那裡繼承了所需的程序空間。
3)在shell中輸入可執行程式所需的參數,shell程式把這些參數用參數傳遞的方式傳給execve系統調用,然後execve系統調用以系統調用參數傳遞的方式傳給sys_execve,最後sys_execve在初始化新程式的使用者态堆棧時,将這些參數放在main函數取參數的位置上。
4)如果目前系統中沒有所需要的資源,那麼新程序會被挂起,直到有資源喚醒新程序,轉為可運作态。
3.關于靜态連結的可執行程式和動态連結的可執行程式,它們在處理execve系統調用傳回時的比較:
1)對于靜态連結的可執行程式,elf_entry是新程式的執行起點。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執行。
2)對于動态連結的可執行程式,需要先加載連結器ld,elf_entry = load_elf_interp(…) 将CPU控制權交給ld來加載依賴庫,ld完成加載工作後将CPU控制權還給新程序。
3) 載入時間和執行時間對比
-靜态連結是在生成可執行程式的時候就把庫中的内容加入到程式中,即一開始就把所有子產品都加載進入記憶體
-裝載時動态連結是在将功能子產品讀入記憶體時把動态庫中調用到的相關子產品的内容載入記憶體
-運作時動态連結是在執行程式調用到子產品内容時再将動态庫中的相應子產品載入到記憶體。
總結:
新的可執行程式通過修改核心堆棧EIP作為新程式的起點,從new_ip開始執行後start_thread把傳回到使用者态的位置從Int 0x80的下一條指令變成新加載的可執行檔案的入口位置。當執行到execve系統調用時,陷入核心态,用execve加載的可執行檔案覆寫目前程序的可執行程式,當execve系統調用傳回時,傳回新的可執行程式的執行起點(main函數位置),是以execve系統調用傳回後新的可執行程式能順利執行。execve系統調用傳回時,如果是靜态連結,elf_entry指向可執行檔案規定的頭部(main函數對應的位置0x8048***);如果需要依賴動态連結庫,elf_entry指向動态連結器的起點。動态連結主要是由動态連結器ld來完成的。