学习方针:
● 了解穿插编译东西链
● 了解分步构建穿插编译东西链的办法
● 学会运用Crosstool东西构建穿插编译东西链
2.1 穿插编译东西链介绍
读者或许会有疑问,为什么要用穿插编译器?穿插编译浅显地讲便是在一种渠道上编译出能运转在体系结构不同的另一种渠道上的程式,比如在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.1 分步构建穿插编译链
分步构建,望文生义便是一步一步地树立穿插编译链,不同于2.2.2节中叙述的Crosstool脚本东西一次编译生成的办法,该办法适宜那些期望深化学习了解构建穿插编译东西链的读者。该办法相对来说难度较大,一般情况下困难重重,犹如唐僧西天取经,不过本文会尽或许周详地介绍构建的每一个进程,读者完全能依据本节的内容自己独立实践,构建自己的穿插东西链。该进程所需的时刻较长,期望读者有较强的耐性和意志去学习和实践他,经过实践能使读者愈加明晰穿插编译器的构建进程及各个东西包的效果。该办法所需资源如表2.1所示。
表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 用来寄存编译好的穿插编译东西和库文件。
2.树立环境变量
该进程的意图是为了便利重复输入途径,因为重复操作每件相同的作业总会让人觉得十分费事,假如读者不习惯运用环境变量就能略过该步,直接输入绝对途径就能。声明以下环境变量的意图是在之后编译东西库的时分会用到,十分便利输入,尤其是能下降输错途径的危险。
# export PRJROOT=/home/mike/armlinux
# export TARGET=arm-linux
# export PREFIX=$PRJROOT/tools
# export TARGET_PREFIX=$PREFIX/$TARGET
# export PATH=$PREFIX/bin:$PATH
留意,用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
4.取得内核头文件
编译器需求经过体系内核的头文件来取得方针渠道所支撑的体系函数调用所需求的信息。关于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)。设置完退出并保存,检查一下内核目录中的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
7.编译装置完好的gcc
因为第一次装置的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
2.2.2 用Crosstool东西构建穿插东西链
Crosstool是一组脚本东西集,可构建和测验不同版别的gcc和glibc,用于那些支撑glibc的体系结构。他也是个开源项目,下载地址是http://kegel.com/crosstool。用Crosstool构建穿插东西链要比上述的分步编译简单得多,而且也便利许多,关于只是为了作业需求构建穿插编译东西链的读者主张运用此办法。用Crosstool东西构建所需资源如表2.2所示。
表2.2 所需资源
装置包
下载地址
crosstool-0.42.tar.gz
http://kegel.com/crosstool
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
4.履行脚本
将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文件的最终增加下面一行。
export PATH=/opt/crosstool/gcc-3.3.6-glibc-2.3.2/arm-linux/bin:$PATH
设置完环境变量,也就意味着穿插编译东西链已构建完结,然后就能用2.2.1.8节中的办法进行测验刚刚树立的东西链,此处就不必再赘述。
2.3 本章小结
本章叙述的内容十分有实用价值,因为穿插编译东西链的构建是嵌入式体系研制必不可少的一部分,也是嵌入式体系研制的根底。本章首要对穿插编译东西链进行了大体的介绍,然后别离介绍了两种构建穿插编译东西链的办法:分步构建法和Crosstool东西构建法。这两种构建穿插编译东西链的办法在实践使用中十分广泛,信任读者经过学习本章的内容能构建一套自己的穿插编译东西链。第3章将介绍嵌入式体系的发动程式??BootLoader。
2.4 常见问题
问题1:编译boot-trap gcc时呈现过错,提示:crti.o: No such file: No such file or directory collect2: ld returned 1 exit status,为什么会呈现这样的过错?
参考答案:
因为在设置时没有挑选–disable-shared 选项,该选项的意思是只编译静态库。默许选项为–enable-shared,而libf2c 和libiberty 不支撑同享库。
问题2:Glibc里静态库和同享库有什么不同?
参考答案:
使用程式在链接静态库时,会把引证到的数据和代码放到生成的可履行文件中,程式运转时就不再需求库了。使用程式链接同享库时,衔接器不会把引证到的数据和代码放到可履行文件中,而仅做一个符号,当程式运转时,体系会去加载相应的同享库。链接同享库时,可履行文件的巨细会小一些,但运转时依赖于同享库。发动静态库和同享库的办法别离是在设置时用 –disable-shared和–enable-shared选项。
问题3:本地编译器和穿插编译器的效果。
参考答案:
编译器能生成用来在和编译器自身地点的计算机和操作体系(渠道)相同的环境下运转的方针代码,这种编译器叫做本地编译器。别的,编译器也能生成用来在其他渠道上运转的方针代码,这种编译器叫做穿插编译器。