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

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

程序 git命令 進入 信息 exc 完全 三次 linux內核 -h

可執行程序工作原理

書本重要知識總結

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函數只是用了一個全新的程序替換了當前進程的正文、數據段和堆棧段。

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