天天看點

11運作庫

第十一章 運作庫

靜态glibc

1     入口函數和程式初始化 入口函數

:在main函數之前和之後運作的函數稱為入口函數或入口點(entry point)。

一個典型的程式運作如下:

(1)       程序建立,控制權交給入口點,入口點為運作庫中的入口函數

(2)       入口函數對運作庫和程式運作環境進行初始化,包括堆,I/O、線程、全局變量構造等

(3)       初始化後,調用main函數

(4)       Main函數完畢,調用入口函數,做清理工作,包括全局變量析構、堆銷毀、關閉I/O,然後系統調用結束程序。

Glibc

Glibc的入口為_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 實作

繼續閱讀