編譯和連接
1. 預(yù)處理(Prepressing)
- 將所有的
#define刪除,并展開宏定義。 - 處理所有的條件預(yù)編譯,比如
#if#ifdef#elif#else#endif。 - 處理
#include預(yù)編譯指令,將包含的文件插入到該預(yù)編譯指令的位置,注意,這個(gè)過程是遞歸。 - 刪除所有的
//和/* */ - 添加行號和文件名標(biāo)識(shí)
- 保留所有的
#pragma編譯指令
2. 編譯(Compilation)
編譯就是把預(yù)處理完的文件進(jìn)行一系列的詞法分析、語法分析、語義分析及優(yōu)化后生產(chǎn)相應(yīng)的匯編代碼文件。
gcc 這個(gè)命令只是后臺(tái)程序的包裝,它會(huì)根據(jù)不同的參數(shù)要求去調(diào)用預(yù)編譯編譯程序cc1、匯編器as、鏈接器來ld。
3. 匯編(Assembly)
將匯編代碼轉(zhuǎn)變成機(jī)器可以執(zhí)行的指令
4. 鏈接(Linking)
- 詞法分析
首先源代碼程序被輸入到掃描器(scanner),它運(yùn)用一種類似有限狀態(tài)機(jī)(Finite State Machine)的算法將源代碼的字符序列分割成一系列的記號(Token) - 語法分析
語法分析器(Grammar Parser)將對由掃描器產(chǎn)生的記號進(jìn)行語法分析,產(chǎn)生語法樹(Syntax Tree),整個(gè)過程采用長下文無關(guān)語法(Context-free Grammar)的分析手段。語法樹就是以表達(dá)式(Expression)為及節(jié)點(diǎn)的樹。 - 語義分析
由語義分析器(Semantic Analyer)來完成。所能分析的語義是靜態(tài)語義(Static Semantic),通常包括聲明和類型的匹配、類型的轉(zhuǎn)換。 - 中間語言生成
源碼級優(yōu)化器(Source Code Optimizer)將整個(gè)語法樹轉(zhuǎn)換成中間代碼(Intermediate Code) - 目標(biāo)代碼的生成與優(yōu)化
源代碼級優(yōu)化器產(chǎn)生中間代碼標(biāo)志著下面的過程都屬于編輯器后端,主要包括的代碼生成器(Code Generator)和目標(biāo)優(yōu)化器(Target Code Optimizer)。代碼生成器將中間代碼轉(zhuǎn)換目標(biāo)機(jī)器碼。 - 鏈接
鏈接的主要過程包括:地址和空間分配(Adress and Storage Allocation)、符號決議(symbol resolution)和重定向(Relocation)等步驟。
目標(biāo)文件是什么樣的
目標(biāo)文件的內(nèi)容至少有編譯后的機(jī)器指令代碼,數(shù)據(jù)。還有鏈接時(shí)所需要的信息,如符號表,調(diào)試信息,字符串等。
- 程序源代碼編譯后的機(jī)器指令經(jīng)常被放在
代碼段(Code Setion),常見名字有“.code”和 “.text” - 全局變量和局部靜態(tài)變量數(shù)據(jù)經(jīng)常放在
數(shù)據(jù)段(Data Section),一般名字叫“.data” -
.bss段(Block Started by Symbol)只是為未初始化的全局變量和局部靜態(tài)變量預(yù)留位置而已,并沒有內(nèi)容,它在文件中不占據(jù)空間。
自定義段
GCC提供了一個(gè)擴(kuò)展機(jī)制,使得程序員可以指定變量所處的段:
_attribute_((section("FOO"))) int global = 42;
_attribute_((section("Bar"))) void foo ( )
{
}
我們在全局變量或者函數(shù)之前加上“_attribute_((section("name")))”屬性就可以把相應(yīng)的變量或者函數(shù)放到以“name”作為段名的段中。
ELF文件結(jié)構(gòu)描述
ELF文件結(jié)構(gòu)圖
| ELF Header |
|---|
| .text |
| .data |
| .bss |
| other sections... |
| Section header table |
| String Tables |
| Symbol Tables |
| ... |
靜態(tài)鏈接
1.空間與地址分配
主要有兩種方式:按序疊加和相似段合并。按序疊加會(huì)造成內(nèi)存空間大量的內(nèi)部碎片。所以一個(gè)更實(shí)際的方法是將相同性質(zhì)的段合并在一起,叫做相似段合并。比如將所有輸入文件的“.text”段合并到輸出文件的“.text”段。使用這相思段合并方法的鏈接器一般都采用一種叫兩步鏈接(Two-pass Linking)的方法,也就說整個(gè)過程分兩步。
- 空間與地址分配
- 符號解析與重定位