靜态glibc
1 入口函數和程式初始化 入口函數:在main函數之前和之後運作的函數稱為入口函數或入口點(entry point)。
一個典型的程式運作如下:
(1) 程序建立,控制權交給入口點,入口點為運作庫中的入口函數
(2) 入口函數對運作庫和程式運作環境進行初始化,包括堆,I/O、線程、全局變量構造等
(3) 初始化後,調用main函數
(4) Main函數完畢,調用入口函數,做清理工作,包括全局變量析構、堆銷毀、關閉I/O,然後系統調用結束程序。
GlibcGlibc的入口為_start,該入口為ld連結器預設的連結腳本指令的。i386的源碼如下:
libc\sysdeps\i386\elf\Start.S
_start:xorl %ebp, %ebp
popl %esi
movl $esp %ecx
pushl %esp //sp
pushl %edx //rtld_fini
pushl $__libc_csu_fini //fini
pushl $__libc_csu_init //init
pushl %ecx //ubp_av
pushl %esi //argc
pushl main //main
call
__libc_start_main上面右邊注釋幾位
的參數。__libc_start_main的工作如下:
__pthread_initialize_minimal();
_cax_atexit(rtld_fini, NULL, NULL);__libc_init_first(argc, argv, __environ);
__cxa_atexit(fini, NULL, NULL);(*init)(argc, argv, __environ);
加粗部分為注冊函數,main函數結束時調用。在__libc_start_main末尾有:
result = main(argc, argv, __environ);
exit( result );
在exit函數中,依次調用注冊過的函數,然後調用_exit系統調用。因為程式要麼按main函數傳回,要麼exit()結束,都要進入exit函數,能保證注冊函數順利結束。
MSVC CRT 運作庫與I/O使用者打開一個檔案,獲得該檔案的檔案描述符(file description)或者句柄。檔案句柄總是核心的檔案對象相關聯的。fd為0、1、2分别代表标準輸入、标準輸出和錯誤輸出。那麼fd是什麼呢?
每個程序都有一個私有的“打開檔案表”,該表由作業系統核心管理,打開檔案表是一個指針數組,而fd則是數組的下表。
I/O初始化的職責是:需要在使用者空間中建立stdio、stdout、stderr及其他對應的FILE結構,使得程式進入main之後可以直接使用printf、scanf等函數。
2 C/C++ C 語言運作庫 語言标準庫 的啟動檔案Glibc除了C标準庫之外,還有幾個輔助程式運作時的運作庫。分别是crt1.o、crti.o、crtn.o
Crt1.o中包含_start,由它負責調用__libc_start_main初始化libc并且調用main函數。由于C++和ELF檔案改進,出現了在main函數之前\後執行的全局\靜态對象的構造和析構,在目标檔案中引入了.init和.finit。連結器在靜态連結時會把輸入目标檔案總的.init和.finit段收集起來,并且與crti.o和crtn.o中的指令進行合并,形成一個.init和.finit。其中crti.o和crtn.o中的内容是整個初始化段的前部分和後部分。經過連接配接後,最終的形成_init()和_finit兩個函數,由__libc_csu_init和__libc_csu_fini調用。
GCC 平台相關的檔案crtbeginT.o和crtend.o實作C++全局對象的構造和析構。.init和.finit段提供了一個在main之前和之後運作代碼的機制,真正全局構造和析構則由crtbeginT.o和crtend.o完成。
libgcc.a為GCC提供的一個仿真計算庫,軟體仿真各種硬體不提供的運算。
libgcc_eh.a支援C++的異常處理。
3 運作庫與多線程 4 C++ 全局構造與析構 _start à__libc_start_main àinit\__libc_csu_init à_init這裡的_init正是crti中的_init()函數,經過反彙編,可以發現函數進入了
à__do_global_crt_aux在
__do_global_crt_aux中有對__CTOR_LIST__[i]的循環調用。
GCC在編譯某個檔案時,會周遊該檔案中的所有全局對象,生成一個特殊的函數,對全局對象進行構造和析構:(以helloworld為例)
Static void GLOBAL__I_Hw(void)
{
Hw::Hw(); //構造
Atexit(__tcf_1); //析構
}
GCC會在該檔案的目标檔案中生成
.ctors 段,存放指向改函數的指針。
連結器在連結的時候,所有的.ctors段被合并成一個.ctors段,因為合并後的.ctors成了一個指針數組,存放全局構造函數的位址清單__CTOR_LIST__。
在連結的時候,crtbegin.o和crtend.o也包含.ctors段,crtbegin.o作為其實,存放的内容為-1,會被連結器修改為全局構造函數的數量,并且将起始位址定義為__CTOR_LIST__。crtend.o段的内容為全0,并定義符号__CTOR_END__指令段尾。
析構剛好相反,命名跟構造函數對應。
5 fread 實作