==================================================================== 3. 制作交叉工具链 3.1 什么是工具链 3.2 获取交叉工具链的几种途径 3.3 android工具链与gnu工具链的比较 每一个软件,在编译的过程中,都要经过一系列的处理,才能从源代码变成可执行的目标代码。这一系列处理包括:预编译,高级语言编译, 汇编,连接及重定位。这一套流程里面用到的每个工具和相关的库组成的集合,就称为工具链(tool chain)。以GNU的开发工具GCC为例, 它就包括了预编译器cpp,c编译器gcc,汇编器as,和连接器ld等。在GNU自己对工具链定义中,还加进了一套额外的用于处理二进制包的 工具包binutils,整个工具链应该是GCC+binutils+Glibc, binutils其实与Glibc关系不是很大,它可以被独立安装的,所以GNU工具 链也可以狭义地被理解为GCC+Glibc。 要构建出一个交叉工具链,需要解决三个问题。一是这个工具链必须是可以运行在原工作站平台上的。二是我们需要更换一个与目标平台对应的 汇编器,使得工具链能产生对应的目标代码,三是要更换一套与目标平台对应的二进制库,使得工具链在连接时能找到正确的二进制库。 3.2 获取交叉工具链的几种途径 3.2.1 利用源代码制作交叉工具链 网上直接下载工具链或者从方案商处获取(如:marvell) 下载地址: http://www.angstrom-distribution.org/unstable/ 3.2.2 用脚本制作工具链 3.2.2.1 croostool-0.43 http://www.kegel.com/crosstool/crosstool-0.43.tar.gz 制作工具链的源码包搭配情况: http://www.kegel.com/crosstool/crosstool-0.43/buildlogs/ 3.2.2.2 buildroot http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2 若想详细地了解buildroot可参考该文档http://buildroot.uclibc.org/buildroot.html 3.2.3 利用OE制作工具链 http://www.scratchbox.org/wiki/OpenEmbedded 3.3 android工具链与gnu工具链的比较 Android所用的Toolchain(即交叉编译工具链)可从下面的网址下载: http://android.kernel.org/pub/android-toolchain-20081019.tar.bz2。如果下载了完整的Android项目的源代码,则可以在 “<your_android>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin”目录下找到交叉编译工具,比如Android所用的 arm-eabi-gcc-4.2.1。Android并没有采用glibc作为C库,而是采用了Google自己开发的Bionic Libc,它的官方Toolchain也是基于 Bionic Libc而并非glibc的。这使得使用或移植其他Toolchain来用于Android要比较麻烦:在Google公布用于Android的官方Toolchain之前, 多数的Android爱好者使用的Toolchain是在http://www.codesourcery.com/gnu_toolchains/arm/download.html 下载的一个通用的 Toolchain,它用来编译和移植Android 的Linux内核是可行的,因为内核并不需要C库,但是开发Android的应用程序时,直接采用或者移植其他 的Toolchain都比较麻烦,其他Toolchain编译的应用程序只能采用静态编译的方式才能运行于Android模拟器中,这显然是实际开发中所不能接 受的方式。目前尚没有看到说明成功移植其他交叉编译器来编译Android应用程序的资料。 与glibc相比,Bionic Libc有如下一些特点: - 采用BSD License,而不是glibc的GPL License; - 大小只有大约200k,比glibc差不多小一半,且比glibc更快; - 实现了一个更小、更快的pthread; - 提供了一些Android所需要的重要函数,如”getprop”, “LOGI”等; - 不完全支持POSIX标准,比如C++ exceptions,wide chars等; - 不提供libthread_db 和 libm的实现 另外,Android中所用的其他一些二进制工具也比较特殊: - 加载动态库时使用的是/system/bin/linker而不是常用的/lib/ld.so; - prelink工具不是常用的prelink而是apriori,其源代码位于” <your_android>/build/tools/apriori” - strip工具也没有采用常用的strip,即“<your_android>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin” 目录下的arm-eabi-strip,而是位于<your_android>/out/host/linux-x86/bin/的soslim工具。 参考文档: CLFS2.0原理分析 http://www.linuxsir.org/bbs/showthread.php?t=267672 Cross-Compiled Linux From Scratch http://cross-lfs.org/view/clfs-sysroot/arm/ 全手工制作arm-linux交叉编译工具链《一》 http://blog.chinaunix.net/u2/62168/showart_1898748.html 自己制作arm-linux交叉编译环境(一)-scratch篇 http://blog.csdn.net/chenzhixin/archive/2007/01/12/1481442.aspx 如何建立交叉编译工具链 http://www.decell.org/article.asp?id=53 Android Toolchain与Bionic Libc http://www.top-e.org/jiaoshi/html/?151.html ndroid编译环境(2) - 手工编译C模块 ================================================= ================================================= 4. 软件编译常识 4.1 链接器和加载器 4.2 android 的标准链接器和加载器 4.3 Makefile基本语法 何为链接器和加载器? 链接器为ld,加载为ld-linux.so.2,两个的区别很大,一个编译时用,一个运行时用,ld负责在编译的搜索路径里找到要求的库,并查看 是否有提供了需要的 符号(如函数等),如果有,记录相关信息到程序中,由ld-linux.so.2在执行时查找到该库,并根据相关信息进行需 要符号的重定位等工作.注意 这两者的搜索库的方式是不同的。 动态连接器通常是指的动态加载器(不要与 Binutils 里的标准连接器 ld 混淆了)。动态连接器由 Glibc 提供,用来 找到并加载一个程序运行时所需的共享库,在做好程序运行的准备之后,运行这个程序。动态连接器的名称通常是 ld-linux.so.2,标准连接器 ld 由 Binutils 这个包提供。 标准连接器 查看gcc使用的标准连接器 mhf@mhf-desktop:/usr/local/marvell-arm-linux-4.1.1/bin$ arm-linux-gcc -print-prog-name=ld 编译时库的搜索路径,以下几种方式让连接器去找需要的库 1. 编译的时候明确指定,如: gcc test.c ./say.so -o test中的 ./say.so 2. 编译 Binutils 的时候通过LIB_PATH 变量指定, 如:make -C ld LIB_PATH=/tools/lib -C ld LIB_PATH=/tools/lib 这个选项重新编译 ld 子目录中的所有文件。在命令行中指定 Makefile 的 LIB_PATH 变量值,使它明确指向/tools/lib工具目录, 以覆盖默认值。这个变量的值指定了连接器的默认库搜索路径。 来源:Linux From Scratch - 版本 6.4第 5 章 构建临时系统 5.4. Binutils-2.18 - 第一遍 http://www.bitctp.org/lfsbook-6.4/chapter05/binutils-pass1.html 3. 在源码包configure的时候通过 --with-lib-path 指定,或者 --lib- path 例如: binutils-2.18/configure --prefix=/tools --disable-nls --with-lib-path=/tools/lib 配置选项的含义: --with-lib-path=/tools/lib 告诉配置脚本在为编译 Binutils 的过程中使用正确的库搜索路径,也就是将 /tools/lib 传递给连接器。这防止连接器搜索宿主系统中的库文件目录。 来源: Linux From Scratch - 版本 6.4 第 5 章 构建临时系统 lfs 5.13. Binutils-2.18 - 第二遍 http://www.bitctp.org/lfsbook-6.4/chapter05/binutils-pass2.html 4. 到 ld –verbose | grep SEARCH 列出的默认目录下去找 5. -L/usr/gpephone/lib 指定的目录找 经常以 LDFLAGS=" -L/usr/gpephone/lib -L/lib -L/usr/lib -L/usr/X11R7/lib" 的方式传入 参数 -rpath 与 -rpath-link 如果使用了'-rpath'选项, 那运行时搜索路径就只从'-rpath'选项中得到 'nodefaultlib'标志一个对象,使在搜索本对象所依赖的库时,忽略所有缺省库搜索路径. LDFLAGS="-Wl,-rpath-link=/usr/gpephone/lib/:/usr/gphone/lib:/usr/local/lib -L/usr/gpephone/lib -L/usr/gphone/lib" -rpath 与 -rpath-link 的特性: 1. 在编译的时候我们都可以使用这两个路径, 2. '-rpath'跟'-rpath_link'的不同之处在于,由'-rpath'指定的路径会被包含到可执行程序中,并在运行时使用, 而'-rpath-link'选项仅仅在链接时起作用。 -dumpspecs Display all of the built in spec strings -dumpversion Display the version of the compiler -dumpmachine Display the compiler's target processor -print-search-dirs Display the directories in the compiler's search path -print-prog-name=<prog> Display the full path to compiler component <prog> -specs=<file> Override built-in specs with the contents of <file> -Wa,<options> Pass comma-separated <options> on to the assembler -Wp,<options> Pass comma-separated <options> on to the preprocessor -Wl,<options> Pass comma-separated <options> on to the linker 从工具链内建的规范中查看动态加载器 gcc -dumpspecs | grep dynamic-linker //本机 查看编译起所指定的动态加载器 1. s3c2440 (arm9tdmi) 平台的工具链 /scratchbox/compilers/arm-9tdmi-softfloat-linux-gcc-3.4.4-glibc-2.3.5/bin/arm-softfloat-linux-gnu-gcc -dumpspecs | grep dynamic-linker /scratchbox/compilers/arm-softfloat-linux-gcc-3.4.4-glibc-2.3.5/bin/arm-softfloat-linux-gnu-gcc -dumpspecs | grep dynamic-linker 2. marvell 的工具链 /scratchbox/compilers/marvell-arm-linux-4.1.1/bin/arm-linux-gcc -dumpspecs | grep dynamic-linker 3. scrathbox 中工具链 host-gcc /scratchbox/compilers/host-gcc/bin/host-gcc -dumpspecs | grep dynamic-linker 如果我们在编译的时候给编译起 gcc 指定 -specs=/scratchbox/compilers/host-gcc/host-gcc.spec ,那么-specs指定 的规范将会覆盖工具链内建的规范。 cat /scratchbox/compilers/host-gcc/host-gcc.specs | grep ld 有如下内容: -dynamic-linker /scratchbox/host_shared/lib/ld.so /scratchbox/compilers/host-gcc/bin/gcc -specs=/scratchbox/compilers/host-gcc/host-gcc.specs mhf@mhf-desktop:/usr/local/marvell-arm-linux-4.1.1/arm-iwmmxt-linux-gnueabi/bin$ ./gcc -dumpspecs|grep dynamic-linker gcc -dumpspecs | sed 's@/lib/ld-linux.so.2@/tools&@g' | sudo tee `dirname $(gcc -print-libgcc-file-name)`/specs cat `dirname $(gcc -print-libgcc-file-name)`/specs | grep tools 查看本机应用程序使用的动态加载器 readelf -l /usr/bin/make | grep interpreter [Requesting program interpreter: /lib/ld-linux.so.2] 查看 scratchbox 中应用程序使用的动态加载器 readelf -l /scratchbox/tools/bin/make | grep interpreter [Requesting program interpreter: /scratchbox/host_shared/lib/ld.so] cd ~/svn/mohuifu.svn/trunk/mysource/compiler_test /scratchbox/compilers/host-gcc/bin/gcc -specs=/scratchbox/compilers/host-gcc/host-gcc.specs -o ld.so.test1 ld.so.test.c /scratchbox/compilers/host-gcc/bin/gcc -o ld.so.test2 ld.so.test.c readelf -l ./ld.so.test1 | grep interpreter readelf -l ./ld.so.test2 | grep interpreter 其他示例: readelf -l /scratchbox/tools/bin/make | grep interpreter readelf -l /usr/bin/make | grep interpreter 分别显示: [Requesting program interpreter: /scratchbox/host_shared/lib/ld.so] [Requesting program interpreter: /lib/ld-linux.so.2] 下面的方式也可以查看应用程序所使用的加载器 strings /scratchbox/tools/bin/make |grep lib strings /usr/bin/make |grep lib 分别为: /scratchbox/host_shared/lib/ld.so /lib/ld-linux.so.2 查看应程序加载器库的搜索路径 显示 scratchbox 中加载器的库搜索路径 strings /scratchbox/host_shared/lib/ld.so |grep lib display library search paths /scratchbox/host_shared/lib/ /scratchbox/tools/lib/ 显示本机中加载器的库搜索路径 strings /lib/ld-linux.so.2 |grep lib display library search paths /lib/ /usr/lib/ /lib/i486-linux-gnu/ /usr/lib/i486-linux-gnu/ ldd 验证应用程序所使用动态库 ldd /scratchbox/tools/bin/make libc.so.6 => /scratchbox/host_shared/lib/libc.so.6 (0xb7ef9000) /scratchbox/host_shared/lib/ld.so => /scratchbox/host_shared/lib/ld.so (0xb802f000) ldd /usr/bin/make librt.so.1 => /lib/tls/i686/cmov/librt.so.1 (0xb7fb9000) libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e5b000) libpthread.so.0 => /lib/tls/i686/cmov/libpthread.so.0 (0xb7e42000) /lib/ld-linux.so.2 (0xb7fd5000) 参考文档: 交叉编译中libtool相关的问题 http://hi.baidu.com/lieyu063/blog/item/9c99a2dd23e41f365882dd39.html 静态库和共享库库的定位搜索路径 http://blog.csdn.net/lwhsyit/archive/2008/08/26/2830783.aspx Linux动态连接原理 http://blog.chinaunix.net/u2/67984/showart_1359874.html 程序编译链接运行时对库关系的探讨(原创) http://www.360doc.com/content/061107/09/13188_251964.html http://lamp.linux.gov.cn/Linux/LFS-6.2/chapter05/toolchaintechnotes.html [Linux命令] ld 中文使用手册完全版(译) http://blog.csdn.net/rstevens/archive/2008/01/28/2070568.aspx scratchbox 是mameo (nokia) 提供的一个集成开发环境,可以去官方网站: http://www.scratchbox.org/ http://www.scratchbox.org/download/ 4.2 android 的标准链接器和加载器 android的标准链接器 ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-ld android 中标准连接器搜索库的路径 ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-ld -verbose | grep SEARCH SEARCH_DIR("/android/mathias/armdev/toolchain-eabi-4.2.1/arm-eabi/lib"); Android编译环境所用的交叉编译工具链是./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc, -I和-L参数指定了所用的C库头文件和动态库文件路径分别是bionic/libc /include 和out/target/product/generic/obj/lib, 其他还包括很多编译选项以及-D所定义的预编译宏。这里值得留意的是参数“-Wl,-dynamic-linker,/system/bin/linker”,它指定了 Android专用的动态链接器/system/bin/linker,而不是通常所用的ld.so。 上面的“make clean-$(LOCAL_MODULE)”是Android编译环境提供的make clean的方式。 android中应用程序使用的加载器 strings out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild | grep link /system/bin/linker ./prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc -dumpspecs|grep dynamic-linker %{mbig-endian:-EB} %{mlittle-endian:-EL} %{static:-Bstatic} %{shared:-shared} %{symbolic:-Bsymbolic} %{!static:%{shared: -Bsymbolic} %{!shared:%{rdynamic:-export-dynamic} %{!dynamic-linker:-dynamic-linker /system/bin/linker}}} -X android中加载器搜索库的路径 strings /nfsroot/rootfs/system/bin/linker | grep lib /system/lib /lib 生成的可执行程序可用file和readelf命令来查看一下: file out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), stripped readelf -d out/target/product/littleton/obj/EXECUTABLES/rild_intermediates/rild |grep NEEDED 0x00000001 (NEEDED) Shared library: [liblog.so] 0x00000001 (NEEDED) Shared library: [libcutils.so] 0x00000001 (NEEDED) Shared library: [libril.so] 0x00000001 (NEEDED) Shared library: [libc.so] 0x00000001 (NEEDED) Shared library: [libstdc++.so] 0x00000001 (NEEDED) Shared library: [libm.so] 0x00000001 (NEEDED) Shared library: [libdl.so] 这是ARM格式的动态链接可执行文件,运行时需要libc.so和libm.so。“not stripped”表示它还没被STRIP。嵌入式系统中为节省空间通常 将编译完成的可执行文件或动态库进行STRIP,即去掉其中多余的符号表信息。在前面“make helloworld showcommands”命令的最后我们也 可以看到,Android编译环境中使用了out/host/linux-x86/bin/soslim工具进行STRIP。 4.3 Makefile基本语法 Makefile详解(超级好) linux/Unix环境下的make和makefile详解 http://www.unlinux.com/doc/program/20051026/2365.html 跟我一起写 Makefile http://dev.csdn.net/develop/article/20/20025.shtm =================================================