1. 程式人生 > >2018-2019-1 20189213《Linux核心原理與分析》第八週作業

2018-2019-1 20189213《Linux核心原理與分析》第八週作業

可執行程式工作原理

書本重要知識總結

1.ELF檔案
ELF(Excutable and Linking Format)即可執行的和可連結的格式,是一個目標檔案格式的標準。通過readelf -h hello檢視可執行檔案hello的頭部(-a檢視全部資訊,-h只檢視頭部資訊),頭部裡面註明了目標檔案型別ELF32。Entry point address是程式入口,地址為0x400400,即可執行檔案載入到記憶體中開始執行的第一行程式碼地址。頭部後還有一些程式碼資料等等。可執行檔案的格式和程序的地址空間有一個對映的關係,當程式要載入到記憶體中執行時,將ELF檔案的程式碼段和資料段載入到程序的地址空間。


ELF檔案裡面分為三種目標檔案:
可重定位檔案——檔案中儲存著程式碼和適當的資料,用來和其它的目標檔案一起來建立一個可執行檔案、靜態庫檔案或者是一個共享目標檔案(主要是.o檔案);
可執行檔案——檔案中儲存著一個用來執行的程式,該檔案指出了exec(BA_OS)如何來建立程式程序映象(作業系統怎麼樣把可執行檔案載入起來並且從哪裡開始執行);
共享檔案——檔案中儲存著程式碼和合適的資料,用來被兩個連結器連結。第一個是連結編輯器(靜態連結),可以和其它的可重定位和共享目標檔案來建立其它的object。第二個是動態連結器,聯合一個可執行檔案和其它的共享目標檔案來建立一個程序映象。
ELF格式簡介:
①ELF檔案的索引表
②ELF Header結構
③Section Header結構
④Program Header結構

2.可執行程式的預處理、編譯、彙編、連結
gcc –E hello.c -o hello.i //預處理
gcc -S hello.i -o hello.s -m32//編譯
gcc -c hello.s -o hello.o -m32 //彙編
gcc hello.o -o hello -m32 //連結

用gcc hello.o -o hello.static -m32 -static進行靜態編譯,得到的hello.static把C庫裡需要的東西也放到可執行檔案裡了。用命令ls –l,可以看到hello只有5K左右,hello.static比700K還有多一點。

3.動態連結
動態連結有裝載時動態連結和執行時動態連結兩種方式。
下面對動態連結例項進行分析:
首先是各個對應標頭檔案和函式:
shlibexample.h:


shlibexample.c:

dlllibexample.h:

dlllibexample.c:

分別以共享庫和動態載入共享庫的方式使用libshlibexample.so檔案和libdllibexample.so檔案:

編譯main.c,注意這裡只提供shlibexample的-L(庫對應的介面標頭檔案所在目錄)和-l(庫名,如libshlibexample.so去掉lib和.so的部分),並沒有提供dllibexample的相關資訊,只是指明瞭-ldl:
主函式main.c:

實驗:使用gdb跟蹤分析execve系統呼叫核心處理函式sys_execve。

首先還是將menu目錄刪除,用git命令複製一個新的menu目錄,用test_exec.c將test.c覆蓋,然後重新編譯rootfs:

發現在MenuOS中使用help命令可以看到增加了exec命令,執行exec指令發現比fork指令增加了一行輸出“helloworld!”,實際上是新載入了一個可執行程式來輸出了一行語句:

檢視程式碼我們發現,在test.c中新增了exec函式:

在Makefile中不僅編譯了hello.c,還在生成根檔案系統時把init和hello都放到rootfs.img中:

下面我們使用對gdb進行跟蹤分析:
首先還是凍結核心,載入符號表並設定埠,準備單步除錯:

然後分別在sys_execve、load_elf_binary、start_thread處設定斷點:

然後三次執行後,在執行exec命令後停在如下圖所示位置:

使用list列出相關程式碼,使用step進入sys_execve函式內部發現呼叫了“do_execve()”函式繼續執行到“load_elf_binary”處的斷點:


繼續執行到“start_thread”處的斷點,因為是靜態連結,“elf_entry”指向了可執行檔案中定義的入口地址,使用po new_ip指令列印其指向的地址,“new_ip”是返回到使用者態的第一條指令的地址:


檢視hello的elf頭部,看定義的入口地址與new_ip所指向的地址是否一致:

發現確實是一致的。

問題

1.32位可執行檔案Addr會顯示類似0x8048000的地址,而我的這裡程式入口地址是0x400400,可能與因為這是64位的系統有關。
2.進行預處理編譯彙編連結時,使用自己的虛擬機器在連結這一步時無法成功,初次感覺時gcc版本原因,但更換最新版gcc之後還是不行,不知道原因在哪。


總結

對“Linux核心裝載和啟動一個可執行程式”的理解:
當linux核心或程式使用fork函式建立子程序後,子程序往往要呼叫一種exec函式(exec家族的一種)以執行另一個程式;在呼叫一種exec函式時,該程序執行的程式完全被替換為新程式,而新程式則從其main函式處開始執行,因為呼叫exec函式並不建立新程序,所以前後的程序ID並未改變,或者說exec函式只是用了一個全新的程式替換了當前程序的正文、資料段和堆疊段。