程式的入口
加載
linux可執行檔案都是通過調用
execve
函數來調用加載器的.
加載器将可執行檔案的代碼和資料從磁盤拷貝到記憶體中, 然後通過第一條指令來查找程式運作的入口, 進而執行整個程式. 而将資料從磁盤複制到記憶體的過程就叫做加載.
程式的入口
通過從核心設定的第一條指令找到程式的入口, 一般gcc預設編譯程式的入口是
_libc_start_main
這一個預設函數, 而預設函數的入口又是存放在段
<_start>
裡面. _start函數調用系統啟動函數, 它初始化環境, 調用使用者層的main函數, 處理main函數的傳回值, 最後将傳回值傳回給核心處理.
一個簡單的代碼
int main()
{ }
執行
gcc main.c
在執行
objdump a.out -d
# 如果适合看intel的彙編, 可加上 -M intel 即可
可以看到,
call __libc_start_main
調用此函數, 而它是在
<_start>
段中 . 在<_start>斷之前還有3個段.
<_init>
段程式會初始化調用
ELF
中
.init
函數. 有興趣可以去查一下ELF.
如果想要自己來設定程式的入口的, 可以在gcc中添加禁用mian入口的指令
gcc -nostartfiles -e 入口 filename.c
-nostartfiles : 關掉gcc預設的main函數作為入口
-e : 設定程式開始的入口
// 簡單的嘗試
#include <stdio.h>
#include <stdlib.h>
void print()
{
printf("print\n");
exit(0);
}
如果在程式的結尾處沒有加上exit函數, 調用return也會出錯, 報出斷錯誤. 這裡主要是exit函數會直接告訴系統該程式退出, 而
return
隻是将程式傳回, 我們并沒有将程式位址壓棧, 是以return傳回的位址就會産生越界, 連個如果都沒有, 那麼程式也就不知道它自己執行結束.
同樣我們objdump看一下, 此時程式的入庫設定成了print函數, 結束的時候在調用exit, 殺死該程序.
沒有調用exit的函數, 則顯示的便是這樣.
對exit和return不太區分的, 也可以看看這一篇部落格.exit和return的差別
如果是自己寫的OS程式啟動, 可在boot.s中修改入口, call 入口函數名就行了