如何为嵌入式开发建立交叉编译环境

本文详细介绍如何为ARM架构的嵌入式系统搭建交叉编译环境。从选择合适的软件版本开始,逐步指导读者完成内核头文件、二进制工具(binutils)、初始编译器(bootstrapgcc)、C库(glibc)以及全套编译器(fullgcc)的配置与安装。
AI助手已提取文章相关产品:
如何为嵌入式开发建立交叉编译环境
2010年10月12日
  随着消费类电子产品的大量开发和应用和Linux操作系统的不断健壮和强大,嵌入式系统越来越多的进入人们的生活之中,应用范围越来越广。
  在裁减和定制Linux,运用于你的嵌入式系统之前,由于一般嵌入式开发系统存储大小有限,通常你都要在你的强大的pc机上建立一个用于目标机的交叉编译环境。这是一个由编译器、连接器和解释器组成的综合开发环境。交叉编译工具主要由 binutils、gcc 和 glibc 几个部分组成。有时出于减小 libc 库大小的考虑,你也可以用别的 c 库来代替 glibc,例如 uClibc、dietlibc 和 newlib。建立一个交叉编译工具链是一个相当复杂的过程,如果你不想自己经历复杂的编译过程,网上有一些编译好的可用的交叉编译工具链可以下载。
  下面我们将以建立针对arm的交叉编译开发环境为例来解说整个过程,其他的体系结构与这个相类似,只要作一些对应的改动。我的开发环境是,宿主机 i386-redhat-7.2,目标机 arm。
  这个过程如下
  1. 下载源文件、补丁和建立编译的目录
  2. 建立内核头文件
  3. 建立二进制工具(binutils)
  4. 建立初始编译器(bootstrap gcc)
  5. 建立c库(glibc)
  6. 建立全套编译器(full gcc)
  下载源文件、补丁和建立编译的目录
  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
  $mkdir embedded
  再在这个项目目录 embedded 下建立三个目录 build-tools、kernel 和 tools。
  build-tools-用来存放你下载的 binutils、gcc 和 glibc 的源代码和用来编译这些源代码的目录。
  kernel-用来存放你的内核源代码和内核补丁。
  tools-用来存放编译好的交叉编译工具和库文件。
  $cd embedded
  $mkdir build-tools kernel tools
  执行完后目录结构如下:
  $ls embedded
  build-tools kernel tools
  3. 输出和环境变量
  我们输出如下的环境变量方便我们编译(也可以在.bash_profile文件中加入这些环境变量)。
  $export PRJROOT=/home/liang/embedded
  $export TARGET=arm-linux
  $export PREFIX=$PRJROOT/tools
  $export TARGET_PREFIX=$PREFIX/$TARGET
  $export PATH=$PREFIX/bin:$PATH
  如果你不惯用环境变量的,你可以直接用绝对或相对路径。我如果不用环境变量,一般都用绝对路径,相对路径有时会失败。环境变量也可以定义在.bashrc文件中,这样当你logout或换了控制台时,就不用老是export这些变量了。
  体系结构和你的TAEGET变量的对应如下表
  
  你可以在通过glibc下的config.sub脚本来知道,你的TARGET变量是否被支持,例如:
  $./config.sub arm-linux
  arm-unknown-linux-gnu
  在我的环境中,config.sub 在 glibc-2.2.3/scripts 目录下。
  网上还有一些 HOWTO 可以参考,ARM 体系结构的《The GNU Toolchain for ARM Target HOWTO》,PowerPC 体系结构的《Linux for PowerPC Embedded Systems HOWTO》等。对TARGET的选取可能有帮助。
  4. 建立编译目录
  为了把源码和编译时生成的文件分开,一般的编译工作不在的源码目录中,要另建一个目录来专门用于编译。用以下的命令来建立编译你下载的binutils、gcc和glibc的源代码的目录。
  $cd $PRJROOT/build-tools
  $mkdir build-binutils build-boot-gcc build-gcc build-glibc gcc-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.bz2 build-gcc gcc-patch
  build-binutls build-glibc glibc-2.2.3.tar.gz
  build-boot-gcc gcc-2.95.3.tar.gz glibc-linuxthreads-2.2.3.tar.gz
  建立内核头文件
  把你从 www.kernel.org 下载的内核源代码放入 $PRJROOT /kernel 目录
  进入你的 kernel 目录:
  $cd $PRJROOT /kernel
  解开内核源代码
  $tar -xzvf linux-2.4.21.tar.gz
  或
  $tar -xjvf linux-2.4.21.tar.bz2
  小于 2.4.19 的内核版本解开会生成一个 linux 目录,没带版本号,就将其改名。
  $mv linux linux-2.4.x
  给 Linux 内核打上你的补丁
  $cd linux-2.4.21
  $patch -p1 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
  上面的修改方案来自IBM developerworks上的"如何为嵌入式开发建立交叉编译环境". 但我在编译过程中老是碰见"libgcc1.S:438: asm/unistd.h: No such file or directory"这样的错误. 原先以为是本地GCC的问题, 用gcc-2.95.3, gcc-3.3等编译, 还是如此. 又以为需要在这里设置内核头文件, 拷贝, 建立符号连接... 什么方法都试过, 还是不行。
  实际上, 应该这样修改t-linux:
  在TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC语句之前添加"T_CFLAGS = -Dinhibit_libc -D__gthr_posix_h"
  成为:
  T_CFLAGS = -Dinhibit_libc -D__gthr_posix_h
  TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC
  -Dinhibit_libc等同于在命令中加上"--with-newlib", 它告诉配置工具不要使用glibc, 因为当前的glibc并不是针对目标板的. 从字面上看, 它是告诉配置工具"使用一个新的c库来作为目标板的C链接库. 然而当前我们并没有可用的C链接库, 所以这个选项只是让GCC能够正确编译, 生成一个初始编译器而已. 随后可以选择C链接库.
  -D__gthr_posix_h我不知道是什么意思, 反正如果不更改上面的语句, 就会有如下的错误:
  ../../gcc-2.95.3/gcc/gthr-posix.h:37: pthread.h: No such file or directory
  可能是去掉posix thread支持吧.
  你如果没定义 -Dinhibit,编译时将会报如下的错误
  ../../gcc-2.95.3/gcc/libgcc2.c:41: stdlib.h: No such file or directory
  ../../gcc-2.95.3/gcc/libgcc2.c:42: unistd.h: No such file or directory
  make[3]: *** [libgcc2.a] Error 1
  make[2]: *** [stmp-multilib-sub] Error 2
  make[1]: *** [stmp-multilib] Error 1
  make: *** [all-gcc] Error 2
  如果没有定义 -D__gthr_posix_h,编译时会报如下的错误
  In file included from gthr-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: No such file or directory
  make[3]: *** [libgcc2.a] Error 1
  make[2]: *** [stmp-multilib-sub] Error 2
  make[1]: *** [stmp-multilib] Error 1
  make: *** [all-gcc] Error 2
  还有一种与-Dinhibit同等效果的方法,那就是在你配置configure时多加一个参数-with-newlib,这个选项不会迫使我们必须使用newlib。我们编译了bootstrap-gcc后,仍然可以选择任何c库。
  接着就是配置boostrap gcc, 后面要用bootstrap gcc 来编译 glibc 库。
  $cd ..; cd build-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
  $make all-gcc
  $make install-gcc
  我们来看看 $PREFIX/bin 里面多了哪些东西
  $ls $PREFIX/bin
  你会发现多了 arm-linux-gcc 、arm-linux-unprotoize、cpp 和 gcov 几个文件。
  Gcc-gnu 的 C 语言编译器
  Unprotoize-将 ANSI C 的源码转化为 K&R C 的形式,去掉函数原型中的参数类型。
  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 -xvzf glibc-2.2.3.tar.gz
  $tar -xzvf glibc-linuxthreads-2.2.3.tar.gz --directory=glibc-2.2.3
  然后进入 build-glibc 目录配置 glibc
  $cd build-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 变量设成你刚编译完的boostrap gcc,用它来编译你的glibc。--enable-add-ons是告诉glibc用 linuxthreads 包,在上面我们已经将它放入了 glibc 源码目录中,这个选项等价于 -enable-add-ons=linuxthreads。--with-headers 告诉 glibc 我们的linux 内核头文件的目录位置。
  配置完后就可以编译和安装 glibc
  $make
  $make install_root=$TARGET_PREFIX prefix="" install
  然后你还要修改 libc.so 文件
  将
  GROUP ( /lib/libc.so.6 /lib/libc_nonshared.a)
  改为
  GROUP ( libc.so.6 libc_nonshared.a)
  这样连接程序 ld 就会在 libc.so 所在的目录查找它需要的库,因为你的机子的/lib目录可能已经装了一个相同名字的库,一个为编译可以在你的宿主机上运行的程序的库,而不是用于交叉编译的。
  建立全套编译器(full gcc)
  在建立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++ 告诉 full gcc 支持 c 和 c++ 语言。
  然后编译和安装你的 full gcc
  $make all
  $make install
  我们再来看看 $PREFIX/bin 里面多了哪些东西
  $ls $PREFIX/bin
  你会发现多了 arm-linux-g++ 、arm-linux-protoize 和 arm-linux-c++ 几个文件。
  G++-gnu的 c++ 编译器。
  Protoize-与Unprotoize相反,将K&R C的源码转化为ANSI C的形式,函数原型中加入参数类型。
  C++-gnu 的 c++ 编译器。
  到这里你的交叉编译工具就算做完了,简单验证一下你的交叉编译工具。
  用它来编译一个很简单的程序 helloworld.c
  #include
  int main(void)
  {
  printf("hello world\n");
  return 0;
  }
  $arm-linux-gcc helloworld.c -o helloworld
  $file helloworld
  helloworld: ELF 32-bit LSB executable, ARM, version 1,
  dynamically linked (uses shared libs), not stripped
  上面的输出说明你编译了一个能在 arm 体系结构下运行的 helloworld,证明你的编译工具做成功了。

您可能感兴趣的与本文相关内容

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值