--- title: 執行階段程式庫 (CRT) tags: C 語言筆記 --- ## 執行階段程式庫 (CRT) ### crt0.o * crt0.o,又稱為 c0,是連接到 C 語言程式的啟動函式,他們不是函式庫,比較像是內核的組合語言,負責進行程式在進入 main 函式以前的工作,包含初始化程式的 stack,設置中斷請求等等工作,而後面加入了建構函式和解構函式等功能之後被稱為 crt1.o * crt1.o 裡面包含了進入函式 _start,由 start 呼叫 __libc_start_main 進行初始化,然後呼叫程式中的 main 函式 ### CRT 行為 * crt 為 c runtime 的縮寫,描述進入 main 函式之前的初始化和退出main函式的清理工作 * 包含初始化及終止 C/C++ 可執行檔所需的程式碼 * 會提供內部 DLL 進入點函式呼叫 * 會呼叫建構函式和解構函式 * 還有許多功能,如例外處理、偵錯支援、執行時檢查、類型資訊、實現細節與特定擴充函式 ### main & C runtime * 範例: ```int main() { return 1; }```,可以用 ```echo $?``` 來取得回傳值 1 ,其實在程式執行完後會呼叫 exit(result),而這就是 C runtime (簡稱 crt) 來處理 * int main(int argc, char *argv[]) * Arguments: expressions passed into a function * Parameters: values received by the function ![](https://i.imgur.com/0AxO5D8.png) * 程式執行時的 envp 也是經由 CRT 傳遞 * 範例 ```c= #include <stdio.h> int main(int argc, char *argv[]) { for (char **ptr = (char**) argv; *ptr != NULL; ptr++) puts(((char *) *ptr)); return 0; } ``` * ```(gdb) x/4s argv[0]``` * 後 3 項是 envp (environment variables),在 C run-time 傳遞進來的內容 ```c= 0x7fffffffe455:"/home/will/test/ret1" 0x7fffffffe46a:"SHELL=/bin/bash" 0x7fffffffe47a:"SESSION_MANAGER=local/will:@/tmp/.ICE-unix/1207,unix/will:/tmp/.ICE-unix/1207" 0x7fffffffe4c8:"QT_ACCESSIBILITY=1" ``` * Linux 系統下程序的入口是 "_start" ,這個函數是 linux 系統庫(Glibc)的一部分,當我們的程式和 Glibc linking 在一起形成最終的可執行文件之後,這個函數就是程序執行初始化的入口函數 * 測試 ``` $gcc test.c -nostdlib # -nostdlib (不使用標準庫) ``` 會出錯 ``` cannot find entry symbol _start ``` * 所以可以結論出 * __start 這個符號是程序的起始 * main 是被標準庫調用的一個符號 * _start 函式:![](https://i.imgur.com/ArW9sRZ.png) * 可以發現在調用 _start 之前,用戶的參數和環境變量就已經被放入 stack 之中了 * __libc_start_main 一共有七個參數,其中 * init: 負責 main 開始之前的初始化工作 * fini: main 結束之後的收尾工作 * rtld_fini(edx): 和動態加載有關的收尾工作 * main 開始之前的工作: * 初始化 stack pointer * 初始化 全域 和 靜態 變數,也就是 .data 的內容 * 將未初始化部分的變數給予初值: 數值型 short,int,long等為 0,bool 為 FALSE,pointer 為 NULL,etc,即 .bss 的內容 * run global constructors * 將 main 函數的參數,argc、argv 等傳遞給 main 函數,然後才真正運行 main 函數 * main 結束之後執行的函數: ```int atexit(void (*function)(void))``` * 用來 內存釋放、關閉打開的文件、關閉socket描述符、釋放鎖,etc * 使用到 atexit 函式是跟 stack 一樣先進後出的(最先宣告,最晚執行) ### GNU Toolchain * 編譯的流程還有格式![](https://i.imgur.com/5nADWjO.png) * 待補 https://www.bottomupcs.com/compiling.xhtml https://www.bottomupcs.com/libraries.xhtml ### environ = argv + argc + 1 * 從 argv 之後會接一個 "\0" (NULL),之後緊接 environ 可以推得 * 從 gdb 觀察: * ```(gdb) x/1s argv[0]```: argv 有幾個這邊就會有幾個,所以用 argc 來加 ``` 0x7fffffffe44b: "/home/will/test/p_environ" ``` * ```(gdb) x/1s argv[1]```: "\0"(NULL),最後的 + 1 ``` 0x0: <error: Cannot access memory at address 0x0> ``` * ```(gdb) x/4s argv[2]```: environ 資料 ``` 0x7fffffffe465:"SHELL=/bin/bash" 0x7fffffffe475:"SESSION_MANAGER=local/will:@/tmp/.ICE-unix/1207,unix/will:/tmp/.ICE-unix/1207" 0x7fffffffe4c3:"QT_ACCESSIBILITY=1" 0x7fffffffe4d6:"COLORTERM=truecolor" ```