《Linux內核分析》第七週筆記 可執行程序的裝載

20135132陳雨鑫 + 原創做品轉載請註明出處 + 《Linux內核分析》MOOC課程http://mooc.study.163.com/course/USTC-1000029000 」linux

1、預處理、編譯和連接和目標文件的格式shell

 

一、可執行程序是怎麼來的?函數

理解編譯連接的過程和ELF可執行文件格式spa

過程:命令行

​.c文件彙編成彙編代碼.asm,3d

再彙編成目標碼.o,調試

連接成可執行文件a.out,code

最後可執行文件就能夠加載到內存中執行。blog

 

二、目標文件的格式ELF接口

1)主要有三種目標文件

可重定位文件、可執行文件、共享文件

2)ELF文件加載到內存是如何加載的呢?

代碼、數據都加載到內存中,默認ELF文件從0x開始加載,開始加載的是頭部,會有一些信息,因此入口地址0x(這個地方就是程序的實際入口,可執行文件加載到內存中開始執行的第一行代碼從這裏開始)

三、靜態連接的ELF可執行文件和進程的地址空間

入口地址爲0x8048*00

2、可執行程序、共享庫和動態連接

一、使用exec*庫函數加載一個可執行文件

(1)動態連接分爲可執行程序轉載時動態連接和運行時動態連接

1)在linux下動態連接的文件是.so

2)libshlibexample.so文件(生成一個共享庫文件)

   libdllibexample.so(生成可動態加載文件)

(2)如果靜態連接的,elf_entry就是指向可執行文件裏邊規定的那個頭部,即main函數對應的位置,若這個可執行文件是須要依賴其它動態連接庫的話,則elf_entry就是指向動態連接器的起點

二、

(1)execve:當前的可執行程序在執行execve這個系統調用的時候,它陷入到內核態,在內核態裏它用這個execve加載的這個可執行文件把當前進程的可執行程序給覆蓋掉,當execve這個系統調用返回的時候,返回的不是原來的那個可執行程序了,而是新的可執行程序了,它返回的是新的可執行程序的起點,即main函數大體的位置

(2)ELF文件加載到內存是如何加載的呢?

代碼、數據都加載到內存中,默認ELF文件從0x開始加載,開始加載的是頭部,會有一些信息,因此入口地址0x(這個地方就是程序的實際入口,可執行文件加載到內存中開始執行的第一行代碼從這裏開始)

•命令行參數和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的封裝例程

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

裝載時動態連接和運行時動態連接應用舉例

動態連接分爲可執行程序裝載時動態連接和運行時動態連接,以下代碼演示了這兩種動態連接。

 •準備.so文件

shlibexample.h (1.3 KB) - Interface of Shared Lib Example

shlibexample.c (1.2 KB) - Implement of Shared Lib Example

 編譯成libshlibexample.so文件

 1.$ gcc -shared shlibexample.c -o libshlibexample.so -m32

 dllibexample.h (1.3 KB) - Interface of Dynamical Loading Lib Example

dllibexample.c (1.3 KB) - Implement of Dynamical Loading Lib Example

 編譯成libdllibexample.so文件

 1.$ gcc -shared dllibexample.c -o libdllibexample.so -m32

 •分別以共享庫和動態加載共享庫的方式使用libshlibexample.so文件和libdllibexample.so文件

 main.c  (1.9 KB) - Main program

 編譯main,注意這裏只提供shlibexample的-L(庫對應的接口頭文件所在目錄)和-l(庫名,如libshlibexample.so去掉lib和.so的部分),並無提供dllibexample的相關信息,只是指明瞭-ldl

1.$ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32

2.$ export LD_LIBRARY_PATH=$PWD #將當前目錄加入默認路徑,不然main找不到依賴的庫文件,固然也能夠將庫文件copy到默認路徑下。

3.$ ./main

4.This is a Main program!

5.Calling SharedLibApi() function of libshlibexample.so!

6.This is a shared libary!

7.Calling DynamicalLoadingLibApi() function of libdllibexample.so!

8.This is a Dynamical Loading libary!

 3、可執行程序的裝載

1.可執行程序的裝載相關關鍵問題分析 

(1)可執行程序的裝載實際上至關於系統調用。execve系統調用比較特殊。

(2)sys_execve內核處理過程:

  • do_execve -> do_execve_common -> exec_binprm
  • 最後根據給出的文件名加載文件頭部信息尋找對應的文件格式處理模塊。

(3)fmt->load_binary(bprm):用來解析ELF格式文件的執行的位置,這個位置是load_elf_binary。

(4)內核是如何支持多種不一樣可執行文件格式的?

本質上是觀察者模式,經過修改內核堆棧中EIP的值做爲新程序的起點。

(5)莊周(調用execve的可執行程序)入睡(調用execve陷入內核),醒來(系統調用execve返回用戶態)發現本身是蝴蝶(被execve加載的可執行程序)。

2. sys_execve的內部處理過程

do_ execve調用do_ execve_ common,do_ execve_ common主要依靠exec_ binprm,其中重要的函數:search_binary_handler(bprm)。

  • 打開file文件,找到文件頭部,把命令行參數和環境變量copy到結構體中:retval=copy_strings(bprm->envc, envp, bprm);
  • 尋找打開的可執行文件處理函數:ret= search_binary_handler(bprm);
  • 尋找可以解析當前可執行文件的模塊,load_ binary加載這個模塊,實際調用的是binfmt_ elf.c:retval=fmt->load_binary(bprm);
  • ELF可執行文件會被默認映射到0X8048000這個地址;
  • 須要動態連接的可執行文件先加載鏈接器ld;不然直接把ELF文件entry地址賦值給entry;
  • start_ thread(regs, elf_ entry, bprm->p)將CPU控制權交給ld來加載依賴庫並完成動態連接。對於靜態連接的文件elf_entry是新程序執行的起點

經過實驗用gdb跟蹤分析一個execve系統調用內核處理函數sys_execve ,驗證對Linux系統加載可執行程序所需處理過程的理解

實驗過程:

把menu刪掉,從新克隆一份

 

進入test.c中查看:

進入makefile中查看:

開始使用gdb跟蹤​:

 new_ip是返回到用戶態的第一條指令的地址 看該可執行程序的入口點地址,發現和new_ip的位置是同樣的。

退出調試狀態,輸入redelf -h hello能夠查看hello的EIF頭部:

 4、淺談Linux內核裝載和啓動一個可執行程序

  檢查ELF可執行文件的有效性,尋找動態連接的「.interp」段,設置動態連接器路徑(與動態連接有關),根據ELF可執行文件的程序頭表的描述,對ELF文件進行映射,好比代碼,數據,只讀數據,代碼、數據都加載到內存中,默認ELF文件從0x開始加載,開始加載的是頭部,會有一些信息,因此入口地址0x(這個地方就是程序的實際入口,可執行文件加載到內存中開始執行的第一行代碼從這裏開始),初始化ELF進程環境,好比進程啓動時EDX寄存器的地址應該是DT_FINI的地址(和動態連接有關),將系統調用的返回地址修改成ELF可執行文件的入口,這個入口點取決於程序的連接方式,對於靜態連接的可執行文件,如果靜態連接的,elf_entry就是指向可執行文件裏邊規定的那個頭部,即main函數對應的位置,若這個可執行文件是須要依賴其它動態連接庫的話,則elf_entry就是指向動態連接器的起點。

相關文章
相關標籤/搜索