gcc支持多种不同的语言,也支持多种不同的CPU架构。
在它的实现上,不同语言编译的实现是通过
conststruct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
这个结构体的不同定义来实现的。比如c语言的编译器就通过gcc/c-lang.c指定了lang_hooks这个结构体的一个实现。而C++语言编译器对此结构体的实现则放在gcc/cp/cp-lang.c中,同样在gcc目录下还可以发现gcc/objcp/objcp-lang.c和gcc/treelang/treetree.c这两个文件也提供了lang_hooks的不同实现。由此就可以很容易发现gcc所支持的编程语言。
而要生成不同目标CPU的代码,gcc则提供了另一个结构体:
struct gcc_target targetm = TARGET_INITIALIZER;
对此结构体的不同实现则是放在gcc/config/xxx/目录下的,其中xxx是具体的平台,如i386,bfin等等。
当然在后端,除了对gcc_target这个结构体提供不同的实现外,还必须重新定义一系列的宏,这些宏都是放在不同平台的config子目录下的,那么gcc如何知道要包含哪个目录下的头文件呢?
答案就在tm.h。以下是bf561交叉编译器下tm.h的内容:
#ifndef GCC_TM_H
#define GCC_TM_H
#ifdef IN_GCC
# include"options.h"
# include"config/bfin/bfin.h"
# include"config/dbxelf.h"
# include"config/elfos.h"
# include"config/bfin/elf.h"
# include"defaults.h"
#endif
#ifdefined IN_GCC && !defined GENERATOR_FILE && !defined USED_FOR_TARGET
# include"insn-constants.h"
# include"insn-flags.h"
#endif
#endif/* GCC_TM_H */
tm.h并不是gcc源码包的一部分,而是动态生成的,在gcc/gcc目录下的Makefile有这样的部分:
tm.h: cs-tm.h ; @true
…
cs-tm.h: Makefile
TARGET_CPU_DEFAULT="$(target_cpu_default)" /
HEADERS="$(tm_include_list)" DEFINES="$(tm_defines)" /
$(SHELL) $(srcdir)/mkconfig.sh tm.h
这说明tm.h的生成要靠mkconfig.sh将tm_include_list这个变量中列出的文件。
再看看tm_include_list的定义:
tm_include_list=options.h config/bfin/bfin.h config/dbxelf.h config/elfos.h config/bfin/elf.h defaults.h
这个变量也是在Makefile中定义的,但是Makefile是由gcc/configure动态生成的,所以再看看gcc/configure中的相关部分:
tm_file_list="options.h"
tm_include_list="options.h"
for f in $tm_file; do
case $f in
defaults.h )
tm_file_list="${tm_file_list} /$(srcdir)/$f"
tm_include_list="${tm_include_list} $f"
;;
* )
tm_file_list="${tm_file_list} /$(srcdir)/config/$f"
tm_include_list="${tm_include_list} config/$f"
;;
esac
done
离目标越来越近了,顺藤摸瓜,看看在config.gcc中对$tm_file的定义:
tm_file=${cpu_type}/${cpu_type}.h
…
case ${target} in
# Support site-specific machine types.
…
bfin*-elf*)
tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h"
tmake_file=bfin/t-bfin-elf
use_collect2=no
;;
bfin*-uclinux*)
tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h bfin/uclinux.h"
tmake_file=bfin/t-bfin-uclinux
use_collect2=no
;;
bfin*-linux-uclibc*)
tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h linux.h bfin/linux.h"
tmake_file="t-slibgcc-elf-ver bfin/t-bfin-linux"
use_collect2=no
;;
bfin*-*)
tm_file="${tm_file} dbxelf.h elfos.h bfin/elf.h"
tmake_file=bfin/t-bfin
use_collect2=no
;;
…
呵呵,最后看看cpu_type的定义:
cpu_type=`echo ${target} | sed 's/-.*$//'`
…
case ${target} in
…
bfin*-*)
cpu_type=bfin
;;
…
至此,终于真相大白!