LINUX核心分析。7
一、得到一個可執行程式
1. 預處理、編譯、連結
gcc hello.c -o hello.exe
gcc編譯原始碼生成最終可執行的二進位制程式,GCC後臺隱含執行了四個階段步驟。
預處理 => 編譯 => 彙編 => 連結
預處理:編譯器將C原始碼中包含的標頭檔案編譯進來和執行巨集替換等工作。
gcc -E hello.c -o hello.i
編譯:gcc首先要檢查程式碼的規範性、是否有語法錯誤等,以確定程式碼的實際要做的工作,在檢查無誤後,gcc把程式碼翻譯成組合語言。
gcc –S hello.i –o hello.s
-S:該選項只進行編譯而不進行彙編,生成彙編程式碼。
彙編:把編譯階段生成的.s檔案轉成二進位制目的碼.gcc –c hello.s –o hello.o
連結:將編譯輸出.o檔案連結成最終的可執行檔案。
gcc hello.o –o hello
執行:若連結沒有-o指明,則生成可執行檔案預設為a.out
./hello
2. 目標檔案格式
(1)檔案格式
a.out是最早的可執行檔案格式
注:ABI——應用程式二進位制介面
(2)ELF分類
可重定位檔案:儲存著程式碼和適當的資料,用來和其他的object檔案一起來建立一個可執行檔案或者是一個共享檔案。
可執行檔案:儲存著一個用來執行的程式;該檔案指出了exec(BA_OS)如何來建立程式程式映象。
共享檔案:儲存著程式碼和合適的資料,用來被下面的兩個連結器連結。 •第一個是連線編輯器[請參看ld(SD_CMD)],可以和其他的可重定位和共享object檔案來建立其他的object。
第二個是動態連結器,聯合一個可執行檔案和其他的共享object檔案來建立一個程式映象。
object檔案參與程式的連結(建立)和執行。
(3)ELF頭
檢視ELF檔案的頭部:readelf
在檔案開始儲存了:
- 路線圖:描述該檔案組織情況
- 程式頭表:告訴系統如何建立一個程式的記憶體映像
-
section頭表:描述檔案的section資訊。(每個section在這個表中有一個入口,給出該section資訊)
當建立或增加一個程式映像時,系統在理論上將拷貝一個檔案的段到一個虛擬的記憶體段。3. 靜態連結的ELF可執行檔案和程式的地址空間
入口點:程式從0x804800開始。
可執行檔案載入到記憶體中開始執行的第一行程式碼。
一般靜態連結將會把所有程式碼放在同一個程式碼段。
動態連線的程式會有多個程式碼段。二、可執行程式的執行環境
1. 命令列引數和shell環境
列出/usr/bin下的目錄資訊
$ ls -l /usr/bin
Shell本身不限制命令列引數的個數,命令列引數的個數受限於命令自身
int main(int argc, char argv[], char envp[])
Shell會呼叫execve將命令列引數和環境引數傳遞給可執行程式的main函式
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
庫函式exec*都是execve的封裝例程2. 命令列引數和shell環境變數的儲存與傳遞
shell程式 => execve => sys_execve
命令列引數和環境串都放在使用者態堆疊中
初始化新程式堆疊時拷貝進去
- 可執行程式動態連結
(1)動態連結
實際上,裝載過程是一個廣度遍歷,遍歷的物件是“依賴樹”。
主要過程是動態連結器完成、使用者態完成。
(2)裝載時動態連結
/準備.so檔案/
shlibexample.h (1.3 KB) - Interface of Shared Lib Example
shlibexample.c (1.2 KB) - Implement of Shared Lib Example
/編譯成libshlibexample.so檔案/
$ gcc -shared shlibexample.c -o libshlibexample.so -m32
/使用庫檔案(因為已經包含了標頭檔案所以可以直接呼叫函式)/
SharedLibApi();
(3)執行時動態連結
dllibexample.h (1.3 KB) - Interface of Dynamical Loading Lib Example
dllibexample.c (1.3 KB) - Implement of Dynamical Loading Lib Example
/編譯成libdllibexample.so檔案/
$ gcc -shared dllibexample.c -o libdllibexample.so -m32
/使用庫檔案/
void * handle = dlopen("libdllibexample.so",RTLD_NOW);//先載入進來
int (*func)(void);//宣告一個函式指標
func = dlsym(handle,"DynamicalLoadingLibApi");//根據名稱找到函式指標
func(); //呼叫已宣告函式
(4)執行
$ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32
$ export LD_LIBRARY_PATH=$PWD
/將當前目錄加入預設路徑,否則main找不到依賴的庫檔案,當然也可以將庫檔案copy到預設路徑下。/
三、可執行程式的裝載
- sys_execve核心處理過程
(1)新的可執行程式起點
一般是地址空間為0x8048000或0x8048300
(2)execve與fork
execve和fork都是特殊一點的系統呼叫:一般的都是陷入到核心態再返回到使用者態。
fork兩次返回,第一次返回到父程式繼續向下執行,第二次是子程式返回到ret_from_fork然後正常返回到使用者態。
execve執行的時候陷入到核心態,用execve中載入的程式把當前正在執行的程式覆蓋掉,當系統呼叫返回的時候也就返回到新的可執行程式起點。
execve
執行到可執行程式 -> 陷入核心
構造新的可執行檔案 -> 覆蓋掉原可執行程式
返回到新的可執行程式,作為起點(也就是main函式)
需要構造其執行環境;
Shell會呼叫execve將命令列引數和環境引數傳遞給可執行程式的main函式,先函式呼叫引數傳遞,再系統呼叫引數傳遞。
(3)靜態連結的可執行程式和動態連結的可執行程式execve系統呼叫返回時不同
靜態連結:elf_entry指向可執行檔案的頭部,一般是main函式,是新程式執行的起點。
動態連結:elf_entry指向ld(動態連結器)的起點,載入load_elf_interp
- 動態連結的可執行程式的裝載
(1)可執行檔案開始執行的起點在哪裡?如何才能讓execve系統呼叫返回到使用者態時執行新程式?
修改int 0x80壓入核心堆疊的EIP,通過修改核心堆疊中EIP的值作為新程式的起點。
(2)Linux核心是如何支援多種不同的可執行檔案格式
static struct linux_binfmt elf_format//宣告一個全域性變數 = {
.module = THIS_MODULE,
.load_binary = load_elf_binary,//觀察者自動執行
.load_shlib = load_elf_library,
.core_dump = elf_core_dump,
.min_coredump = ELF_EXEC_PAGESIZE,
};
static int __iit init_elf_binfmt(void)
{n
register_binfmt(&elf_format);//把變數註冊進核心連結串列,在連結串列裡查詢檔案的格式
return 0;
}
(3)動態連結
可執行程式需要依賴動態連結庫,而這個動態連結庫可能會依賴其他的庫,這樣形成了一個關係圖——動態連結庫會生成依賴樹。
依賴動態連結器進行載入庫並進行解析(這就是一個圖的遍歷),裝載所有需要的動態連結庫;之後ld將CPU的控制權交給可執行程式
動態連結的過程主要是動態連結器在起作用,而不是核心完成的。
四,實驗
初始化環境:
跟蹤:
五,感想
本週學習的內容上學期在婁老師中有所涉及,所以還比較親切,可執行檔案的生成的過程,還學習了靜態庫,動態庫。其中預處理,編譯成彙編指令,變成二進位制程式碼,最後執行可執行檔案這四步已經深深的印入我們的腦海。
相關文章
- Linux核心分析。3Linux
- Linux核心分析。4Linux
- Linux核心分析。5Linux
- LINUX核心分析。6Linux
- LINUX核心分析。8Linux
- Linux核心技術分析Linux
- Linux核心分析方法(轉)Linux
- Linux核心排程分析(程式排程)Linux
- Linux程式排程核心實現分析Linux
- 深入分析LInux核心連結串列Linux
- 使用GDK7除錯Linux核心之KVM除錯Linux
- Linux核心分析--系統呼叫實現程式碼分析(轉)Linux
- 《Linux核心分析》筆記與課件整理Linux筆記
- Linux核心記憶體管子系統分析Linux記憶體
- Linux核心原始碼分析之setup_arch (四)Linux原始碼
- Linux核心原始碼分析之setup_arch (二)Linux原始碼
- Linux核心原始碼分析之set_arch (一)Linux原始碼
- Linux核心原始碼分析之setup_arch (三)Linux原始碼
- Linux核心建立一個程式的過程分析Linux
- [轉帖]Linux核心原始碼分析分享專題Linux原始碼
- 使用GDK7除錯Linux核心之printk函式除錯Linux函式
- Linux 核心排程器原始碼分析 - 初始化Linux原始碼
- Linux4.1.15核心啟動流程簡單分析Linux
- Linux核心之 核心同步Linux
- 認識linux核心(linux核心的作用)Linux
- 《Linux核心分析》 之 計算機是如何工作的。1Linux計算機
- toa 核心模組分析
- Android核心分析Android
- 使用GDK7除錯Linux核心之觀察i915除錯Linux
- CentOS 7核心升級教程。CentOS
- centos7 核心升級CentOS
- 初識Linux核心-DIY核心模組Linux
- Linux 4.x MTD原始碼分析-核心資料結構Linux原始碼資料結構
- 《Linux核心分析》 之 作業系統是如何工作的。2Linux作業系統
- linux核心檔案IO的系統呼叫實現分析(open)Linux
- linux核心dos_ddos攻擊防禦演算法分析Linux演算法
- 深入淺出分析Linux系統核心漏洞的問題(轉)Linux
- Linux 核心剖析Linux