天天看點

從編譯器角度來了解C++代碼的編譯和連結原理

我們可以在linux系統下對下列代碼是如何進行編譯進行一個了解

main.cpp:

//引用sum.cpp檔案裡面定義的全局變量以及函數
extern int gdata;
int sum (int, int) ;
int data = 20;
int main ()
{
	int a = gdata;
	int b = data;
	int ret =sum (a, b);
	return o;
}

           

sum.cpp:

int gdata = 10;
int sum(int a, int b)
{
	return a+b;
}
           
從編譯器角度來了解C++代碼的編譯和連結原理

接下來我們将從以下幾個問題展開:

1.*.o檔案的格式組成是什麼樣的?

2.可執行檔案的組成格式是什麼樣子?

3.上圖中步驟一和步驟二是做的什麼事情?

4.符号表的輸出裡面的符号怎麼了解?

5.符号什麼時候配置設定虛拟位址?

預編譯(.c -> .i): 1.#開頭的指令的預處理(除了#pragma lib和#pragma link)2.将所有的“#define”删除,并且展開所有的宏定義。3.注釋的删除和替換4.頭檔案的引入。4.保留所有的#pragma編譯器指令,因為編譯器須要使用它們。

編譯(.i -> .s):詞法分析;文法分析;語義分析;以及優化後生産相應的彙編代碼檔案。

彙編(.s -> .o):彙編分為兩種x86和AT&T;将彙編代碼轉變成機器可以執行的指令

,生成二進制可重定位的目标檔案。

連結(.o -> a.out):編譯完成的所有.o檔案+靜态庫檔案進行連結,第一個步驟就是将所有.o檔案段的合并,符号表合并後進行符号解析;第二個步驟就是符号的重定位(連結的核心)。

.o檔案的格式組成:

elf檔案頭

.text

.data

.bss

.symbal

.section table

下面我們詳解一下.o檔案段的合并:

就是當我們main.o sum.o合并的時候各個段就要進行合并了.text <=> .text .bss <=> .bss .data<=> .data等等之間進行合并

符号解析:所有對符号的引用,都要找到該符号定義的地方。

符号解析成功以後!!!!就要給所有的符号配置設定虛拟位址。(即符号的重定向)

符号什麼時候配置設定虛拟位址:

連結的第一步符号解析完成後進行配置設定虛拟位址的。(編譯過程符号是不配置設定虛拟位址的)

我們二進制可重定位目标檔案和可執行檔案最大的差別是什麼呢?

可執行檔案有program headers段。由兩個load告訴系統運作這個程式的時候把哪些内容加載到記憶體中,加載的是代碼段和資料段,由下圖可以看出:

從編譯器角度來了解C++代碼的編譯和連結原理

可執行檔案加載的大概過程如下圖:

從編譯器角度來了解C++代碼的編譯和連結原理