本节书摘来自华章出版社《深入分析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>