读者或许会有疑问,为什么要用穿插编译器?穿插编译浅显地讲便是在一种渠道上编译出能运转在体系结构不同的另一种渠道上的程序,比如在PC渠道(X86 CPU)上编译出能运转在以ARM为内核的CPU渠道上的程序,编译得到的程序在X86 CPU渠道上是不能运转的,有必要放到ARM CPU渠道上才干运转,尽管两个渠道用的都是Linux体系。这种办法在异渠道移植和嵌入式开发时十分有用。相对与穿插编译,往常做的编译叫本地编译,也便是在当时渠道编译,编译得到的程序也是在本地履行。用来编译这种跨渠道程序的编译器就叫穿插编译器,相对来说,用来做本地编译的东西就叫本地编译器。所以要生成在方针机上运转的程序,有必要要用穿插编译东西链来完结。在削减和定制Linux内核用于嵌入式体系之前,因为一般嵌入式开发体系存储巨细有限,一般都要在性能优越的PC上树立一个用于方针机的穿插编译东西链,用该穿插编译东西链在PC上编译方针机上要运转的程序。穿插编译东西链是一个由编译器、衔接器和解说器组成的归纳开发环境,穿插编译东西链首要由binutils、gcc和glibc 3个部分组成。有时出于减小 libc 库巨细的考虑,也能够用其他 c 库来替代 glibc,例如 uClibc、dietlibc 和 newlib。树立穿插编译东西链是一个适当杂乱的进程,假如不想自己阅历杂乱繁琐的编译进程,网上有一些编译好的可用的穿插编译东西链能够下载,但就以学习为意图来说读者有必要学习自己制造一个穿插编译东西链。
构建ARM Linux穿插编译东西链
构建穿插编译器的第一个进程便是确认方针渠道。在GNU体系中,每个方针渠道都有一个清晰的格局,这些信息用于在构建进程中辨认要运用的不同东西的正确版别。因而,当在一个特定方针机下运转GCC时,GCC便在目录途径中查找包含该方针标准的应用程序途径。GNU的方针标准格局为CPU-PLATFORM -OS。例如x86/i386 方针机名为i686-pc-linux-gnu。本章的意图是叙述树立依据ARM渠道的穿插东西链,所以方针渠道名为arm-linux-gnu。
一般构建穿插东西链有3种办法。
办法一 分步编译和装置穿插编译东西链所需求的库和源代码,终究生成穿插编译东西链。该办法相对比较困难,适宜想深化学习构建穿插东西链的读者。假如只是想运用穿插东西链,主张运用办法二或办法三构建穿插东西链。
办法二 经过Crosstool脚本东西来完结一次编译生成穿插编译东西链,该办法相关于办法一要简略许多,而且犯错的时机也十分少,主张大多数情况下运用该办法构建穿插编译东西链。
办法三 直接经过网上(ftp.arm.kernel.org.uk)下载现已制造好的穿插编译东西链。该办法的长处不必多说,当然是简略省劲,但与此一起该办法有必定的坏处便是局限性太大,因为毕竟是他人构建好的,也便是固定的没有灵活性,所以构建所用的库以及编译器的版别或许并不适宜你要编译的程序,一起或许会在运用时呈现许多莫名的过错,主张读者慎用此办法。
为了让读者真实的学习穿插编译东西链的构建,下面将要点详细地介绍前两种构建ARM Linux穿插编译东西链的办法。
分步构建穿插编译链
分步构建,望文生义便是一步一步地树立穿插编译链,不同于2.2.2节中叙述的Crosstool脚本东西一次编译生成的办法,该办法适宜那些期望深化学习了解构建穿插编译东西链的读者。该办法相对来说难度较大,一般情况下困难重重,犹如唐僧西天取经,不过本文会尽或许详细地介绍构建的每一个进程,读者完全能够依据本节的内容自己独立实践,构建自己的穿插东西链。该进程所需的时刻较长,期望读者有较强的耐性和意志去学习和实践它,经过实践能够使读者愈加清楚穿插编译器的构建进程以及各个东西包的效果。该办法所需资源如表2.1所示。
装置包 下载地址 装置包 下载地址
linux-2.6.10.tar.gz ftp.kernel.org glibc-2.3.2.tar.gz ftp.gnu.org
binutils-2.15.tar.bz2 ftp.gnu.org glibc-linuxthreads-2.3.2.tar.gz ftp.gnu.org
gcc-3.3.6.tar.gz ftp.gnu.org
经过相关站点下载以上资源后,就能够开端树立穿插编译东西链了。
1.树立作业目录
首要树立作业目录,作业目录便是在什么目录下构建穿插东西链,目录的构建一般没有特其他要求,能够依据个人喜爱树立。以下所树立的目录是作者自界说的,当时的用户界说为mike,因而用户目录为/home/mike,在用户目录下首要树立一个作业目录(armlinux),树立作业目录的指令行操作如下:
# cd /home/mike
# mkdir armlinux
再在这个作业目录armlinux下树立3个目录 build-tools、kernel 和 tools。详细操作如下:
# cd armlinux
# mkdir build-tools kernel tools
其间各目录的效果如下。
● build-tools 用来寄存下载的binutils、gcc、glibc等源代码和用来编译这些源代码的目录;
● kernel 用来寄存内核源代码;
● tools 用来寄存编译好的穿插编译东西和库文件。
该进程的意图是为了便利重复输入途径,因为重复操作每件相同的作业总会让人觉得很费事,假如读者不习惯运用环境变量就能够略过该步,直接输入绝对途径就能够。声明以下环境变量的意图是在之后编译东西库的时分会用到,很便利输入,尤其是能够下降输错途径的危险。
# export PRJROOT=/home/mike/armlinux
# export TARGET=arm-linux
# export PREFIX=$PRJROOT/tools
# export TARGET_PREFIX=$PREFIX/$TARGET
# export PATH=$PREFIX/binPATH
留意,用export声明的变量是暂时的变量,也便是当刊出或替换了操控台,这些环境变量就消失了,假如还需求运用这些环境变量就有必要重复export操作,所以有时会很费事。值得幸亏的是,环境变量也能够界说在bashrc文件中,这样当刊出或替换操控台时,这些变量就一向有用,就不必老是export 这些变量了。
3.编译、装置Binutils
Binutils 是GNU东西之一,它包含衔接器、汇编器和其他用于方针文件和档案的东西,它是二进制代码的处理保护东西。装置Binutils东西包含的程序有 addr2line、ar、as、c++filt、gprof、ld、nm、objcopy、objdump、ranlib、readelf、size、 strings、strip、libiberty、libbfd和libopcodes。对这些程序的简略解说如下。
● addr2line 把程序地址转换为文件名和行号。在指令行中给它一个地址和一个可履行文件名,它就会运用这个可履行文件的调试信息指出在给出的地址上是哪个文件以及行号。
● ar 树立、修正、提取归档文件。归档文件是包含多个文件内容的一个大文件,其结构确保了能够康复原始文件内容。
● as 首要用来编译GNU C编译器gcc输出的汇编文件,发生的方针文件由衔接器ld衔接。
● c++filt 衔接器运用它来过滤 C++ 和 Java 符号,避免重载函数抵触。
● gprof 显现程序调用段的各种数据。
● ld 是衔接器,它把一些方针和归档文件结合在一起,重定位数据,并衔接符号引证。一般,树立一个新编译程序的最终一步便是调用ld。
● nm 列出方针文件中的符号。
● objcopy 把一种方针文件中的内容仿制到另一种类型的方针文件中。
● objdump 显现一个或许更多方针文件的信息。运用选项来操控其显现的信息,它所显现的信息一般只要编写编译东西的人才感兴趣。
● ranlib 发生归档文件索引,并将其保存到这个归档文件中。在索引中列出了归档文件各成员所界说的可重分配方针文件。
● readelf 显现elf格局可履行文件的信息。
● size 列出方针文件每一段的巨细以及整体的巨细。默许情况下,关于每个方针文件或许一个归档文件中的每个模块只发生一行输出。
● strings 打印某个文件的可打印字符串,这些字符串最少4个字符长,也能够运用选项-n设置字符串的最小长度。默许情况下,它只打印方针文件初始化和可加载段中的可打印字符;关于其他类型的文件它打印整个文件的可打印字符。这个程序关于了解非文本文件的内容很有协助。
● strip 丢掉方针文件中的悉数或许特定符号。
● libiberty 包含许多GNU程序都会用到的函数,这些程序有getopt、obstack、strerror、strtol和strtoul。
● libbfd 二进制文件描绘库。
● libopcode 用来处理opcodes的库,在生成一些应用程序的时分也会用到它。
Binutils东西装置依赖于Bash、Coreutils、Diffutils、GCC、Gettext、Glibc、Grep、Make、Perl、Sed、Texinfo等东西。
介绍完Binutils东西后,下面将分步介绍装置binutils-2.15的进程。
首要解压binutils-2.15.tar.bz2包,指令如下:
# cd $PRJROOT/build-tools
# tar –xjvf binutils-2.15.tar.bz2
接着装备Binutils东西,主张树立一个新的目录用来寄存装备和编译文件,这样能够使源文件和编译文件独立开,详细操作如下:
# cd $PRJROOT/build-tools
# mkdir build-binutils
# cd build-binutils
# ../ binutils-2.15/configure –target=$TARGET –prefix=$PREFIX
其间选项–target的意思是拟定生成的是 arm-linux 的东西,–prefix 是指出可履行文件装置的方位。履行上述操作会呈现许多check信息,最终发生 Makefile 文件。接下来履行make和装置操作,指令如下:
# make
# make install
该编译进程较慢,需求数十分钟,装置完结后检查/home/mike/armlinux/tools/bin目录下的文件,假如检查成果如下,标明此刻Binutils东西现已装置完毕。
# ls $PREFIX/bin
arm-linux-addr2line arm-linux-ld arm-linux-ranlib arm-linux-strip
arm-linux-ar arm-linux-nm arm-linux-readelf
arm-linux-as arm-linux-objcopy arm-linux-size
arm-linux-c++filt arm-linux-objdump arm-linux-strings
编译器需求经过体系内核的头文件来取得方针渠道所支撑的体系函数调用所需求的信息。关于Linux内核,最好的办法是下载一个适宜的内核,然后仿制取得头文件。需求对内核做一个根本的装备来生成正确的头文件;不过,不需求编译内核。关于本例中的方针arm-linux,需求以下进程。
(1)在kernel目录下解压linux-2.6.10.tar.gz内核包,履行指令如下:
# cd $PRJROOT/kernel
# tar –xvzf linux-2.6.10.tar.gz
(2)接下来装备编译内核使其生成正确的头文件,履行指令如下:
# cd linux-2.6.10
# make ARCH=arm CROSS_COMPILE=arm-linux- menuconfig
其间ARCH=arm标明是以arm为体系结构,CROSS_COMPILE=arm-linux-标明是以arm-linux-为前缀的穿插编译器。也能够用config和xconfig来替代menuconfig,引荐用make menuconfig,这也是内核开发人员用的最多的装备办法。留意在装备时必定要挑选处理器的类型,这儿挑选三星的S3C2410(System Type->ARM System Type->/Samsung S3C2410),如图2.1所示。装备完退出并保存,检查一下内核目录中的include/linux/version.h和 include/linux/autoconf.h文件是不是生成了,这是编译glibc时要用到的,假如version.h 和 autoconf.h 文件存在,阐明生成了正确的头文件。
仿制头文件到穿插编译东西链的目录,首要需求在/home/mike/armlinux/tools/arm-linux目录下树立东西的头文件目录inlcude,然后仿制内核头文件到此目录下,详细操作如下:
# mkdir –p $TARGET_PREFIX/include
# cp –r $PRJROOT/kernel/linux-2.6.10/include/linux $TARGET_PREFIX/include
# cp –r $PRJROOT/kernel/linux-2.6.10/include/asm-arm $TARGET_PREFIX/include/asm
5.编译装置boot-trap gcc
这一步的意图首要是树立arm-linux-gcc东西,留意这个gcc没有glibc库的支撑,所以只能用于编译内核、BootLoader等不需求C库支撑的程序,后边创立C库也要用到这个编译器,所以创立它首要是为创立C库做预备,假如只想编译内核和BootLoader,那么装置完这个就能够到此完毕。装置指令如下:
# cd $PRJROOT/build-tools
# tar –xvzf gcc-3.3.6.tar.gz
# mkdir build-gcc
# cd gcc-3.3.6
# vi gcc/config/arm/t-linux
由所以第一次装置ARM穿插编译东西,没有支撑libc库的头文件,所以在gcc/config/arm/t- linux文件中给变量TARGET_LIBGCC2_CFLAGS增加操作参数选项-Dinhibit_libc -D__gthr_ posix_h来屏蔽运用头文件,不然一般默许会运用/usr/inlcude头文件。
将TARGET_LIBGCC2-CFLAGS = -fomit-frame-pointer –fPIC改为TARGET_LIBGCC2- CFLAGS=-fomit-frame-pointer–fPIC -Dinhibit_libc -D__gthr_posix_h
修正完t-linux文件后保存,紧接着履行装备操作,如下指令:
# cd build-gcc
# ../ build-gcc /configure –target=$TARGET –prefix=$PREFIX –enable-languages=c
–disable-threads –disable-shared
其间选项–enable-languages=c标明只支撑C言语,–disable-threads标明去掉thread功用,这个功用需求glibc的支撑。–disable-shared标明只进行静态库编译,不支撑同享库编译。
接下来履行编译和装置操作,指令如下:
# make
# make install
装置完结后,在/home/mike/armlinux/tools/bin下检查,假如arm-linux-gcc等东西现已生成,标明boot-trap gcc东西现已装置成功。
6.树立glibc库
glibc是GUN C库,它是编译Linux体系程序很重要的组成部分。装置glibc-2.3.2版别之前引荐先装置以下的东西:
● GNU make 3.79或更新;
● GCC 3.2或更新;
● GNU binutils 2.13或更新。
首要解压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
然后进行编译装备,glibc-2.2.3装备前有必要新建一个编译目录,不然在glibc-2.2.3目录下不答应进行装备操作,此处在$PRJROOT/build-tools目录下树立名为build-glibc的目录,装备操作 如下:
# cd $PRJROOT/build-tools
# mkdir build-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(Cross Compiler)变量设成刚编译完的gcc,用它来编译glibc。–prefix=”/usr”界说了一个目录用于装置一些与方针机器无关的数据文件,默许情况下是/usr/local目录。–enable-add-ons是告知glibc用linuxthreads包,在上面现已将它放入 glibc源代码目录中,这个选项等价于-enable-add-ons=linuxthreads。–with-headers告知glibc linux内核头文件的目录 方位。
装备完后就能够编译和装置 glibc了,详细操作如下:
# make
# make install
因为第一次装置的gcc没有穿插glibc的支撑,现在现已装置了glibc,所以需求从头编译来支撑穿插glibc。而且上面的gcc也只支撑C言语,现在能够让它一起支撑C言语还要和C++言语。详细操作如下:
# cd $PRJROOT/build-tools/gcc-2.3.6
# ./configure –target=arm-linux –enable-languages=c,c++ –prefix=$PREFIX
# make
# make install
装置完结后会发现在$PREFIX/bin目录下又多了arm-linux-g++ 、arm-linux-c++等文件。
# ls $PREFIX/bin
arm-linux-addr2line arm-linux-g77 arm-linux-gnatbind arm-linux-ranlib
arm-linux-ar arm-linux-gcc arm-linux-jcf-dump arm-linux-readelf
arm-linux-as arm-linux-gcc-3.3.6 arm-linux-jv-scan arm-linux-size
arm-linux-c++ arm-linux-gccbug arm-linux-ld arm-linux-strings
arm-linux-c++filt arm-linux-gcj arm-linux-nm arm-linux-strip
arm-linux-cpp arm-linux-gcjh arm-linux-objcopy grepjar
arm-linux-g++ arm-linux-gcov arm-linux-objdump jar
8.测验穿插编译东西链
到此为止,现已介绍完了用分步构建的办法树立穿插编译东西链。下面经过一个简略的程序测验刚刚树立的穿插编译东西链看是否能够正常作业。写一个最简略的hello.c源文件,内容如下:
#include
int main( )
{
printf(“Hello,world!\n”);
return 0;
}
经过以下指令进行编译,编译后生成名为hello的可履行文件,经过file指令能够检查文件的类型。当显现以下信息时标明穿插东西链正常装置了,经过编译生成了ARM体系可履行的文件。留意,经过该穿插编译链编译的可履行文件只能在ARM体系下履行,不能在依据X86的一般PC上履行。
# arm-linux-gcc –o hello hello.c
# file hello
hello: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.4.3,
dynamically linked (uses shared libs), not stripped
Crosstool 是一组脚本东西集,可构建和测验不同版别的gcc和glibc,用于那些支撑glibc的体系结构。它也是一个开源项目,下载地址是http: //kegel.com/crosstool。用Crosstool构建穿插东西链要比上述的分步编译简单得多,而且也便利许多,关于只是为了作业需求构建穿插编译东西链的读者主张运用此办法。用Crosstool东西构建所需资源如表2.2所示。
表2.2 所需资源
装置包 下载地址
crosstool-0.42.tar.gz [url]http://kegel.com/crosstool [/url]
linux-2.6.10.tar.gz ftp.kernel.org
binutils-2.15.tar.bz2 ftp.gnu.org
gcc-3.3.6.tar.gz ftp.gnu.org
glibc-2.3.2.tar.gz ftp.gnu.org
glibc-linuxthreads-2.3.2.tar.gz ftp.gnu.org
linux-libc-headers-2.6.12.0.tar.bz2 ftp.gnu.org
1.预备资源文件
首要从网上下载所需资源文件linux-2.6.10.tar.gz、binutils-2.15.tar.bz2、gcc-3.3.6.tar.gz、 glibc- 2.3.2.tar.gz、glibc-linuxthreads-2.3.2.tar.gz和linux-libc-headers- 2.6.12.0.tar.bz2。然后将这些东西包文件放在新建的/home/mike/downloads目录下,最终在/home/mike目录下解压crosstool-0.42.tar.gz,指令如下:
# cd /home/mike
# tar –xvzf crosstool-0.42.tar.gz
2.树立脚本文件
接着需求树立自己的编译脚本,起名为arm.sh,为了简化编写arm.sh,寻觅一个最接近的脚本文件demo-arm.sh作为模板,然后将该脚本的内容仿制到arm.sh,修正arm.sh脚本,详细操作如下:
# cd crosstool-0.42
# cp demo-arm.sh arm.sh
# vi arm.sh
修正后的arm.sh脚本内容如下:
#!/bin/sh
set -ex
TARBALLS_DIR=/home/mike/downloads # 界说东西链源码所寄存方位。
RESULT_TOP=/opt/crosstool # 界说东西链的装置目录
export TARBALLS_DIR RESULT_TOP
GCC_LANGUAGES=”c,c++” # 界说支撑C, C++言语
export GCC_LANGUAGES
# 创立/opt/crosstool目录
mkdir -p $RESULT_TOP
# 编译东西链,该进程需求数小时完结。
eval cat arm.dat gcc-3.3.6-glibc-2.3.2.dat sh all.sh –notest
echo Done.
3.树立装备文件
在arm.sh 脚本文件中需求留意arm.dat和gcc-3.3.6-glibc-2.3.2.dat两个文件,这两个文件是作为Crosstool的编译的装备文件。其间arm.dat文件内容如下,首要用于界说装备文件、界说生成编译东西链的称号以及界说编译选项等。
KERNELCONFIG=pwd/arm.config # 内核的装备
TARGET=arm-linux- # 编译生成的东西链称号
TARGET_CFLAGS=”-O” # 编译选项
gcc-3.3.6-glibc-2.3.2.dat文件内容如下,该文件首要界说编译进程中所需求的库以及它界说的版别,假如在编译进程中发现有些库不存在时,Crosstool会主动在相关网站上下载,该东西在这点上相对比较智能,也十分有用。
BINUTILS_DIR=binutils-2.15
GCC_DIR=gcc-3.3.6
GLIBC_DIR=glibc-2.3.2
GLIBCTHREADS_FILENAME=glibc-linuxthreads-2.3.2
LINUX_DIR=linux-2.6.10
LINUX_SANITIZED_HEADER_DIR=linux-libc-headers-2.6.12.0
将Crosstool的脚本文件和装备文件预备好之后,开端履行arm.sh脚原本编译穿插编译东西。详细履行指令如下:
# cd crosstool-0.42
# ./arm.sh
经过数小时的绵长编译之后,会在/opt/crosstool目录下生成新的穿插编译东西,其间包含以下内容:
arm-linux-addr2line arm-linux-g++ arm-linux-ld arm-linux-size
arm-linux-ar arm-linux-gcc arm-linux-nm arm-linux-strings
arm-linux-as arm-linux-gcc-3.3.6 arm-linux-objcopy arm-linux-strip
arm-linux-c++ arm-linux-gccbug arm-linux-objdump fix-embedded-paths
arm-linux-c++filt arm-linux-gcov arm-linux-ranlib
arm-linux-cpp arm-linux-gprof arm-linux-readelf
5.增加环境变量
然后将生成的编译东西链途径增加到环境变量PATH上去,增加的办法是在体系/etc/ bashrc文件的最终增加下面一行,如图2.2所示。
用Vi编辑器在bashrc文件中增加环境变量
export PATH=/opt/crosstool/gcc-3.3.6-glibc-2.3.2/arm-linux/bin:$PATH
设置完环境变量,也就意味着穿插编译东西链现已构建完结,然后就能够用2.2.1.8节中的办法进行测验刚刚树立的东西链,此处就不必再赘述。