先记下另外一个网站http://blog.sina.com.cn/s/blog_44543ce90100h6ya.html
硬件环境:
工作站:基于x86体系的个人计算机或其他同类的计算机系统。
目标平台:基于ARM v4I 内核体系结构的SoC系统,本文使用的目标系统是三星公司的S3C2410系列。
软件环境:
工作站:使用2.6.x内核的Linux系统,具备GCC 3.2以上编译环境,具备网络环境。用户须拥有系统的根用户(root)权限。本文使用的是Gentoo Linux系统,内核版本2.6.19,GCC版本4.1.1。
目标平台:无相关要求。
术语及约定:
Workstation:指运行在前述硬件和软件条件下的工作站的集合
Target:指基于ARM v4I架构设计的嵌入式系统平台
ARM架构:指ARM v4I架构
“注:”:作者提供的注解或需要注意的地方。
“代码:”:需要用户手工输入并执行的代码,每条命令都以“#”开头。
一.概述
当我们需要从源代码编译出一个能运行在ARM 架构上的程序的时候,我们可以有两种方法。第一种是像我们平常一样,使用相同架构机器上的编译器,编译出运行在同一架构上的程序。在嵌入式系统领域,这是较难实现的。嵌入式系统的在设计的时候由于考虑到功耗,体积等要求,性能往往较弱。在编译一些比较大规模的程序时候,嵌入式系统在编译上耗费的时间,是我们无法忍受的。因此,我们需要使用交叉编译技术。
交叉编译技术,是一种在一个异构平台上编译出目标平台程序的一种技术。理论上来说,交叉工具链可以用在任何两种异构的系统中,例如,我们可以构建出PowerPC-ARM工具链,Sun Sparc-x86工具链等等。目前交叉工具链一般用于目标平台计算能力较弱,需要其他计算能力较强的平台帮助产生可运行软件的场合。在基于ARM的嵌入式系统开发中,我们一般会使用x86架构的计算机系统作为工作站,故最为常用的是x86-ARM交叉工具链。 目前市场上存在着各种各样不用的x86-ARM交叉工具链,有ARM公司自家在RVDS开发工具中整合的armcc,有Microsoft 在Platform Builder中的armv4I 编译器等。一般来说,这些由商业公司提供的工具链,都不会附有工具链相关的源代码和制作方法,灵活性不足,并且它们一般都与整套开发系统捆绑销售使用,成本较高。为了更好的理解这种技术和降低成本,获得更好的灵活性,市场上有不少的公司使用国际开源组织GNU开发的工具链作为他们产品开发的工具。本文下面的章节就为大家展示一个完整的GNU工具链构建过程。其中可能涉及到一些GNU Linux系统的基本操作知识,如果大家对此认识不深,请自行查阅相关资料。
二.工具链原理及构建方法
每一个软件,在编译的过程中,都要经过一系列的处理,才能从源代码变成可执行的目标代码。这一系列处理包括:预编译,高级语言编译,汇编,连接及重定位。这一套流程里面用到的每个工具和相关的库组成的集合,就称为工具链(tool chain)。以GNU的开发工具GCC为例,它就包括了预编译器cpp,c编译器gcc,汇编器as,和连接器ld等。在GNU自己对工具链定义中,还加进了一套额外的用于处理二进制包的工具包binutils,整个工具链应该是GCC+binutils+Glibc,不过大家后面将会看到binutils其实与Glibc无关,它是可以被独立安装的,所以GNU工具链也可以狭义地被理解为GCC+Glibc。
一般的情况下,工具链运行的环境和它产生的目标代码的环境是一致的。例如我们在VC中编译一个程序,工具链运行在x86平台,产生的也是运行在x86平台上的代码。但实际上,工具链产生的目标代码的运行平台是可以跟工具链运行的环境不一致的。这种产生与运行环境不一致的目标代码的工具链,称为交叉工具链(cross-compiler tool chain),使用这种工具链的编译过程对应地被称为交叉编译(cross-compile)。在GNU中,一般在普通工具链名称的前面加上特定的前缀,以表示是什么类型的工具链。例如x86-arm的工具链,预编译器是arm-linux-cpp,c编译器arm-linux-gcc等。
要构建出一个交叉工具链,需要解决三个问题。一是这个工具链必须是可以运行在原工作站平台上的。二是我们需要更换一个与目标平台对应的汇编器,使得工具链能产生对应的目标代码,三是要更换一套与目标平台对应的二进制库,使得工具链在连接时能找到正确的二进制库。
一下是我们的解决方法。首先我们建一个目录,作为存放工具链的地方。因为binutils和Glibc头文件的独立性,我们先把他们放到这个目录里面。如示意图1所示
然后我们使用工作站平台的工具链,编译出一条交叉工具链,通过指定工具链的类型,我们可以控制该工具链汇编器的输出类型。但此时该工具链指向库依然是工作站平台的二进制库。此阶段称为GCC Stage1。如示意图2所示
接下来我们利用刚刚建立的GCC构建一个目标平台的二进制库。由于二进制库都是自包含的,所以我们无需担心这个二进制库中会调用工作站平台的库进而产生依赖问题。见示意图3
最后我们使用工作站平台的工具链重新编译一次GCC,使得GCC指向新编译出来的库,此阶段称为GCC Stage2。
三.具体实现
下面我们在一个安装GNU Linux 的workstation上安装一条用于编译ARM v4I程序的工具链。
首先我们设置一些环境变量,以方便我们日后的工作。同时建立一个目录以存放我们的工具链。建立一个Shell脚本文件并运行它。
代码:
TARGET=arm-unknown-linux-gnu
PREFIX=/usr/arm
SYSROOT=${PREFIX}/sysroot
export ARCH=arm
export CROSS_COMPILE=${TARGET}-
export PATH=$PATH:${PREFIX}/bin
mkdir -p ${PREFIX}/src
接下来我们获取相关的源代码和补丁包:
代码:
http://ftp.gnu.org/gnu/binutils/binutils-2.16.tar.gz /
http://ftp.gnu.org/gnu/gcc/gcc-3.4.4/gcc-3.4.4.tar.bz2 /
"http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/flow.c.diff?cvsroot=gcc&only_with_tag=csl-arm-branch&r1=1.563.4.2&r2=1.563.4.3" /
http://frank.harvard.edu/~coldwell/toolchain/t-linux.diff /
http://ftp.gnu.org/gnu/glibc/glibc-2.3.5.tar.gz /
http://ftp.gnu.org/gnu/glibc/glibc-linuxthreads-2.3.5.tar.gz /
http://frank.harvard.edu/~coldwell/toolchain/ioperm.c.diff /
http://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.10.tar.gz /
把它们都存放在src,目录下。
注:
你需要把
"http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/flow.c.diff?cvsroot=gcc&only_with_tag=csl-arm-branch&r1=1.563.4.2&r2=1.563.4.3" /
中的内容复制并保存为文件,取名flow.c.diff
接下来我们安装binutils,过程非常简单。
代码:
#cd ${PREFIX}/src
#tar xvfz binutils-2.16.tar.gz
#mkdir -p BUILD/binutils-2.16
#cd BUILD/binutils-2.16
../../binutils-2.16/configure --prefix=${PREFIX} --target=${TARGET} --with-sysroot=${SYSROOT} 2>&1 | tee configure.out
#make 2>&1 | tee make.out
#make install 2>&1 | tee -a make.out
下一步安装Linux header。
代码:
cd ${PREFIX}/src
tar xvfz linux-2.6.10.tar.gz
ln -s linux-2.6.10 linux
cd linux
make s3c2410_defconfig
make include/linux/version.h
mkdir -p ${SYSROOT}/usr/include
cp -a ${PREFIX}/src/linux/include/linux ${SYSROOT}/usr/include/linux
cp -a ${PREFIX}/src/linux/include/asm-arm ${SYSROOT}/usr/include/asm
cp -a ${PREFIX}/src/linux/include/asm-generic ${SYSROOT}/usr/include/asm-generic
下一步,我们安装Glibc 头文件。Glibc头文件在编译GCC的时候被用到。我们可以在下载回来中的Glibc找到这些头文件。需要注意的是,有两个头文件stubs.h和stdio_lim.h只有在编译Glibc时才会产生,我们建立一个空文件来解决这个问题。
代码:
#cd ${PREFIX}/src
#tar xvfz glibc-2.3.5.tar.gz
#patch -d glibc-2.3.5 -p1 <ioperm.c.diff
#cd glibc-2.3.5
#tar xvfz ../glibc-linuxthreads-2.3.5.tar.gz
#cd ..
#mkdir -p BUILD/glibc-2.3.5-headers
#cd BUILD/glibc-2.3.5-headers
../../glibc-2.3.5/configure --prefix=/usr --host=${TARGET} --enable-add-ons=linuxthreads --with-headers=${SYSROOT}/usr/include 2>&1 | tee configure.out
#make cross-compiling=yes install_root=${SYSROOT} install-headers 2>&1 | tee make.out
#touch ${SYSROOT}/usr/include/gnu/stubs.h
#touch ${SYSROOT}/usr/include/bits/stdio_lim.h
Glibc安装好后,我们开始编译GCC Stage1。其中有两个文件需要打补丁以解决一些bug。第一个补丁使得这次编译过程能产生出C运行时文件 crti.o 和 crtn.o。这两个文件在GCC Stage中被用到。第二个补丁用来解决编译过程中产生的编译器内部错误(ICE)问题。
代码:
#cd ${PREFIX}/src
#bunzip2 -c gcc-3.4.4.tar.bz2 | tar xvf -
#patch -d gcc-3.4.4 -p1 < flow.c.diff
#patch -d gcc-3.4.4 -p1 < t-linux.diff
#mkdir -p BUILD/gcc-3.4.4-stage1
#cd BUILD/gcc-3.4.4-stage1
../../gcc-3.4.4/configure --prefix=${PREFIX} --target=${TARGET} --enable-languages=c --with-sysroot=${SYSROOT} 2>&1 | tee configure.out
#make 2>&1 | tee make.out
#make install 2>&1 | tee -a make.out
使用刚编译出的GCC编译Glibc
代码:
#cd ${PREFIX}/src
# mkdir -p BUILD/glibc-2.3.5
#cd BUILD/glibc-2.3.5
BUILD_CC=gcc CC=${CROSS_COMPILE}gcc AR=${CROSS_COMPILE}ar RANLIB=${CROSS_COMPILE}ranlib AS=${CROSS_COMPILE}as LD=${CROSS_COMPILE}ld ../../glibc-2.3.5/configure --prefix=/usr --build=i386-redhat-linux --host=arm-unknown-linux-gnu --target=arm-unknown-linux-gnu --without-__thread --enable-add-ons=linuxthreads --with-headers=${SYSROOT}/usr/include 2>&1 | tee configure.out
#make 2>&1 | tee make.out
#make install_root=${SYSROOT} install
重新编译GCC,即GCCStage2
代码:
#cd ${PREFIX}/src
#mkdir -p BUILD/gcc-3.4.4
#cd BUILD/gcc-3.4.4
../../gcc-3.4.4/configure --prefix=${PREFIX} --target=${TARGET} --enable-languages=c --with-sysroot=${SYSROOT} 2>&1 | tee configure.out
#make 2>&1 | tee make.out
#make install 2>&1 | tee -a make.out
交叉工具链至此建立完成。