2019-2020-1 20199314 《Linux內核原理與分析》 第八週做業

可執行程序工做原理、

一、ELF(Executable and Linkable Format)可執行和可連接文件,其包含了如下三類:

  1. 可重定位文件(Relocatable File):保存着代碼和適當的數據,用來和其它的目標文件一塊兒來建立一個可執行文件、靜態庫文件或者是一個共享目標文件(主要是.o文件)
  2. 可執行文件(Executable File):保存着一個用來執行的程序,通常由多個可重定位文件結合生成,是完成了全部重定位工做和符號解析(除了運行時解析的共享庫符號)的文件。
  3. 共享目標文件(Shared Object File):保存着代碼和合適的數據,用來被兩個連接器連接。第一個是連接編輯器(靜態連接),能夠和其它的可重定位和共享目標文件來建立其它的object。第二個是動態連接器,聯合一個可執行文件和其它的共享目標文件來建立一個進程映象。

二、ELF文件格式:

ELF文件的主體是各類節,以及描述這些節屬性的信息(Program header table和 Section header table),以及ELF文件的總體性信息(ELF header),以下圖。git

三、程序編譯

程序從源代碼到可執行文件的步驟:預處理、編譯、彙編、銜接--以hello.c爲例。github

預處理: gcc -E hello.c -o hello.i  -m32
編譯: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和hello-static文件就是可執行文件。可執行文件中的內容包括有編譯後的機器指令代碼、數據還包括了連接時所需要的一些信息,好比符號表、調試信息、字符串等。
其文件格式,以下圖。編程

四、靜態銜接和動態銜接

靜態連接:在編譯連接時直接將須要的執行代碼複製到最終可執行文件中,優勢是代碼的裝載速度快,執行速度也比較快,對外部環境依賴度低。缺點是若是多個應用程序使用同一庫函數,會被裝載屢次,浪費內存。
動態連接:編譯時不直接複製可執行代碼,而是經過記錄一系列符號和參數,在程序運行或加載時將這些信息傳遞給操做系統。操做系統負責將須要的動態庫加載到內存中,在程序運行到指定的代碼時,去共享執行內存中已經加載的動態庫去執行代碼,最終達到運行時連接的目的。優勢是多個程序能夠共享同一段代碼,而不須要在磁盤上存儲多個複製。缺點是在運行時加載可能會影響程序的前期執行性能,並且對使用的庫依賴性較高。(分爲裝載時動態連接和運行時動態連接)vim

使用gcc hello.o -o hello.static -static進行靜態連接,發現獲得的可執行程序文件大小可達到動態連接的100倍,如圖。編輯器

五、動態銜接

動態銜接分爲如下兩種;函數

  1. 可執行程序裝載時動態銜接
    2.運行時動態銜接。

裝載時動態銜接意味着在程序一開始啓動的時候其所調用的庫須要在一開始就提供,而運行時動態銜接只有程序運行到相關語句纔會訪問dllibexample。性能

六、fork和execve區別與聯繫

對於fork():
一、子進程複製父進程的全部進程內存到其內存地址空間中。父、子進程的
「數據段」,「堆棧段」和「代碼段」徹底相同,即子進程中的每個字節都
和父進程同樣。
二、子進程的當前工做目錄、umask掩碼值和父進程相同,fork()以前父進程
打開的文件描述符,在子進程中一樣打開,而且都指向相同的文件表項。
三、子進程擁有本身的進程ID。測試

對於exec():
一、進程調用exec()後,將在同一塊進程內存裏用一個新程序來代替調用
exec()的那個進程,新程序代替當前進程映像,當前進程的「數據段」,
「堆棧段」和「代碼段」背新程序改寫。
二、新程序會保持調用exec()進程的ID不變。
三、調用exec()以前打開打開的描述字繼續打開(好像有什麼參數能夠令打開
的描述字在新程序中關閉)網站

實驗1:

編程使用exec* 庫函數加載一個可執行文件,動態連接分爲可執行程序裝載時動態連接和運行時動態連接,編程練習動態連接庫的這兩種使用方式。操作系統

一、實驗樓上說要從某個網站上下載下面兩個源碼:

shlibexample.h (1.3 KB) - Interface of Shared Lib Example
shlibexample.c (1.2 KB) - Implement of Shared Lib Example
可是後來查到是網易雲付費課的附件,爲了兩個代碼不必花這麼多錢,再說書上已經有源碼了,能夠本身寫一遍,可是書上的代碼有一個大坑,直接影響了我後面作實驗的進度,以後來我會說明。

