嵌入式交叉编译工具
交叉编译工具是为了使在上位机中编译的文件能够在不同平台的目标机中执行,搭建交叉编译环境是嵌入式开发的第一步,也是关键的一步。不同的体系结构、不同的操作系统,甚至是不同版本的内核,都会用到不同的交叉编译器。选择交叉编译器非常重要, 有些交叉编译器经常会有部分的 BUG,都会导致最后的代码无法正常运行。
1. 交叉编译工具安装
- 交叉编译器完整的安装一般涉及多个软件的安装(读者可以从 ftp://gcc.gnu.org/pub/下 载),包括 GNU binutils、gcc、glibc、glibc-linuxthreads 等软件,以及获得Linux内核头文件、安装Glibc头文件。
- binutils 主要用于生成一些 辅助工具,如 readelf、objcopy、objdump、as、ld 等;
- gcc 是用来生成交叉编译器的,主要 生成 arm-linux-gcc 交叉编译工具(应该说,生成此工具后已经搭建起交叉编译环境,可以 编译 Linux 内核了,但由于没有提供标准用户函数库,用户程序还无法编译);
- glibc 主要是 提供用户程序所使用的一些基本的函数库;
- glibc-linuxthreads 是线程相关函数库。
- 最初,交叉环境的搭建是需要手动一步步进行搭建的,搭建方法复杂,很多步骤都涉及对硬件平台的选择一次成功安装的过程需要长达数小时的时间。现在嵌入式平台社区或厂商一般会提供在各种平台上测试通过的交叉编译器,或把以上安装步骤全部写入脚本文件或者以发行包的形式提供,这样就大大方便了用户的使用。因此, 常见的交叉编译工具下载网站有:
- ARM官方:developer.arm.com
- linaro社区:www.linaro.org
- 为了大家理解,下面分别从手工自己搭建和直接下载安装现成发行包两种方法进行阐述:
1.1 自己手工搭建交叉编译工具链
-
建立存放工具、源码目录和设置相关的环境变量
mkdir /usr/local/arm //建立工作目录 cd /usr/local/arm //进入工作目录 mkdir -p /usr/local/arm/src //建立安装文件的目录 mkdir -p /usr/local/arm/sysroot //建立系统根目录 mkdir -p /usr/local/arm/bin //建立生成工具存放的目录 mkdir -p /usr/local/arm/build //建立编译目录 export HOST=i686-pc-linux-gnu //设置 HOST 环境变量 export TARGET=arm-linux-gnueabi //设置 TARGET 环境变量 export PREFIX=/usr/local/arm //设置 PREFIX 环境变量 export SYSROOT=/usr/local/arm/sysroot //设置 SYSROOT 环境变量 export PATH=$PATH:${PREFIX}/bin //添加生成工具目录到 PATH 中
-
从网站下载安装的源码包,并将获得的源码放到/usr/local/arm/src 目录下,需要下载的源码包如下表所示:
软件包名称 下 载 地 址 binutils-2.23.tar.bz2 http://ftp.gnu.org/gnu/binutils/ linux-2.6.33.3.tar.bz2 http://www.kernel.org/pub/linux/kernel/v2.6/ gcc-4.6.0.tar.bz2 http://ftp.gnu.org/gnu/gcc/gcc-4.6.0/ glibc-2.17.tar.gz http://ftp.gnu.org/gnu/glibc/ glibc-linuxthreads-2.3.6.tar.gz http://ftp.gnu.org/gnu/glibc/ -
编译 GNU binutils
- Binutils 是 GNU 工具之一,包括连接器、汇编器和其他用于目标文件和档案的工具,它是二进制代码的处理维护工具,安装 Binutils 工具包含的程序有 addr2line、 ar、 as、 c++ filt、gprof、 ld、 nm、 objcopy、 objdump、 ranlib、 readelf、 size、 strings、 strip、 libiberty、 libbfd和 libopcodes。
- 编译步骤:
cd ${PREFIX}/src tar xvfj binutils-2.23.tar.bz2 mkdir -p /usr/local/arm/build/binutils-2.23 cd /usr/local/arm/build/binutils-2.23 ../../src/binutils-2.23/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
-
–target=${TARGET} :这个选项跟–host 一起表示编译生成的可执行文件运行在 HOST 上面,但服务的对象是 TARGET,也就是说,用这些可执行文件连接和汇编出来的程序运行在 TARGET 上面。因为这里默认使用主机的 GCC 编译器,因此省略了–host 选项。
-
–prefix=${PREFIX} :告诉配置脚本当运行 make install 时,把编译好的东西安装在 PREFIX目录下;
-
–with-sysroot=${SYSROOT} :表示SYSROOT 为系统根目录,生成的相应库放在 SYSROOT/lib 目录下,可执行文件放在SYSROOT/sbin 下,配置文件放在 SYSROOT/etc 下,用户文件放在 SYSROOT/usr 下。
-
在上面的编译过程中如果出现问题,最好的方法就是,删除编译目录下的所有文件,删除 Binutils 目录, 重新解压 Binutils, 再重新开始安装。 安装成功后就会在/usr/local/arm/bin目录下生成下面的工具:
arm-linux-addr2line #把程序地址转换为文件名和行号。在命令行中给它一个地址和一个可执行文件名,它就会使用这个可执行文件的调试信息指出给出的地址上是哪个文件及行号。 arm-linux-c++filt #连接器使用它来过滤 C++ 和 Java 符号,防止发生重载函数冲突。 arm-linux-nm #列出目标文件中的符号。 arm-linux-ranlib #产生归档文件索引,并将其保存到这个归档文件中。在索引中列出了归档文件各成员所定义的可重分配目标文件 arm-linux-strings #打印某个文件的可打印字符串,这些字符串最少4个字符长,也可以使用选项-n设置字符串的最小长度。默认情况下,它只打印目标文件初始化和可加载段中的可打印字符;对于其他类型的文件它打印整个文件的可打印字符。这个程序对于了解非文本文件的内容很有帮助。 arm-linux-ar #用来建立、修改和提取归档文件。归档文件是包括了其他多个文件的一个较大的文件,从该文件中可以恢复其他文件内容。 arm-linux-gprof #显示程序调用段的各种数据。 arm-linux-objcopy #把一种目标文件中的内容复制到另一种类型的目标文件中 arm-linux-readelf #显示 elf 格式可执行文件的信息 arm-linux-strip #丢弃目标文件中的全部或者特定符号 arm-linux-as #用来编译gcc 输出的汇编文件,编译产生的目标文件再由连接器 ld 进行连接操作。 arm-linux-ld #把所有编译产生的目标文件和归档文件结合在一起,重新定位数据,并连接符号引用。 arm-linux-objdump #显示一个或者更多目标文件的信息。使用选项来控制其显示的信息 arm-linux-size #列出目标文件每一段的大小及总体的大小 arm-linux-libiberty #包含许多 GNU 程序都会用到的函数, 这些程序有 getopt、obstack、trerror、strtol 和 strtoul arm-linux-libbfd #二进制文件描述库 arm-linux-libopcode #用来处理 opcodes 的库,在生成一些应用程序的时候也会用到它。
-
获得 Linux 内核头文件,并将头文件安装在${SYSROOT}/usr/include 目录下
cd ${PREFIX}/src tar xvfj linux-2.6.33.3.tar.bz2 cd linux-2.6.33.3/ make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig #选择一个对应 CPU 的类型。 make include/linux/version.h mkdir -p ${SYSROOT}/usr/include cp -a ${PREFIX}/src/ linux-2.6.33.3/include/linux ${SYSROOT}/usr/include/linux cp -a ${PREFIX}/src/linux-2.6.33.3/arch/arm/include/asm ${SYSROOT}/usr/include/asm cp -a ${PREFIX}/src/linux-2.6.33.3/include/asm-generic ${SYSROOT}/usr/include/asm-generic
-
第4布主要用来选择一个CPU类型,如图所示
-
-
安装 Glibc 头文件
-
Glibc 是 GUN C 库,它是编译 Linux 系统程序的重要组成部分。在安装 GNU C 库前需要先安装其头文件。
cd ${PREFIX}/src tar xvfz glibc-2.17.tar.gz cd glibc-2.17 tar xvfz ../glibc-linuxthreads-2.3.6.tar.gz cd .. mkdir -p /usr/local/arm/build/glibc-2.17-headers cd /usr/local/arm/build/glibc-2.17-headers ../../src/glibc-2.17/configure --prefix=/usr --host=${TARGET} --enable-add-ons --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
-
-
安装 GCC 第一阶段
-
安装 GCC 共分为两个阶段,第一阶段是为了安装 ARM 交叉编译工具没有支持 libc 库的头文件。
cd ${PREFIX}/src tar xvfj gcc-4.6.0.tar.bz2 mkdir -p mkdir /usr/local/arm/build/gcc-4.6.0 cd /usr/local/arm/build/gcc-4.6.0 ../../src/gcc-4.6.0/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
-
-
安装 GNU C 库 (安装了 Glibc 后才能对 GCC 进行完全安装 )
cd ${PREFIX}/src mkdir -p /usr/local/arm/build/glibc-2.17 cd /usr/local/arm/build/glibc-2.17 BUILD_CC=gcc CC=${TARGET}-gcc AR=${TARGET}-ar RANLIB=${TARGET}-ranlib AS=${TARGET}-as LD=${TARGET}-ld ../../src/glibc-2.3.5/configure --prefix=/usr --build=i386-redhat-linux --host=arm-unknown-linux-gnu --target=armunknown-linux-gnu --without-__thread --enable-add-ons=linuxthreads --with-headers=${SYSROOT}/usr/include 2>&1 | tee configure.out make 2>&1 | tee make.out BUILD_CC=gcc CC=${TARGET}-gcc AR=${TARGET}-ar RANLIB=${TARGET}-ranlib AS=${TARGET}-as LD=${TARGET}-ld ../../src/glibc-2.17/configure --prefix=/usr build=${HOST} --host=${TARGET} –target=${TARGET} --without-__thread --enable-add-ons=linuxthreads --with-headers=${SYSROOT}/usr/include 2>&1 | tee make.out
-
完全安装 GCC
cd ${PREFIX}/src mkdir -p /usr/local/arm/build/gcc-4.6.0 cd /usr/local/arm/build/gcc-4.6.0 ../../src/gcc-4.6.0/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
-
删除源码目录、临时目录和一些中间目录,得到 arm-linux、 bin、 lib、 libexec 和share 目录。
- 编译交叉编译器是一个很耗时的工作,对于实际项目的作用并不大。除非在某些应用程序或者驱动模块已经通过测试进入成品库,而这些应用程序或驱动模块依赖某个版本 GCC 或 glibc,同时修改和测试应用程序或驱动模块的工作量相对非常复杂,此时可以选择需要的版本进行建立交叉编译环境。一般情况下,建议直接使用开发板厂商提供的交叉编译器,或者在网上下载稳定的交叉编译器。目前针对 2.4 内核的稳定版本为 2.95.3,针对 2.6 内核的稳定版本为 3.4.1。本文使用开发商提供的 4.4.3 版本,在后面的内核移植和驱动移植过程中使用的就是 4.4.3 版本。
1.2 安装现成交叉编译工具发行包
-
假设我们从网站已经下载好交叉编译工具链cross-4.3.2.bar.bz2了,如何安装使用呢?
-
1)解压缩到指定目录
$ mkdir –p /usr/local/arm /* 这是交叉编译器安装目录*/ $ cp cross-4.3.2.bar.bz2 /usr/local/arm $ cd /usr/local/arm $ tar jxvf cross-4.3.2.tar.g $ ls 4.3.2 $ ls 4.3.2/bin arm-none-linux-gnueabi-addr2line arm-none-linux-gnueabi-gfortran arm-none-linux-gnueabi-ar arm-none-linux-gnueabi-gprof arm-none-linux-gnueabi-as arm-none-linux-gnueabi-ld arm-none-linux-gnueabi-c++ arm-none-linux-gnueabi-ldd arm-none-linux-gnueabi-cc arm-none-linux-gnueabi-nm arm-none-linux-gnueabi-c++filt arm-none-linux-gnueabi-objcopy arm-none-linux-gnueabi-cpp arm-none-linux-gnueabi-objdump arm-none-linux-gnueabi-ct-ng.config arm-none-linux-gnueabi-populate arm-none-linux-gnueabi-g++ arm-none-linux-gnueabi-ranlib arm-none-linux-gnueabi-gcc arm-none-linux-gnueabi-readelf arm-none-linux-gnueabi-gcc-4.3.2 arm-none-linux-gnueabi-run arm-none-linux-gnueabi-gccbug arm-none-linux-gnueabi-size arm-none-linux-gnueabi-gcov arm-none-linux-gnueabi-sstrip arm-none-linux-gnueabi-gdb arm-none-linux-gnueabi-strings arm-none-linux-gnueabi-gdbtui arm-none-linux-gnueabi-stri
-
2)把交叉开发工具链的路径添加到环境变量 PATH 中
$ export PATH=$PATH:/usr/local/arm/4.3.2/bin
- 环境变量的配置文件有如下2个,随便添加到其中一个文件中即可
- profile 类文件:用户第一次登录时仅运行一次,profile 类文件包括每个用户主目录 下的.profile 文件和/etc/profile 等。用户再次登录时就会运行主目录下的.profile 文件的脚本。
- bashrc 类文件:每当打开 bash shell 时(如当打开一个虚拟终端时)运行该脚本文 件。bash 类文件包括每个用户主目录下的.bashrc 文件和/etc/bash.bashrc 等
- 环境变量的配置文件有如下2个,随便添加到其中一个文件中即可
-
3)确认配置交叉编译工具链成功
$ arm-linux-gcc –v /*查看交叉编译器的版本信息*/ arm-none-linux-gnueabi-gcc -v Using built-in specs. Target: arm-none-linux-gnueabi Configured with: /home/linux/crosstooll/toolchain_build/targets/src/gcc-4.3.2/configure --build=i686-build_pc-linux-gnu --host=i686-build_pc-linux-gnu --target=arm-none-linux-gnueabi --prefix=/usr/local/arm/4.3.2 --with-sysroot = /home/linux/toolchain/arm-none-linux-gnueabi//sys-root --enable-languages=c,c++,fortran --disable-multilib --with-arch=armv4t --with-cpu=arm9tdmi --with-tune=arm920t --with-float=soft --with-pkgversion=crosstool-NG-1.8.1-farsight --disable-sjlj-exceptions --enable-__cxa_atexit --disable-libmudflap --with-gmp=/home/linux/crosstooll/toolchain_build/ targets/arm-none-linux-gnueabi/build/static--with-mpfr=/home/linux/crosstooll/toolchain_build/targets/arm-none-linux-gnueabi/build/static--enable-threads=posix--enable-target-optspace--with-local-prefix=/home/linux/toolchain/arm-none-linux-gnueabi//sys-root --disable-nls --enable-symvers=gnu--enable-c99 --enable-long-long Thread model: posix gcc version 4.3.2
- 从上面打印的版本信息中可以看到==“–prefix=/usr/local/arm/ 4.3.2”==,这就是交叉编译器 安装的路径。它是在编译前通过 prefix 选项配置的,所以,这个工具链安装的路径必须是/usr/local/arm/4.3.2。
-
1.3 交叉编译器测试
-
在使用新建立的交叉编译器前需要对其进行简单的测试,查看其生成的文件是否可以移植到 ARM 平台的开发板上运行。其步骤如下:
-
将 arm-linux-gcc 添加到环境变量中(打开/etc/profile文件,找到”#Path manipulation“部分,添加 arm-linux-gcc 所在目录 ),修改后保存配置重启系统配置生效 。
# Path manipulation if [ "$EUID" = "0" ]; then pathmunge /sbin pathmunge /usr/sbin pathmunge /usr/local/sbin pathmunge /usr/local/arm/bin #修改添加的 fi
-
编写简单的测试程序,查看程序适应的体系结构。
#include <stdio.h> int main() { printf("test arm-linux-gcc"); return 0; }
-
对上面的程序进行交叉编译, 并查看其头文件信息, 看其运行属于哪种平台体系结构
arm-linux-gcc –o test test.c #交叉编译 test.c,生成 test readelf test –h #使用 readelf 命令查看头文件信息ELF 头: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2 s complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (可执行文件) Machine: ARM #可以看出该文件运行的环境为ARM Version: 0x1 入口点地址: 0x8338 程序头起点: 52 (bytes into file) Start of section headers: 4464 (bytes into file) 标志: 0x5000002, has entry point, Version5 EABI 本头的大小: 52 (字节) 程序头大小: 32 (字节) Number of program headers: 10 节头大小: 40 (字节) 节头数量: 30 字符串表索引节头: 27
标志: 0x5000002, has entry point, Version5 EABI
本头的大小: 52 (字节)
程序头大小: 32 (字节)
Number of program headers: 10
节头大小: 40 (字节)
节头数量: 30
字符串表索引节头: 27
-
-