如何为嵌入式开发建立交叉编译环境.docx
- 文档编号:11834601
- 上传时间:2023-04-05
- 格式:DOCX
- 页数:19
- 大小:28.45KB
如何为嵌入式开发建立交叉编译环境.docx
《如何为嵌入式开发建立交叉编译环境.docx》由会员分享,可在线阅读,更多相关《如何为嵌入式开发建立交叉编译环境.docx(19页珍藏版)》请在冰豆网上搜索。
如何为嵌入式开发建立交叉编译环境
如何为嵌入式开发建立交叉编译环境
文档选项
窗体顶端
窗体底端
级别:
初级
恩梁元(sunix_yuanenliang@),软件工程师
2005年9月01日
在进行嵌入式开发之前,首先要建立一个交叉编译环境,这是一套编译器、连接器和libc库等组成的开发环境。
文章通过一个具体的例子说明了这些嵌入式交叉编译开发工具的制作过程。
随着消费类电子产品的大量开发和应用和Linux操作系统的不断健壮和强大,嵌入式系统越来越多的进入人们的生活之中,应用范围越来越广。
在裁减和定制Linux,运用于你的嵌入式系统之前,由于一般嵌入式开发系统存储大小有限,通常你都要在你的强大的pc机上建立一个用于目标机的交叉编译环境。
这是一个由编译器、连接器和解释器组成的综合开发环境。
交叉编译工具主要由binutils、gcc和glibc几个部分组成。
有时出于减小libc库大小的考虑,你也可以用别的c库来代替glibc,例如uClibc、dietlibc和newlib。
建立一个交叉编译工具链是一个相当复杂的过程,如果你不想自己经历复杂的编译过程,网上有一些编译好的可用的交叉编译工具链可以下载。
下面我们将以建立针对arm的交叉编译开发环境为例来解说整个过程,其他的体系结构与这个相类似,只要作一些对应的改动。
我的开发环境是,宿主机i386-redhat-7.2,目标机arm。
这个过程如下
1.下载源文件、补丁和建立编译的目录
2.建立内核头文件
3.建立二进制工具(binutils)
4.建立初始编译器(bootstrapgcc)
5.建立c库(glibc)
6.建立全套编译器(fullgcc)
下载源文件、补丁和建立编译的目录
1.选定软件版本号
选择软件版本号时,先看看glibc源代码中的INSTALL文件。
那里列举了该版本的glibc编译时所需的binutils和gcc的版本号。
例如在glibc-2.2.3/INSTALL文件中推荐gcc用2.95以上,binutils用2.10.1以上版本。
我选的各个软件的版本是:
linux-2.4.21+rmk2
binutils-2.10.1
gcc-2.95.3
glibc-2.2.3
glibc-linuxthreads-2.2.3
如果你选的glibc的版本号低于2.2,你还要下载一个叫glibc-crypt的文件,例如glibc-crypt-2.1.tar.gz。
Linux内核你可以从www.kernel.org或它的镜像下载。
Binutils、gcc和glibc你可以从FSF的FTP站点ftp:
//ftp.gun.org/gnu/或它的镜像去下载。
在编译glibc时,要用到Linux内核中的include目录的内核头文件。
如果你发现有变量没有定义而导致编译失败,你就改变你的内核版本号。
例如我开始用linux-2.4.25+vrs2,编译glibc-2.2.3时报BUS_ISA没定义,后来发现在2.4.23开始它的名字被改为CTL_BUS_ISA。
如果你没有完全的把握保证你改的内核改完全了,就不要动内核,而是把你的Linux内核的版本号降低或升高,来适应glibc。
Gcc的版本号,推荐用gcc-2.95以上的。
太老的版本编译可能会出问题。
Gcc-2.95.3是一个比较稳定的版本,也是内核开发人员推荐用的一个gcc版本。
如果你发现无法编译过去,有可能是你选用的软件中有的加入了一些新的特性而其他所选软件不支持的原因,就相应降低该软件的版本号。
例如我开始用gcc-3.3.2,发现编译不过,报as、ld等版本太老,我就把gcc降为2.95.3。
太新的版本大多没经过大量的测试,建议不要选用。
回页首
2.建立工作目录
首先,我们建立几个用来工作的目录:
在你的用户目录,我用的是用户liang,因此用户目录为/home/liang,先建立一个项目目录embedded。
$pwd
/home/liang
$mkdirembedded
再在这个项目目录embedded下建立三个目录build-tools、kernel和tools。
build-tools-用来存放你下载的binutils、gcc和glibc的源代码和用来编译这些源代码的目录。
kernel-用来存放你的内核源代码和内核补丁。
tools-用来存放编译好的交叉编译工具和库文件。
$cdembedded
$mkdirbuild-toolskerneltools
执行完后目录结构如下:
$lsembedded
build-toolskerneltools
3.输出和环境变量
我们输出如下的环境变量方便我们编译。
$exportPRJROOT=/home/liang/embedded
$exportTARGET=arm-linux
$exportPREFIX=$PRJROOT/tools
$exportTARGET_PREFIX=$PREFIX/$TARGET
$exportPATH=$PREFIX/bin:
$PATH
如果你不惯用环境变量的,你可以直接用绝对或相对路径。
我如果不用环境变量,一般都用绝对路径,相对路径有时会失败。
环境变量也可以定义在.bashrc文件中,这样当你logout或换了控制台时,就不用老是export这些变量了。
体系结构和你的TAEGET变量的对应如下表
你可以在通过glibc下的config.sub脚本来知道,你的TARGET变量是否被支持,例如:
$./config.subarm-linux
arm-unknown-linux-gnu
在我的环境中,config.sub在glibc-2.2.3/scripts目录下。
网上还有一些HOWTO可以参考,ARM体系结构的《TheGNUToolchainforARMTargetHOWTO》,PowerPC体系结构的《LinuxforPowerPCEmbeddedSystemsHOWTO》等。
对TARGET的选取可能有帮助。
4.建立编译目录
为了把源码和编译时生成的文件分开,一般的编译工作不在的源码目录中,要另建一个目录来专门用于编译。
用以下的命令来建立编译你下载的binutils、gcc和glibc的源代码的目录。
$cd$PRJROOT/build-tools
$mkdirbuild-binutilsbuild-boot-gccbuild-gccbuild-glibcgcc-patch
build-binutils-编译binutils的目录
build-boot-gcc-编译gcc启动部分的目录
build-glibc-编译glibc的目录
build-gcc-编译gcc全部的目录
gcc-patch-放gcc的补丁的目录
gcc-2.95.3的补丁有gcc-2.95.3-2.patch、gcc-2.95.3-no-fixinc.patch和gcc-2.95.3-returntype-fix.patch,可以从http:
//www.linuxfromscratch.org/下载到这些补丁。
再将你下载的binutils-2.10.1、gcc-2.95.3、glibc-2.2.3和glibc-linuxthreads-2.2.3的源代码放入build-tools目录中
看一下你的build-tools目录,有以下内容:
$ls
binutils-2.10.1.tar.bz2build-gccgcc-patch
build-binutlsbuild-glibcglibc-2.2.3.tar.gz
build-boot-gccgcc-2.95.3.tar.gzglibc-linuxthreads-2.2.3.tar.gz
回页首
建立内核头文件
把你从www.kernel.org下载的内核源代码放入$PRJROOT/kernel目录
进入你的kernel目录:
$cd$PRJROOT/kernel
解开内核源代码
$tar-xzvflinux-2.4.21.tar.gz
或
$tar-xjvflinux-2.4.21.tar.bz2
小于2.4.19的内核版本解开会生成一个linux目录,没带版本号,就将其改名。
$mvlinuxlinux-2.4.x
给Linux内核打上你的补丁
$cdlinux-2.4.21
$patch-p1<../patch-2.4.21-rmk2
编译内核生成头文件
$makeARCH=armCROSS_COMPILE=arm-linux-menuconfig
你也可以用config和xconfig来代替menuconfig,但这样用可能会没有设置某些配置文件选项和没有生成下面编译所需的头文件。
推荐大家用makemenuconfig,这也是内核开发人员用的最多的配置方法。
配置完退出并保存,检查一下的内核目录中的include/linux/version.h和include/linux/autoconf.h文件是不是生成了,这是编译glibc是要用到的,version.h和autoconf.h文件的存在,也说明了你生成了正确的头文件。
还要建立几个正确的链接
$cdinclude
$ln-sasm-armasm
$cdasm
$ln-sarch-epxaarch
$ln-sproc-armvproc
接下来为你的交叉编译环境建立你的内核头文件的链接
$mkdir-p$TARGET_PREFIX/include
$ln-s$PRJROOT/kernel/linux-2.4.21/include/linux$TARGET_PREFIX/include/linux
$in-s$PRJROOT/kernel/linux-2.4.21/include/asm-arm$TARGET_PREFIX/include/asm
也可以把Linux内核头文件拷贝过来用
$mkdir-p$TARGET_PREFIX/include
$cp-r$PRJROOT/kernel/linux-2.4.21/include/linux$TARGET_PREFIX/include
$cp-r$PRJROOT/kernel/linux-2.4.21/include/asm-arm$TARGET_PREFIX/include
回页首
建立二进制工具(binutils)
binutils是一些二进制工具的集合,其中包含了我们常用到的as和ld。
首先,我们解压我们下载的binutils源文件。
$cd$PRJROOT/build-tools
$tar-xvjfbinutils-2.10.1.tar.bz2
然后进入build-binutils目录配置和编译binutils。
$cdbuild-binutils
$../binutils-2.10.1/configure--target=$TARGET--prefix=$PREFIX
--target选项是指出我们生成的是arm-linux的工具,--prefix是指出我们可执行文件安装的位置。
会出现很多check,最后产生Makefile文件。
有了Makefile后,我们来编译并安装binutils,命令很简单。
$make
$makeinstall
看一下我们$PREFIX/bin下的生成的文件
$ls$PREFIX/bin
arm-linux-addr2linearm-linux-gasparm-linux-objdumparm-linux-strings
arm-linux-ararm-linux-ldarm-linux-ranlibarm-linux-strip
arm-linux-asarm-linux-nmarm-linux-readelf
arm-linux-c++filtarm-linux-objcopyarm-linux-size
我们来解释一下上面生成的可执行文件都是用来干什么的
add2line-将你要找的地址转成文件和行号,它要使用debug信息。
Ar-产生、修改和解开一个存档文件
As-gnu的汇编器
C++filt-C++和java中有一种重载函数,所用的重载函数最后会被编译转化成汇编的标号,c++filt就是实现这种反向的转化,根据标号得到函数名。
Gasp-gnu汇编器预编译器。
Ld-gnu的连接器
Nm-列出目标文件的符号和对应的地址
Objcopy-将某种格式的目标文件转化成另外格式的目标文件
Objdump-显示目标文件的信息
Ranlib-为一个存档文件产生一个索引,并将这个索引存入存档文件中
Readelf-显示elf格式的目标文件的信息
Size-显示目标文件各个节的大小和目标文件的大小
Strings-打印出目标文件中可以打印的字符串,有个默认的长度,为4
Strip-剥掉目标文件的所有的符号信息
回页首
建立初始编译器(bootstrapgcc)
首先进入build-tools目录,将下载gcc源代码解压
$cd$PRJROOT/build-tools
$tar-xvzfgcc-2.95.3.tar.gz
然后进入gcc-2.95.3目录给gcc打上补丁
$cdgcc-2.95.3
$patch-p1<../gcc-patch/gcc-2.95.3.-2.patch
$patch-p1<../gcc-patch/gcc-2.95.3.-no-fixinc.patch
$patch-p1<../gcc-patch/gcc-2.95.3-returntype-fix.patch
echotimestamp>gcc/cstamp-h.in
在我们编译并安装gcc前,我们先要改一个文件$PRJROOT/gcc/config/arm/t-linux,把
TARGET_LIBGCC2-CFLAGS=-fomit-frame-pointer-fPIC
这一行改为
TARGET_LIBGCC2-CFLAGS=-fomit-frame-pointer-fPIC-Dinhibit_libc-D__gthr_posix_h
你如果没定义-Dinhibit,编译时将会报如下的错误
../../gcc-2.95.3/gcc/libgcc2.c:
41:
stdlib.h:
Nosuchfileordirectory
../../gcc-2.95.3/gcc/libgcc2.c:
42:
unistd.h:
Nosuchfileordirectory
make[3]:
***[libgcc2.a]Error1
make[2]:
***[stmp-multilib-sub]Error2
make[1]:
***[stmp-multilib]Error1
make:
***[all-gcc]Error2
如果没有定义-D__gthr_posix_h,编译时会报如下的错误
Infileincludedfromgthr-default.h:
1,
from../../gcc-2.95.3/gcc/gthr.h:
98,
from../../gcc-2.95.3/gcc/libgcc2.c:
3034:
../../gcc-2.95.3/gcc/gthr-posix.h:
37:
pthread.h:
Nosuchfileordirectory
make[3]:
***[libgcc2.a]Error1
make[2]:
***[stmp-multilib-sub]Error2
make[1]:
***[stmp-multilib]Error1
make:
***[all-gcc]Error2
还有一种与-Dinhibit同等效果的方法,那就是在你配置configure时多加一个参数-with-newlib,这个选项不会迫使我们必须使用newlib。
我们编译了bootstrap-gcc后,仍然可以选择任何c库。
接着就是配置boostrapgcc,后面要用bootstrapgcc来编译glibc库。
$cd..;cdbuild-boot-gcc
$../gcc-2.95.3/configure--target=$TARGET--prefix=$PREFIX\
>--without-headers--enable-languages=c--disable-threads
这条命令中的-target、--prefix和配置binutils的含义是相同的,--without-headers就是指不需要头文件,因为是交叉编译工具,不需要本机上的头文件。
-enable-languages=c是指我们的boot-gcc只支持c语言。
--disable-threads是去掉thread功能,这个功能需要glibc的支持。
接着我们编译并安装boot-gcc
$makeall-gcc
$makeinstall-gcc
我们来看看$PREFIX/bin里面多了哪些东西
$ls$PREFIX/bin
你会发现多了arm-linux-gcc、arm-linux-unprotoize、cpp和gcov几个文件。
Gcc-gnu的C语言编译器
Unprotoize-将ANSIC的源码转化为K&RC的形式,去掉函数原型中的参数类型。
Cpp-gnu的C的预编译器
Gcov-gcc的辅助测试工具,可以用它来分析和优程序。
使用gcc3.2以及gcc3.2以上版本时,配置boot-gcc不能使用--without-headers选项,而需要使用glibc的头文件。
回页首
建立c库(glibc)
首先解压glibc-2.2.3.tar.gz和glibc-linuxthreads-2.2.3.tar.gz源代码
$cd$PRJROOT/build-tools
$tar-xvzfglibc-2.2.3.tar.gz
$tar-xzvfglibc-linuxthreads-2.2.3.tar.gz--directory=glibc-2.2.3
然后进入build-glibc目录配置glibc
$cdbuild-glibc
$CC=arm-linux-gcc../glibc-2.2.3/configure--host=$TARGET--prefix="/usr"
--enable-add-ons--with-headers=$TARGET_PREFIX/include
CC=arm-linux-gcc是把CC变量设成你刚编译完的boostrapgcc,用它来编译你的glibc。
--enable-add-ons是告诉glibc用linuxthreads包,在上面我们已经将它放入了glibc源码目录中,这个选项等价于-enable-add-ons=linuxthreads。
--with-headers告诉glibc我们的linux内核头文件的目录位置。
配置完后就可以编译和安装glibc
$make
$makeinstall_root=$TARGET_PREFIXprefix=""install
然后你还要修改libc.so文件
将
GROUP(/lib/libc.so.6/lib/libc_nonshared.a)
改为
GROUP(libc.so.6libc_nonshared.a)
这样连接程序ld就会在libc.so所在的目录查找它需要的库,因为你的机子的/lib目录可能已经装了一个相同名字的库,一个为编译可以在你的宿主机上运行的程序的库,而不是用于交叉编译的。
回页首
建立全套编译器(fullgcc)
在建立boot-gcc的时候,我们只支持了C。
到这里,我们就要建立全套编译器,来支持C和C++。
$cd$PRJROOT/build-tools/build-gcc
$../gcc-2.95.3/configure--target=$TARGET--prefix=$PREFIX--enable-languages=c,c++
--enable-languages=c,c++告诉fullgcc支持c和c++语言。
然后编译和安装你的fullgcc
$makeall
$makeinstall
我们再来看看$PREFIX/bin里面多了哪些东西
$ls$PREFIX/bin
你会发现多了arm-linux-g++、arm-linux-protoize和arm-linux-c++几个文件。
G++-gnu的c++编译器。
Protoize-与Unprotoize相反,将K&RC的源码转化为ANSIC的形式,函数原型中加入参数类型。
C++-gnu的c++编译器。
到这里你的交叉编译工具就算做完了,简单验证一下你的交叉编译工具。
用它来编译一个很简单的程序helloworld.c
#include
intmain(void)
{
printf("helloworld\n");
return0;
}
$arm-linux-gcchelloworld.c-ohelloworld
$filehelloworld
helloworld:
ELF32-bitLSBexecutable,ARM,version1,
dynamicallylinked(usessharedlibs),notstripped
上面的输出说明你编译了一个能在arm体系结构下运行的helloworld,证明你的编译工具做成功了。
参考资料
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 如何 嵌入式 开发 建立 交叉 编译 环境