GCC Inline ASM

在使用高级语言编写程序的时候,可以根据需要插入汇编程序,无须另外创建汇编文件。

在编写inline asm程序时,需要使用asm关键字,并将汇编程序用括号括起来,以分号结尾。格式如下:

asm(code : output operand list : input operand list : clobber list);

如果最右侧的部分为空,可以联通相邻的冒号一起省略。最简单的形式是只有指令部分,如:

asm("mov r0,r0");

复杂的形式则需要提供参数列表,GCC支持两种方式标注汇编指令参数,老的方式是使用数字,如%0便是第0个操作数,%1表示第一个操作数,以此类推;新的方式支持变量名,如%[result]或者$[value],这个名字和高级语言里面的变量没有任何关系。例子如下:

asm("vmsr fpscr,%[value]" : : [value] "r" (var));

或者

asm("vmsr fpscr, %0" : : "r" (var));

VMSR是ARM的一条VFP指令,上述汇编指令的意图是将变量var的值保存到FPSCR状态寄存器里面。由于改指令没有输出操作数,所以输出操作数部分为空,但是相应的冒号要保留。下面的例子则只有输出操作数,没有输入操作数和clobber list,在这种情况下,相应的冒号也可以省略。

asm("vmrs %[result], fpscr" : "=r" (var));

或者

asm("vmrs %0, fpscr" : "=r" (var));

VMRS指令将状态寄存器FPSCR的值保存到变量var里。注意输出操作数要使用”=“。

更多inline asm的知识,参见ARM GCC Inline Assembler Cookbook

undefined reference

在很久很久以前,只有GNU ld,后来Google贡献了一个gold,就有了两个linker。

Android 2.3里面的gcc 4.4.3默认搭配的是gold。

$ cd prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/arm-eabi/bin
$ ls -l ld*
-rwxr-xr-x 1 liang liang 32272032 2010-12-20 02:44 ld
-rwxr-xr-x 1 liang liang  3248778 2010-12-20 02:44 ld.bfd
-rwxr-xr-x 1 liang liang 32272032 2010-12-20 02:44 ld.gold

使用gold没有任何问题,但是如果把上面的ld换成ld.bfd,那么在链接system/media/opensles/tests下面的程序时,就会报如下错误:

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'

这说明gold和GNU ld的行为不完全一致。使用linker选项--allow-shlib-undefined可以忽略这个错误。ld手册上说:这个选项会使ld忽略so库内的未定义符号,但是对于命令行上其它.o文件内的未定义符号仍然报错。

因此,可以在相应的Android.mk文件里添加

LOCAL_LDFLAGS := -Wl,--allow-shlib-undefined

来解决这个问题。

尽管使用这个解决方法可以把影响减少到最小,但是因为要修改的地方较多,比较麻烦。最省事的方式是全局使用-fno-rtti选项,或者像某些帖子上建议的那样,把IAndroidEffect.c改名为IAndroidEffect.cpp,并在Android.mk里做相应修改。

说实话,我感觉opensles里面的东西有点怪,明明是C++程序,非要使用后缀名.c,然后编译的时候使用-x c++来当作C++程序编译。问题是这样编译就没有使用-fno-rtti,因此输出了rtti信息。当然Android的构建系统也不尽如人意,为啥要根据文件后缀名是.c还是.cpp来选择是否使用-fno-rtti呢?