本節書摘來自華章出版社《深入分析gcc 》一書中的第3章,第3.3節,作者 王亞剛 ,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。
3.3 gcc源代碼編譯
在獲得了gcc的源代碼後,為了生成目标機器上的編譯器程式,需要對源代碼進行編譯,一般步驟包括:
(1)使用conf?igure腳本完成編譯配置,生成makef?ile檔案。
(2)使用make工具編譯源代碼。
(3)使用make工具安裝生成的編譯程式等。
使用的典型腳本為:
這個過程一般很簡單,可以直接在${gcc_source}目錄下使用指令./conf?igure。該腳本會對gcc源代碼編譯、安裝的環境進行檢查,并根據conf?igure腳本的參數對編譯環境進行配置,進而最終生成makef?ile檔案,作為後續make工具進行編譯和安裝的依據。
然而,稍微仔細分析後會發現,這又是一個非常令人煩惱的過程,原因是這個配置指令具有較多的選項。可以使用./conf?igure --help檢視這些配置選項及其主要的功能說明。
下面對編譯配置中的一些主要選項進行簡單的說明。
(1)安裝目錄(installation directories)選項:
--pref?ix=prefix:用來指定目标機器無關代碼的安裝目錄,預設值為/usr/local。
--exec-pref?ix=eprefix:用來指定目标機器相關代碼的安裝目錄,一般與--pref?ix選項指定的prefix值相同。
(2)程式名稱(program names):
--program-pref?ix=prefix:設定安裝程式名的字首為prefix。
--program-suff?ix=suffix:設定安裝程式名的字尾為suffix。
例如,如果使用如下指令進行配置:
<code>[gcc@host1 gcc-4.4.0]$ ./configure --program-prefix=prefix-</code>
那麼最終生成的gcc編譯程式的名稱将變成:/usr/local/bin/prefix-gcc,其中prefix-gcc裡面的“prefix-”就是由--program-pref?ix=prefix-選項所指定的。
(3)系統類型(system types):
用來描述生成、運作及目标系統的系統類型,通常由--build、--host及--target三個選項指定,其中:
build=build:指定生成(build)編譯器程式的機器和作業系統平台資訊。
host=host:指定生成的編譯器程式所運作的機器和作業系統平台資訊,預設值與build相同。
target=target:指定生成的編譯器程式生成代碼所運作的機器和作業系統平台資訊,預設值與host相同。
關于這幾個選項的指定,通常會有如下的幾種組合方式:
1)build、host、target三者相同,這一般表示在本機進行gcc源代碼的編譯,生成的編譯器程式gcc也運作在與本機相同的機器和作業系統平台下,并且生成的編譯器編譯出的目标程式也将運作在同樣的系統環境中。
2)host與target相同,但host與build不同,這是一種典型的生成交叉編譯工具的情況,即在build主機環境上編譯gcc源代碼,生成的編譯器程式将運作在host主機環境中,并且該編譯器程式編譯生成的目标檔案也将運作在host,即target環境中。
3)buid、host及target各不相同,這是一種最複雜的情況,也屬于一種生成交叉編譯工具的情況,即在build主機環境上編譯gcc源代碼,生成的編譯器程式将運作在host主機環境中,并且該編譯器程式編譯生成的目标檔案将運作在另一種target機器環境中。
例3-1 通過makefile檔案檢視build、host及target系統的配置值
假設本例運作的主機資訊如下:
第一種情況,在gcc-4.4.0源代碼目錄中直接執行./conf?igure,那麼在生成makef?ile檔案中包含了如下資訊:
這種情況下,conf?igure腳本會自動根據系統的資訊“猜測”出build的值,例如本例中看到的build的值為“i686-pc-linux-gnu”,而host的預設值與build相同,target的預設值與host相同。此時三者的值都是相同的,這就是上述系統類型1)中的組合方式。
第二種情況,假如要在本機上對gcc源代碼進行編譯,生成的編譯器程式也運作在本機上,但編譯器需要為arm機器生成運作代碼,此時,編譯配置可以如下:
<code>[gcc@host1 gcc-4.4.0]$ ./configure --target=arm-linux-gnu</code>
或者:
生成的makef?ile檔案都包含了如下内容:
此時build與host相同,即用來編譯gcc的機器和将來要運作編譯器的機器類型是相同的,但是編譯器生成的代碼卻不在host主機系統上運作,而是要運作在一種arm-unknown-linux-gnu機器上,這就是一種典型的交叉編譯。
(4)可選編譯特性的運作與禁止。
一般使用--enable-feature[=arg]或者--disable-feature表示設定該feature的值為arg,或者禁止該特性。
(5)編譯時可選的一些包的配置。
一般使用--with-package[=arg]或者--without-package來指定包package的值或者禁止使用該包。例如使用--with-mpfr =path指定mpfr包的目錄。
(6)編譯時的環境變量。
這些環境變量可能包括編譯、彙編、連結等工具以及這些工具運作時的選項值,例如:
其中,cflags="-w -g -o0"就指定了編譯标志clags的部分值。表3-1給出了conf?igure腳本中常用環境變量的名稱和意義。

執行./conf?igure指令後,就生成了gcc-4.4.0目錄下的makef?ile檔案。此時,可以使用make工具來進行整個gcc源代碼的編譯過程。這個過程的執行異常複雜,為了了解整個編譯的詳細過程,建議讀者将編譯的過程重定向到檔案中,并結合makef?ile檔案進行仔細研讀。例如,可以采用如下指令形式:
gcc源代碼編譯生成一個在本機運作的編譯器時,通常采用一個叫做bootstrapping的技術,即将gcc源代碼的編譯過程分為多個階段,通常包括stage1、stage2及stage3三個階段。這三個階段的功能分别為:
(1)stage1:使用一個現有的編譯器将gcc的源代碼編譯,生成一個新的編譯器new_gcc1;
(2)stage2:使用new_gcc1重新編譯gcc的源代碼,生成另外一個新的編譯器new_gcc2;
(3)stage3:使用new_gcc2重新編譯gcc的源代碼,生成另外一個新的編譯器new_gcc3,并且對new_gcc2和new_gcc3進行比較,如果兩者相同,則表示gcc源代碼編譯成功,否則表示生成的編譯器存在bug。
采用上述過程的目的是充分保證編譯器的正确性。詳細内容可以參考如下文檔:
<a href="http://stackoverf?low.com/questions/9429491/how-are-gcc-g-bootstrapped">http://stackoverf?low.com/questions/9429491/how-are-gcc-g-bootstrapped</a>
<a href="https://gcc.gnu.org/install/build.html">https://gcc.gnu.org/install/build.html</a>
可以通過前面生成的make-process檔案中的部分内容驗證上述說法。
以下是各個階段生成的gcc檔案的對比。
使用現有編譯器生成的xgcc(即new_gcc1):
使用new_gcc1重新編譯gcc的源代碼,生成另外一個新的編譯器xgcc(即new_gcc2):
使用new_gcc2重新編譯gcc的源代碼,生成另外一個新的編譯器xgcc(即new_gcc3):
可以看出,兩個階段生成的編譯器gcc/xgcc與prev-gcc/xgcc的大小相同。
另外,還可以使用diff工具對兩個編譯器目标檔案進行對比,從指令的結果可以看出兩者是完全相同的,是以,本次gcc源代碼的編譯是成功的。
<code>[gcc@localhost host-i686-pc-linux-gnu]$ diff gcc/xgcc prev-gcc/xgcc</code>
gcc源代碼成功編譯後,生成的可執行程式及文檔等的安裝可以使用如下指令進行:
<code>make install</code>