GCC編譯過程詳解

gcc 編譯過程
從 hello.c 到 hello(或 a.out)文件, 必須歷經 hello.i、 hello.s、 hello.o,最後纔得到 hello(或
a.out)文件,分別對應着預處理、編譯、彙編和鏈接 4 個步驟,整個過

程如圖 所示:

這 4 步大致的工作內容如下:
(1) 預處理, C 編譯器對各種預處理命令進行處理,包括頭文件包含、宏定義的擴
展、條件編譯的選擇等;
(2) 編譯,將預處理得到的源代碼文件,進行“翻譯轉換”,產生出機器語言的目標
程序,得到機器語言的彙編文件;
(3) 彙編,將彙編代碼翻譯成了機器碼,但是還不可以運行;
(4) 鏈接,處理可重定位文件,把各種符號引用和符號定義轉換成爲可執行文件中
的合適信息,通常是虛擬地址。

下面根據 hello.c 這個示例,跟蹤一下其中的細節。
1)預處理
在 gcc 命令加上-E 參數,可以得到預處理文件。輸入下列命令:
vmuser@Linux-host:hello$ gcc -E hello.c –o hello.i
將會產生 hello.i 文件,這就是 hello.c 經過預處理後的文件。實際操作結果見下圖所示:

 

預編譯得到 hello.i 文件
一個原本連同空行才 8 行的代碼,經過預處理,得到了一個 800 多行的預處理文件,文
件開的內容如圖所示:

hello.i 文件末尾處的內容如圖所示:

其餘部分內容請用 Vi 打開後進行查看。可以看到, hello.c 經過預處理後得到的 hello.i
文件,除了原本的幾行代碼之外,還包含了很多額外的變量、函數等等,這些都是預處理器
處理的結果。

2)編譯
在 gcc 編譯參數加上-S,可以將 hello.i 編譯成 hello.s 文件。命令如下:
$ gcc -S hello.i

實際操作和結果如圖所示:

hello.s 是一個彙編文件,可用 Vi 編輯器打開查看,如下圖所示:

可以看到,該文件內容都是彙編語句。這裏不對彙編進行解釋。

 

(3)彙編
得到了彙編文件後,通過 gcc 就可以得到機器碼了。在終端輸入下列命令,可以得到hello.o 文件。
$ gcc -c hello.s
實際操作和結果如圖所示:

 

4)鏈接
儘管已經得到了機器碼,但這個文件卻還是不可以運行的,必須要經過鏈接才能運行。
在終端輸入下列命令,將會得到可執行文件 a.out。
$ gcc hello.o
操作和結果如圖所示:

a.out 是 gcc 默認輸出文件名稱,可以通過-o 參數指定新的文件名。例如加上“-o hello”
參數,將會生成 hello 文件,這個文件和 a.out 實際上是一樣的,用 md5sum 命令計算文件校
驗值,兩者完全一樣,如圖所示:

 

鏈接可分爲動態鏈接和靜態鏈接:

  • 動態鏈接使用動態鏈接庫進行鏈接,生成的程序在執行的時候需要加載所需的動態庫才能運行。動態鏈接生成的程序小巧,但是必須依賴動態庫,否則無法執行。
  • Linux 下的動態鏈接庫實際是共享目標文件(shared object),一般是.so 文件,作用類似於 Windows 下的.dll 文件。
  •  靜態鏈接使用靜態庫進行鏈接,生成的程序包含程序運行所需要的全部庫,可以直接運行,不過體積較大。
  • Linux 下靜態庫是彙編產生的.o 文件的集合,一般以.a 文件形式出現。gcc 默認是動態鏈接,加上-static 參數則採用靜態鏈接。再來看 hello.c 示例,在鏈接的時候加上-static 參數:

$ gcc hello.o -static -o hello_static
操作命令和結果如下圖所示,可以看到,動態鏈接生成的文件大小是 7155 字節,
而靜態鏈接生成的文件卻有 616096 字節,體積明顯大了很多。

總結:

上面的流程是分:預處理-編譯-彙編-鏈接的多個步驟對原文件進行編譯,我們也可以使用如下方法一次性編譯得到我們需要的可執行文件hello或者a.out,命令如下:

#得到a.out命令
$gcc hello.c

#得到hello的命令
$gcc hello.c -o hello

上面兩條命令可以直接得到我們最後的可執行文件結果,執行過程中它略過了預處理、編譯、彙編和鏈接過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章