SPEC
是gcc
定义的一套domain language,用于确定选项传递的规则,具体含义参见gcc.c
这个文件,搜索The SPEC Language即可。举个简单的例子,在目前的trunk上,gcc/config/linux-android.h
文件里定义了一个宏——ANDROID_CC1_SPEC
,内容如下:
#define ANDROID_CC1PLUS_SPEC \ "%{!fexceptions:%{!fno-exceptions: -fno-exceptions}} " \ "%{!frtti:%{!fno-rtti: -fno-rtti}}"
我们只看rtti
一行,这句话的意思是,如果gcc
或者g++
的命令行上既没有-frtti
,也没有-fno-rtti
,那么调用cc1plus
的时候就把选项-fno-rtti
加上。这句话在其它的CC1_SPEC
上是没有的。
正是这段代码,导致编译Android 2.3时,Google gcc能够正常编译,而使用其它的gcc则会遇到如下的错误:
out/target/product/generic/obj/lib/libOpenSLES.so: undefined reference to `typeinfo for android::SortedVectorImpl'
out/target/product/generic/obj/lib/libOpenSLES.so: undefined reference to `vtable for __cxxabiv1::__vmi_class_type_info'
网上提供的一些方法多是把system/media/opensles/libopensles/IAndroidEffect.c
改为IAndroidEffect.cpp
,这样做的效果是导致编译该文件的命令行发生了变化。
Android在编译C和C++程序(根据文件后缀名判断断)时分别使用不同的命令行选项,编译C的方式如下(可忽略):
define transform-c-or-s-to-o-no-deps @mkdir -p $(dir $@) $(hide) $(PRIVATE_CC) \ $(foreach incdir, \ $(PRIVATE_C_INCLUDES) \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_TARGET_PROJECT_INCLUDES) \ $(PRIVATE_TARGET_C_INCLUDES) \ ) \ , \ -I $(incdir) \ ) \ -c \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_TARGET_GLOBAL_CFLAGS) \ $(PRIVATE_ARM_CFLAGS) \ ) \ $(PRIVATE_CFLAGS) \ $(1) \ $(PRIVATE_DEBUG_CFLAGS) \ -MD -o $@ $< endef define transform-c-to-o-no-deps @echo "target $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<" $(call transform-c-or-s-to-o-no-deps, ) endef define transform-c-to-o $(transform-c-to-o-no-deps) $(hide) $(transform-d-to-p) endef
而编译C++程序的方式如下:
define transform-cpp-to-o @mkdir -p $(dir $@) @echo "target $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<" $(hide) $(PRIVATE_CXX) \ $(foreach incdir, \ $(PRIVATE_C_INCLUDES) \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_TARGET_PROJECT_INCLUDES) \ $(PRIVATE_TARGET_C_INCLUDES) \ ) \ , \ -I $(incdir) \ ) \ -c \ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \ $(PRIVATE_TARGET_GLOBAL_CFLAGS) \ $(PRIVATE_TARGET_GLOBAL_CPPFLAGS) \ $(PRIVATE_ARM_CFLAGS) \ ) \ -fno-rtti \ $(PRIVATE_CFLAGS) \ $(PRIVATE_CPPFLAGS) \ $(PRIVATE_DEBUG_CFLAGS) \ -MD -o $@ $< $(hide) $(transform-d-to-p) endef
其中最大的区别就是编译C++使用了选项-fno-rtti
。
然而实际上IAndroidEffect.c
虽然文件后缀名为c,里面可是实实在在的C++程序,为了让编译器把.c文件当成C++程序编译,需要使用选项-x c++
。那么为什么改名前Google gcc能够正确编译而其它gcc不可以呢?我们可以使用选项-###
打印出gcc调用cc1plus时的选项看个究竟。通过分析发现,Google gcc调用cc1plus
时使用了-fno-rtti
,而其它gcc并没有使用该选项,因此默认是输出RTTI信息的。
这是因为Google gcc在默认情况下使用了ANDROID_CC1_SPEC
,而其它gcc没有。通过查看gcc/config/linux-android.h
文件我们知道,使用选项-mandroid
即可以启用这个宏。因此,如果编译器支持,我们可以把-mandroid
添加到android的TARGET_GLOBAL_CLFAGS
里面,来解决上述链接错误。如果编译器不支持-mandroid
选项,也可以在TARGET_GLOBAL_CFLAGS
里面添加-fno-rtti
选项,同样能够解决这个问题。