Android原生(Native)C开发之八:Toolchain环境搭建篇
Android开源已经有一段时间了,一直没有去研究它,一是没有时间,二是没有Linux环境去测试,三是块头太大了(源码2G,加编译要5G左右)。
最近项目差不多近尾声了,终于可以喘口气,有时来好好研究一下Android的源码了,就在WinXP中从网上下载了Android的源码,一开始只是想 看看自已感兴趣的部分(GUI、OpenGL ES、Audio等),后来在网上找到了一些在Cygwin下搭建原生开发环境的文章,于是就自已试了下,发现不要Linux、不下全部源码进行原生开发 完全是可行的!
首先,要装Cygwin这个“活宝”,网上有很多相关的文章,下载包时请选择中国的镜像:http://www.cygwin.cn,比较快,如下包一定要安装:gcc、make、Flex、bison、gettext、gettext-devel、textinfo ,另外一定要装 git 包,下源码就靠它了;
其次,Cygwin及git安装好了,进入Cygwin,输入git --version,如果能正确显示就表示git安装没有问题,就可以开始有选择地下载Android的源码了。Android的源码太多了,如果只为原 生开发,只要下载bionic及build两个包,先为Android源码建一个目录,进入此目录,再按如下步骤取源码:
1. bionic,Android没有用 glibc,用的是google自已写的 bionic Libc,小是小,快是快,但有一个最大问题,对C++的兼容不好(这是后话)。命令如下:
git clone git://android.git.kernel.org/platform/bionic.git
2. build,其实我们只是要参考Android的一些编译链接选项,命令如下:
git clone git://android.git.kernel.org/platform/build.git
3. system/core,其实我们只是要一个头文件:AndroidConfig.h,也可直接从android git web上下载:http://android.git.kernel.org/?p=platform/system/core.git;a=blob_plain;f=include/arch/linux-arm/AndroidConfig.h;hb=HEAD ,如果要下整个目录,命令如下:
git clone git://android.git.kernel.org/platform/system/core.git
更多Android项目信息请访问:http://android.git.kernel.org/,大家可根据自已的兴趣选择下载。
再次,下载Android订制的toolchain的源码(http://android.git.kernel.org/pub/android-toolchain-20081019.tar.bz2 ),编译步骤如下:
1.解压文件,并进入目录,执行如下命令配置要编译的target及安装的目录:
./configure --target=arm-eabi --prefix=/cygdrive/d/Android/cupcake/toolchain
虽然安装目录可稍后安装配,但推荐在配置时设置好,目录一定要是绝对路径,如要装在 D:/Android/cupcake/toolchain,则为:/cygdrive/d/Android/cupcake/toolchain。
2.执行:make build 命令,如果有错误按提示再编译,一般没有什么大问题;
3.安装:make install。
Toolchain编译安装完成后,就要开始进行一些环境配置的动作了,网上说直接将bionic的一些头文件复制到 $toolchain/arm-eabi/include,一般情况是,但先执行如下看一下:
$arm-eabi-cpp -v
一般gcc会从如下3个目录去搜索头文件:
$toolchain/lib/gcc/arm-eabi/4.2.1/include
$toolchain/arm-eabi/sys-include
$toolchain/arm-eabi/include
如下头文件需从android源码目录复制到toolchain目录$toolchain/lib/gcc/arm-eabi/4.2.1/include:
bionic/libc/arch-arm/include
bionic/libc/include
bionic/libstdc++/include
bionic/libc/common
bionic/libc/arch-arm
bionic/libm/include
bionic/libm/include/arch/arm (好象找不到此目录,ignore it!)
bionic/libthread_db/include
其中,一些头文件有重复:limits.h一定要用bionic/libc/include目录的,endian.h 用 bionic/libc/arch-arm/include,再将system/core/include/arch/linux-arm目录下的 AndroidConfig.h文件复制到$toolchain/lib/gcc/arm-eabi/4.2.1/include。
头文件复制完成了,接下来复制库文件,库文件因没有所有源码不能通过编译得到,只好从模拟器中pull下来,这次要用到一个工具:busybox 。
从网上下载busybox 的可执行程序,启动模拟器,将busybox push上去,执行:
$adb push busybox /dev/sample/busybox
$adb shell chmod 777 /dev/sample/busybox
$adb shell ./dev/sample/busybox tar -cf /dev/sample/libs.tar /system/lib
$adb pull /dev/sample/libs.tar libs.tar
这样就将模拟器下的 /system/lib 目录的所有库(so)文件打包并下载下来了,解压libs.tar就得到了我们所需要的所有库文件,执行如下命令:
arm-eabi-ld --verbose 或 arm-eabi-ld --verbose | grep SEARCH_DIR
复制所有库文件到 SEARCH_DIR(一般是$toolchain/arm-eabi/lib)目录下,同时将 toolchain 的编译脚本 armelf.x armelf.xsc 从下载的Android的源码目录的 build/core 复制到同目录。
至此,环境基本上搭建完成,可以试着编译一个 hello world C 源码了。
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int i;
printf("argc/t = %d/n", argc);
for (i = 0; i < argc; i++) {
printf("argv[%d]/t = %s/n", i, argv[i]);
}
printf("Hello world!/n");;
return 0;
}
编译命令如下:
$ arm-eabi-gcc -nostdlib -Bdynamic -Wl,-T,armelf.x -Wl,-dynamic-linker,/system/bin/linker -include AndroidConfig.h -lc -o hello hello.c
得到警告:
warning: cannot find entry symbol _start; defaulting to 000082c8
没有入口函数 _start,加上再编译,成功了,push至模拟器执行看一下:
$ adb shell ./dev/sample/hello
argv[0] = (null)
argv[1] = (null)
argv[2] = (null)
argv[3] = (null)
argv[4] = (null)
argv[5] = (null)
argv[6] = (null)
argv[7] = (null)
argv[8] = (null)
argv[9] = (null)
argv[10] = (null)
argv[11] = ./dev/sample/hello
argv[12] = (null)
argv[13] =
argv[14] = (null)
[1] Segmentation fault ./dev/sample/hello
很不幸,Segmentation fault!好象加_start也不是那么有用,看样子非得要象build的makefile中说的,要crtbegin_dynamic.o和crtend.o,但没有编整个源码,怎么得到这两个文件呢?
答案就在下载的Android的源码bionic的目录中,进入$android_src/bionic/libc/arch-arm/bionic,你会发现在很多汇编文件,其中我们想要的 crtbegin_dynamic.S,crtend.S就在其中,编译它们:
arm-eabi-gcc -mthumb-interwork -o crtbegin_dynamic.o -c crtbegin_dynamic.S
arm-eabi-gcc -mthumb-interwork -o crtend.o -c crtend.S
不出意外,将得到 crtbegin_dynamic.o & crtend.o,再来编译我们的hello.c
$arm-eabi-gcc -nostdlib -Bdynamic -Wl,-T,armelf.x -Wl,-dynamic-linker,/system/bin/linker -include AndroidConfig.h -lc -o hello hello.c crtbegin_dynamic.o crtend.o
将生成的hello push到模拟器,再执行,如下:
$adb push hello /dev/sample/hello
$adb shell chmod 777 /dev/sample/hello
$adb shell ./dev/sample/hello A B C
输出如下:
argc = 4
argv[0] = ./dev/sample/hello
argv[1] = A
argv[2] = B
argv[3] = C
Hello world!
这正是我们想要看到的。