shlibexample.h
#ifndef  _SH_LTB_EXAMPLE_H_
#define  _SH_LTB_EXAMPLE_H_
#define SUCCESS 0
#define FAILURE (-1)
#ifdef __cplusplus
extern "C" {
#endif
int SharedLibApi();
#ifdef __cplusplus
}
#endif
#endif

dllibexample.h
#ifndef  _DL_LTB_EXAMPLE_H_
#define  _DL_LTB_EXAMPLE_H_
#ifdef _cplusplus
extern "C"{
#endif
int DynamicalLoadingLibApi();
#ifdef _cplusplus
}
#endif
#endif

shlibexample.c
#include <stdio.h>
#include "shlibexample.h"
int SharedLibApi()
{
printf("This is a shared libary!\n");
return SUCCESS;
}

dllibexample.c
#include <stdio.h>
#include "dllibexample.h"
#define SUCCESS 0
#define FAILURE(-1)
int DynamicalLoadingLibApi()
{
printf(「This is a Dynamical Loading library」!\n」);
return SUCCESS;
}

二、編譯成libshlibexample.so文件

$ gcc -shared shlibexample.c -o libshlibexample.so -m32
$ gcc -shared dllibexample.c -o libdllibexample.so -m32

錯誤到這裏就顯現出來了,爲何會報錯我在網上查了不少答案,什麼須要更新gcc啊,什麼C文件頭名字衝突啊,反正各類答案,耗費了好久時間就是依舊會報錯,因而我從新打了好幾遍代碼,直到發現有一個賊細小的地方,書上很容易誤導。

注意看!這分明看着是就是LIB對吧,我在vim編輯器中敲的時候也和書上的樣子差很少,然而我仔細看了一下I的上橫和下橫仍是有點長短不一的,這貨該不會是T吧!!,結果事實驗證了個人判斷,這貨就是T,LTB。終於找到緣由了

三、mian函數中分別以共享庫和動態加載共享庫的方式使用libshlibexample.so文件和libdllibexample.so文件

main.c
#include <stdio.h>
#include "shlibexample.h" 
#include <dlfcn.h>

int main()
{
   printf("This is a Main program!\n");
/* Use Shared Lib */
  printf("Calling SharedLibApi() function of libshlibexample.so!\n");
 SharedLibApi(); //直接調用共享庫
/* Use Dynamical Loading Lib */
  void * handle = dlopen("libdllibexample.so",RTLD_NOW);//打開動態庫並將其加載到內存
  if(handle == NULL)
{
    printf("Open Lib libdllibexample.so Error:%s\n",dlerror());
    return   FAILURE;
}
int (*func)(void);
char * error;
func = dlsym(handle,"DynamicalLoadingLibApi");
if((error = dlerror()) != NULL)
{
    printf("DynamicalLoadingLibApi not found:%s\n",error);
    return   FAILURE;
}    
printf("Calling DynamicalLoadingLibApi() function of libdllibexample.so!\n");
func();  
dlclose(handle); //卸載庫  
return SUCCESS;
}

四、動態銜接運行測試

由於shilibexample在銜接時就須要提供路徑,對應的頭文件shilibexample.h也須要在編譯器能找到位置。使用參數-L代表文件路徑,-l表示庫文件名。
dllibexample只有在程序運行到相關語句纔會訪問,在編譯時不須要任何的相關信息,使用-ldl指明其所須要的共享庫dlopen,同時修改LD_LIBRARY_PATH確保dllibexample.so能夠查到。

gcc main.c -o main -L./ -l shlibexample -ldl -m32
export LD_LIBRARY_PATH=$PWD
./main

實驗2:

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

實驗二就只能在實驗樓環境作了,畢竟本地虛擬機沒有配好的環境。

一、首先從github上下載包含test_exec.c文件的文件夾,進行編譯。

ls
cd ~/LinuxKernel
rm menu -rf
git clone https://github.com/mengning/menu.git
cd menu 
mv test_exec.c test.c
make rootfs

二、在qemu界面使用help和exec命令查看是否編譯成功。

三、隨後從新啓動qemu。

四、加載內核和端口。

五、設置三個斷點。

六、調試運行程序。

相關文章
相關標籤/搜索