天天看點

程式入口程式的入口

程式的入口

加載

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 入口函數名就行了