搭建交叉编译环境是一项很繁琐和耗时的工作,查过资料后发现一篇描述很详尽的博客,以power pc为例,转载自
http://blog.sina.com.cn/s/blog_70dd16910100y7vj.html。
第一部分 概述
跟主流的PC软件开发工具一样,嵌入式系统开发需要编译器,链接器,解释器,以及其它的一下工具(这些工具可以集成到eclipse当中来方便用户开发使用,比如Windriver公司的workbench 3.1开发环境就是如此)。但是嵌入式系统开发工具的特殊之处在于交叉编译器运行于一个平台(我们的PC主机),而编译出来的程序却运行于另外一个平台(例如基于PowerPC或者ARM的目标机)。正是因为这种特性,我们才把这些工具称为跨平台的开发工具(cross-platform development tools)。
本篇博文,我将基于PowerPC平台来建立我们的交叉编译器,然后用我们新建的这个编译器编译一个简单的应用程序来测试能否正常编译。因为我搭建的这个编译器是用来编译基于PowerPC平台的Linux内核,所以我最后我将用这个编译器来编译Linux内核。
第二部分 搭建创建GNU交叉编译器的主机环境
2.1 主机环境和所需软件包
2.1.1主机环境:
Fedora 14 —Linux version 2.6.35.6-45.fc14.i686
2.1.2所需软件包:
GCC软件包:gcc-4.6.2.tar.bz2
下载网址:http://ftp.gnu.org/gnu/gcc/gcc-4.6.2/
Binnutils软件包: binutils-2.22.tar.gz
下载网址:http://ftp.gnu.org/gnu/binutils/
Glibc库:glibc-2.14.tar.bz2
Glibc库的针对于各个CPU的补丁程序:glibc-ports-2.14.tar.bz2
下载网址:http://ftp.gnu.org/gnu/glibc/
浮点运算C库MPFR(multiple-precision floating-point):mpfr-2.4.2.tar.bz2
它将会被集成到GCC软件包中
下载网址:http://ftp.gnu.org/gnu/mpfr/
复数运算C库:mpc-0.9.tar.gz
它将会被集成到GCC软件包中
下载网址:http://www.multiprecision.org/index.php?prog=mpc&page=download
该网站同时指出了mpc的最新版本,所依赖的mpfr和gmp的最低版本,比如网站主页指出,对于mpc-0.9.tar.gz版本,需求的gmp和mpfr的版本为:Gmp version 4.3.2 或者更高, Mpfr version 2.4.2或者更高
整数,有理数,浮点数算术库:gmp-4.3.2.tar.bz2
它将会被集成到gcc软件包中
下载网址:http://ftp.gnu.org/gnu/gmp/
备注:以上各个软件包,都是我从他们的官方网站上下载的到写这篇博文为止的稳定版本。
Linux内核的头文件:我选择的版本是linux-2.6.38
下载网址:ftp://ftp.kernel.org/pub/linux/kernel/v2.6/
2.1.3 各个软件包的关系
GUN交叉工具链主要有gcc,binutils,glibc构成,由于编译的工具链是针对PowerPC-Linux,所以我们需要linux基于PowerPC平台的头文件。
各个软件包在GNU交叉工具链中的逻辑关系如下图所示:
2.2 创建目录布局和环境变量
2.2.1 创建目录结构布局
我是以用户名tom登录Fedora 14系统(tom是我的英文名字,O(∩_∩)O~),所以我在/home/tom目录下创建target-project目录。
在Target-Project目录下创建powerpc-module目录
在powerpc-module目录下创建build-tools,kernel,tools
在build-tools目录下创建build-gcc,build-boot-gcc,build-glibc,build-binutils四个目录;
示意图如下:
在target-project目录下创建一个seten-powerpc.sh的脚本文件,内容如下:
#! /bin/sh
export PRJECT=powerpc-module
export PRJROOT=/home/tom/target-project/${PRJECT}
export TARGET=powerpc-tom-linux-gnu
export PREFIX=${PRJROOT}/tools
export TARGET_PREFIX=${PREFIX}/${TARGET}
export PATH=${PREFIX}/bin:${PATH}
pwd
cd $PRJROOT
执行命令:
$cd target-project
$sudo chmod +x ./ seten-powerpc.sh
备注:给seten-powerpc.sh加上可执行权限需要用到root权限。
$source ./ seten-powerpc.sh
备注:该命令使得seten-powerpc.sh中的环境变量在当前shell中可见,也可以使用
$. ./ seten-powerpc.sh 注意:两个点之间至少要有一个空格
示意图如下:
然后把2.1.2所提到的软件包全部下载到build-tools目录,同时把linux-2.6.38.tar.bz2下载到kernel目录。
第三部分 搭建过程
3.1 创建过程概述
这个GUN工具链的创建过程可以分为5步
Step 1:安装基于PowerPC的linux头文件;
备注:创建头文件是编译库文件所必须的,这一步只要在创建glibc库文件之前就可以了。
Step 2:安装binutils工具包;
Step 3:安装gcc引导编译器;
备注:这一步是创建一个最简单的基于powerpc的gcc编译器,该编译器只支持c语言。它是为Step 5完整编译gcc工具链做准备的。
Step 4:安装glibc库
Step 5:完整安装gcc编译器
备注:此时编译器将会支持c/c++语言。上面的5个步骤中,除了Step 1仅仅是拷贝头文件,Step 5不需要再次解压gcc压缩包之外(Step 3已经进行了解压了),其它的三个步骤都会包括:解压缩包,配置编译安装环境,编译,安装四个步骤。
3.2 安装GUN交叉编译器的详细过程
3.2.1 安装linux内核头文件
使用命令:
$cd $PRJROOT/kernel
$cd linux-2.6.38
$mkdir -p $TARGET_PREFIX/include
解释:在/home/tom/target-project/powerpc-module/tools/powerpc-tom-linux-gnu目录下建立include目录
$make mrproper
解释:确保linux-2.6.38内核源码是干净的。
$make ARCH=powerpc headers_check
解释:产生include/linux/version.h
$make ARCH=powerpc INSTALL_HDR_PATH=dest headers_install
解释:把powerpc构建的头文件安装在当前目录下得dest目录中
$cp -rv dest/include/* $TARGET_PREFIX/include
解释:再把dest目录中的所有内容移动到$TARGET_PREFIX/include目录中
$rm –rf dest
解释:删除dest目录
执行完的结果如下:
备注:除了使用上面的工具直接安装之外,也可以手动安装,使用下面的命令:
$mkdir -p ${TARGET_PREFIX}/include
$cp -r include/linux/ $TARGET_PREFIX/include
$cp -r include/asm-generic/ $TARGET_PREFIX
$cp -r arch/powerpc/include/asm/ $TARGET_PREFIX/include
3.2.2 安装binutils工具包
Binutils工具包中的工具主要用于操纵二进制obj文件,其中最重要的工具是GNU汇编器as和链接器ld。
Binutils工具包中的工具如下表所示:
工具 功能
as GNU的汇编器
ld GNU的链接器
gasp GNU汇编预处理器
ar 产生、修改和解开一个存档文件
nm 列出obj文件中的所有符号名
objcopy 将某种格式的目标文件转化成另外格式的目标文件
objdump 显示二进制obj文件的信息
ranlib 为一个存档文件产生一个索引,并将这个索引存入存档文件中
readelf 显示elf格式的obj文件信息
size 显示目标文件各个节的大小和目标文件的大小
strings 打印出目标文件中可以打印的字符串
strip 剥掉目标文件的所有的符号信息
c++filt 转换C++重载函数产生到汇编语言标签到对应的函数名
addr2line 把地址转成源文件中对应的行号
安装命令:
$ cd $PRJROOT/build-tools
$ tar xvf binutils-2.22.tar.gz
$ cd $PRJROOT/build-tools/build-binutils
$ ../ binutils-2.22/configure --target=$TARGET --prefix=$PREFIX
解释:
--target=$TARGET:指定工具包中工具的前缀
--prefix=$PREFIX:指定binutils工具包安装的目录
$make
$make install
执行成功后的结果如下图所示:
备注:这一步的编译和前后没有什么关联,所以基本上不会出错,O(∩_∩)O~
3.2.3 安装gcc引导器
和binutils工具包众多的工具相比,gcc软件包中只有一个gcc编译器,外加一些支持包,像运行库之类的。我们现在将要编译并安装的gcc引导器仅支持c语言,我们需要用gcc引导器来编译器glibc库,一旦在step 4中glibc库编译好之后,我们将会step 5中重新编译gcc软件包,以使它支持c/c++语言,“引导器”就是这么来的。
$cd $PRJROOT/build-tools
$ tar xvf gcc-4.6.2.tar.bz2
$tar xvf gmp-5.0.4.tar.bz2
$mv gmp-5.0.4 ./gcc-4.6.2/gmp
解释:解压缩gmp-5.0.4.tar.bz2,并把解压之后的文件移到到gcc之中
$tar xvf mpc-0.9.tar.gz
$mv mpc-0.9 ./gcc-4.6.2/mpc
$tar xvf mpfr-3.0.1.tar.gz
$mv mpfr-3.0.1 ./gcc-4.6.2/mpfr
$cd $PRJROOT/build-tools/build-boot-gcc
解释:进入build-boot-gcc目录
$ ../gcc-4.6.2/configure --target=$TARGET --prefix=$PREFIX --disable-shared --without-headers --with-newlib --enable-languages=c --disable-decimal-float --disable-libgomp --disable-libmudflap --disable-libssp --disable-threads --disable-multilib
解释:
--target=$TARGET --prefix=$PREFIX 同时3.2.2中binutils
--disable-shared:禁用创建共享库
--without-headers:不使用系统头文件,因为这是跨平台编译器,我们还没有建立目标板上的系统头文件。一旦glibc编译之后,我们再次编译gcc时,就是可以使用针对目标板上的系统头文件了。
--with-newlib:不适用c库文件,因为我们还没有建立glibc库。
--enable-languages=c:gcc引导器仅仅支持c语言
--disable-decimal-float:不支持浮点运算
--disable-libgomp:不创建libgomp使用的运行库
备注:关于libgomp,参考:http://gcc.gnu.org/onlinedocs/libgomp/
--disable-libmudflap:不创建libmudflap使用的运行库
备注:关于libmudflap,参考:http://gcc.gnu.org/wiki/Mudflap_Pointer_Debugging
--disable-libssp:不适用运行库的栈崩溃保护机制
--disable-threads:阻止gcc选择多线程保护的头文件
--disable-multilib:进程产生multilib库
接着执行:
$make all-gcc
解释:编译gcc引导器
$make all-target-libgcc
解释:编译libgcc库
$make install-gcc
解释:安装gcc引导器,安装在和binutils工具包相同的目录中。
$make install-target-libgcc
解释:安装libgcc到/home/tom/target-project/powerpc-module /tools/lib/gcc/powerpc-tom-linux-gnu/4.6.2/libgcc.a
执行结果如下:
Glibc库保护很多库文件,目标系统上几乎所有的程序的运行都要依赖这个库。要注意的是,我们通知所说的C库只是glibc中的一个库。
在正式我们要对glibc-2.14中的Makeconfig中的文件进行修改,使用命令如下:
$cd $PRJROOT/build-tools
$ tar xvf glibc-2.14.tar.bz2
$cd $PRJROOT/glibc-2.14
$cp -v Makeconfig{,.orig}
$sed -e 's/-lgcc_eh//g' Makeconfig.orig > Makeconfig
$cd ..
解释:上述命令的左右就是删除Makefileconfig文件中的-lgcc_eh选项,即就是把Makefileconfig中的第555行的内容:
libgcc_eh := -lgcc_eh $(libunwind)
该修改为:
libgcc_eh := $(libunwind)
所以如果不用命令的话,也可以手动删除-lgcc_eh
如果不删除的话,在编译glibc时,会出现“cannot find -lgcc_eh”的错误。
经过上面的修改之后,我们就可以执行命令来编译安装glibc库了。
$tar xvf glibc-ports-2.14.tar.bz2
$mv glibc-ports-2.14 ./glibc-2.14/ports
$cd $PRJROOT/build-tools/build-glibc
$CC=powerpc-tom-linux-gnu-gcc ../glibc-2.14/configure --host=$TARGET --prefix="/usr" --enable-add-ons --with-headers=$TARGET_PREFIX/include libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes
解释:
$CC=powerpc-tom-linux-gnu-gcc:我们现在编译的glibc库是运行在目标机器上,运行环境是目标机系统,所以我们的用的gcc编译器是我们的gcc引导器。
--host=$TARGET:因为我们的库是运行在目标机器上,所以从glibc库的角度来说,主机是目标平台。
--prefix="/usr":
--with-headers=$TARGET_PREFIX/include:使用我们在3.2.1安装的头文件;
libc_cv_forced_unwind=yes
libc_cv_c_cleanup=yes
备注:支持NPTL库,参考:http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library
然后接着执行命令:
$make
$make install_root=$TARGET_PREFIX prefix="" install
安装完毕之后的示意图如下:
现在我们将完全安装支持c/c++语言的gcc编译器,因为在3.2.3安装gcc引导编译器是已经解压了gcc安装包,现在只有直接编译安装就可以了。
但是在编译安装之前,我们要对3.2.4编译的$TARGE_PREFIX/lib目录下的两个库的配置文件进行修改。
第一个是:修改$TARGE_PREFIX/lib目录下的libc.so文件。
修改之前
将原内容:
OUTPUT_FORMAT(elf32-powerpc)
GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a AS_NEEDED ( /lib/ld.so.1 ) )
修改为:用$cp libc.so libc.so.orig备份一下libc.so
OUTPUT_FORMAT(elf32-powerpc)
GROUP (libc.so.6 libc_nonshared.a AS_NEEDED (ld.so.1 ) )
否则在完全编译gcc库时会出现“/bin/ld: cannot find /lib/libc.so.6…”之类的错误。
原因是:powerpc-tom-linux-gnu-ld会在Fedora 14的系统目录/lib中寻找我们在3.2.4中编译的glibc,而不是glibc库所在的$TARGE_PREFIX/lib中(即当前目录),所以我们要去除绝对路径。
第二个是:修改/$TARGET_PREFIX/lib目录下得libpthread.so
修改之前用$cp libpthread.so libpthread.so.orig备份一下libpthead.so
原内容:
OUTPUT_FORMAT(elf32-powerpc)
GROUP ( /lib/libpthread.so.0 /lib/libpthread_nonshared.a )
修改之后:
OUTPUT_FORMAT(elf32-powerpc)
GROUP ( libpthread.so.0 libpthread_nonshared.a )
否则会出现类似“configure: error: Pthreads are required to build libgomp”这类的错误,原因同上。
修改了这两个文件之后,我们就可以正式完整的gcc交叉编译器了,因为我们在2.3.3安装gcc引导编译器时,已经解压了gcc-4.6.2.tar.bz2压缩包,现在只要进入gcc-4.6.2目录即可。
使用下面的命令:
$cd ${PRJROOT}/build-tools/build-gcc
$../gcc-4.6.2/configure --target=$TARGET --prefix=$PREFIX --enable-languages=c,c++
$make all
解释:在3.2.3建立引导gcc编译器时,我们使用了make all-gcc命令,该命令只编译了gcc安装包(仅支持c语言),make all将会同时编译gcc和g++安装包,这样的会我们的编译器就会支持c/c++语言。
$make install
解释:在3.2.3建立引导gcc编译器时,我们使用了make install-gcc命令,现在用make install命令,原因同上。
编译完成之后的示意图:
至此我们的基于PowerPC的gcc交叉编译器已经顺利安装完成了,O(∩_∩)O~
第四部分 测试交叉编译器
4.1 编译一个小程序来验证我们的编译器
示意图如下:
4.2 编译我们基于PowerPC的linux-2.6.38内核
本篇博文中我用的是Linux-2.6.38内核头文件。下面我们将用上面搭建的这个交叉编译器来编译Linux-2.6.38内核。
因为在编译内核zImage镜像时会用到mkimage工具,所以我们先要在我们的Fedora 14系统中安装这个工具uboot-tools工具。
使用命令如下:
$yum search uboot-tools
#yum intall uboot-tools
备注:Fedora 14安装uboot-tools时用到root权限,O(∩_∩)O~
另外在Fedora 14下编译内核会用到ncurses-devel工具,可以用:
$sudo yum install ncurses-devel 进行安装
备注:如果你的Fedora 14没有给当前用户的sudo命令配置root权限的话你需要切换到root权限进行安装(即#yum install ncurses-devel ),而我的tom用户下是给sudo命题配置了root权限的,所以可以用sudo暂时获取root权限安装ncurses-devel,O(∩_∩)O~
在编译内核之前,我们需要对linux-2.6.38内核的几个Makefile文件进行一些处理:
第一件:删除linux-2.6.38/arch/powerpc/kernel/Makefile文件中的第7行中的-Werror:
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
改为:
subdir-ccflags-$(CONFIG_PPC_WERROR) :=
第二件:删除linux-2.6.38/arch/powerpc/mm/Makefile文件中的第5行中的-Werror:
subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror
改为:
subdir-ccflags-$(CONFIG_PPC_WERROR) :=
修改的原因:是这两处的配置文件,把gcc的warnings当做error来处理,当在这两个目录中的代码出现warings时,powerpc-tom-linux-gnu-gcc就会终止编译,所以要把这两个-Werror选项关闭!
执行命令:
$cd $PRJROOT/kernel/linux-2.6.38
$cp arch/powerpc/configs/86xx/sbc8641d_defconfig ./.config
$make ARCH=powerpc CROSS_COMPILE=powerpc-tom-linux-gnu- menuconfig
$make ARCH=powerpc CROSS_COMPILE=powerpc-tom-linux-gnu- uImage
执行完成之后的示意图:
至此,我们完整地再现了基于PowerPC平台的交叉工具链的搭建过程,整个过程花了我六个晚上的时间,再加上对博文的整理也花去了我3个晚上的时间,通过发现错误,解决错误,受益匪浅。
但是到现在为止我们仍然不能确定上面搭建的交叉编译器可以正常工作,只有等到我们把编译的linux kernel加上Rootfs能在我们的PowerPC目标机上跑起来。那到那时,我们才可以拍拍胸膛负责任的说:这个新建的这个编译器没有问题,毕竟实践是检验真理的唯一标准!!
现在我可以负责的告诉大家,我们用这个交叉编译器编译的基于MPC8641D的linux-2.6.38内核可以在飞思卡尔的MPC8641D开发版上顺利的跑起来,
示意图如下:
另外我在整个GNU交叉编译器的搭建过程中,除了安装uboot-tools使用root权限,其余的安装过程都是在普通用户tom的权限下完成的。我们不要滥用root权限(我在初学Linux时就犯了这个错误),因为用root权限很容易在搭建的过程中损换Fedora 14系统中自带的gcc编译器的相关文件,导致我们系统的gcc编译器不能正常工作!!
第六部分 参考资料
Karim Yaghmour.Building Embedded Linux Systems.Chapter 4. Development Tools
http://cross-lfs.org/view/CLFS-1.2.0/ppc/