如何构建一个GCC交叉编译工具链
GCC不仅是一个编译器,它是一个开源工程,可以让你建立各种编译器。一些编译器支持多线程,一些支持共享库,一些支持Multilib(典型的应用是在64位机上运行32位应用程序),这些都取决于在编译编译器时的配置。
本文档将说明怎么建立一个交叉编译器。你需要一个已经安装gcc的Unix-like环境。
一、需要的包
Debian系统,首先需要安装一些包
$sudoapt-getinstallg++makegawk
其他的包将使用源代码来编译。在根文件系统的某个地方新建一个文件夹,下载下面的包。本文是写的,你看到时,可能有更新的包可以使用,所以你可以使用更新的包。
$wget/binutils/binutils-2.24.tar.gz
$wget/gcc/gcc-4.9.2/gcc-4.9.2.tar.gz
$wget/pub/linux/kernel/v3.x/linux-3.17.2.tar.xz
$wget/glibc/glibc-2.20.tar.xz
$wget/mpfr/mpfr-3.1.2.tar.xz
$wget/gmp/gmp-6.0.0a.tar.xz
$wget/mpc/mpc-1.0.2.tar.gz
$wgetftp:///pub/gcc/infrastructure/isl-0.12.2.tar.bz2
$wgetftp:///pub/gcc/infrastructure/cloog-0.18.1.tar.gz
最开始的四个包:binutils、gcc、Linuxkernel和glibc是主要要用的包。后面三个mpfr、gmp、mpc你可以使用系统自带的packagemanager安装,但是可能比较旧。最后两个包ISL和CLooG是可选的,它们支持一些优化。
二、它们是怎么有机结合在一起的呢?
当我们完成时,我们会构建如下的程序和库。首先,构建左边的工具,然后使用这些工具来构建右边的程序和库。我们将不构建目标系统的Linuxkernel,但是我们需要kernelheader来构建目标系统标准C库。
左边的编译器将调用汇编器和链接器。其他的包MPFR、GMP和MPC将连接到编译器中。
右边图中的a.out,运行在目标OS上,使用交叉编译器,连接目标系统标准C库和C++库。C++标准库调用标准C库,C库直接调用Linuxkernel。
注意,除了使用glibc外,还可以使用其他替代的C库实现,比如Newlibc,uclibc等,其他替代的C库用在嵌入式中较多,比glibc库要小,功能没有glibc全面。
三、构建步骤
解压所有的包。
$forfin*.tar*;dotarxf$f;done
创建一些其他目录的符号连接,这5个包依附于gcc,如果存在符号连接,gcc脚本将自动build这些包。
$cdgcc-4.9.2
$ln-s../mpfr-3.1.2mpfr
$ln-s../gmp-6.0.0gmp
$ln-s../mpc-1.0.2mpc
$ln-s../isl-0.12.2isl
$ln-s../cloog-0.18.1cloog
$cd..
选择一个安装路径,确保有写的权限。下面步骤中,我将安装新的工具链到/opt/cross
$sudomkdir-p/opt/cross
$sudochownjeff/opt/cross
整个构建过程中,确保安装的bin子目录在你的PATH环境变量中。后续你可以从PATH中移除该目录,但是大部分构建步骤将会默认通过PATH来查找aarch64-linux-gcc和其他host工具。
$exportPATH=/opt/cross/bin:$PATH
注意/opt/cross/aarch64-linux/目录下的文件。该目录被认为是虚拟的aarch64linux目标系统的根目录。理论上,可以使用里面所有的头文件和库。
1.Binutils
构建binutils、安装交叉汇编器、链接器和其他工具的步骤:
$mkdirbuild-binutils
$cdbuild-binutils
$../binutils-2.24/configure--prefix=/opt/cross--target=aarch64-linux--disable-multilib
$make-j4
$makeinstall
$cd..
我们制定aarch64-linux作为目标系统类型,binutils的配置脚本将识别到它与正在进行编译的主机系统不一样,配置一个交叉汇编器和交叉链接器。这些工具将安装到/opt/cross/bin,名字以arm-linux-开头。
--disable-multilib意味着我们只希望我们的程序和库使用aarch64指令集,而不使用aarch32的指令集。
2.LinuxKernelHeaders
将Linuxkernel头文件安装到/opt/cross/aarch64-linux/include,使用新工具链构建的程序会调用这些目标环境中的aarch64kernel。
$cdlinux-3.17.2
$makeARCH=arm64INSTALL_HDR_PATH=/opt/cross/aarch64-linuxheaders_install
$cd..
我们也可以在构建binutils之前做这件事。
尽管第4步,configure脚本期望linuxkernelheader依据安装,但是实际上在步骤6之前,当我们编译标准C库时,linuxkernelheaders不会被用到。
因为Linuxkernel和其他开源工程不一样,它有一个不同的方式来识别目标CPU架构:ARCH=arm
剩下的步骤涉及构建GCC和Glibc。这里有个道道,就是部分gcc需要部分glibc被构建,而部分glibc又需要gcc被构建。我们不能一步搞定这些编译,而是要分成几步。我们要这几个包之间来往几次。
3.C/C++Compilers
该步将构建gcc的C和C++编译器,并安装到/opt/cross/bin,目前还不能引用这些编译器来构建库。
$mkdir-pbuild-gcc
$cdbuild-gcc
$../gcc-4.9.2/configure--prefix=/opt/cross--target=aarch64-linux--enable-languages=c,c++--disable-multilib
$make-j4all-gcc
$makeinstall-gcc
$cd..
因为我们指定了--target=aarch64-linux,构建脚本会依据aarch64-linux-前缀查找第一步安装的binutils工具。同样,C/C++编译器的名字也会带上aarch64-linux-前缀。
--enable-languages=c,c++避免了在GCC套件中出现其他的编译器,比如Fortran、Java等。
4.StandardCLibraryHeadersandStartupFiles
安装标准C库头文件到/opt/cross/aarch64-linux/include。我们会使用第三步构建的C编译器来编译库的startupfiles并安装到/opt/cross/aarch64-linux/lib。最后我们创建几个傀儡文件,libc.so和stubs.h,在第5步会用到,但是第6步会替换为真的。
$mkdir-pbuild-glibc
$cdbuild-glibc
$../glibc-2.20/configure--prefix=/opt/cross/aarch64-linux--build=$MACHTYPE--host=aarch64-linux--target=aarch64-linux--with-headers=/opt/cross/aarch64-linux/include--disable-multiliblibc_cv_forced_unwind=yes
$makeinstall-bootstrap-headers=yesinstall-headers
$make-j4csu/subdir_lib
$installcsu/crt1.ocsu/crti.ocsu/crtn.o/opt/cross/aarch64-linux/lib
$aarch64-linux-gcc-nostdlib-nostartfiles-shared-xc/dev/null-o/opt/cross/aarch64-linux/lib/libc.so
$touch/opt/cross/aarch64-linux/include/gnu/stubs.h
$cd..
--prefix=/opt/cross/aarch64-linux告诉configure脚本它要安装头文件和库到这里。注意这个和普通的--prefix。
Glibc的configure需要我们制定所有的--build,--host和--target系统类型。
$MACHTYPE是一个预定义的环境变量,表示正在运行build脚本的机器。--build=$MACHTYPE是必须的,因为在第六步中,该build脚本将编译一些额外的工具,这些工具是build进程的一部分。
--host,在glibc的configure中,--host和--target选项都指glibc库最终运行的系统。
我们手动安装C库startup文件,ctr1.octri.o和ctrn.o。其他的方法好像都有副作用。
pilerSupportLibrary
使用第三步的交叉编译器构建compilersupportlibrary,编译器支持的库包含一些C++异常处理样板代码等等。该库依赖第四步安装的startupfile。第六步需要该库。不像其他的指导手册,我们不需要重新运行gccconfigure。只需要在相同配置下构建额外的target即可。
$cdbuild-gcc
$make-j4all-target-libgcc
$makeinstall-target-libgcc
$cd..
两个静态库,libgcc.a和libgcc_eh.a,安装到/opt/cross/lib/gcc/aarch64-linux/4.9.2.
共享库,lingcc_s.so,安装到/opt/cross/aarch64-linux/lib64.
6.StandardCLibrary
这步完成glibc的安装。安装标准C库到/opt/cross/aarch64-linux/lib中。静态库名字为libc.a,动态库为libc.so
$cdbuild-glibc
$make-j4
$makeinstall
$cd..
7.StandardC++Library
最后完成gcc的安装,构建标准C++库,安装到/opt/cross/aarch64-linux/lib64。它依赖第六步的C库。目标静态库名字为libstdc++.a,动态库为libstdc++.so。
$cdbuild-gcc
$make-j4
$makeinstall
$cd..
How to Build a GCC Cross-Compiler
/1119/how-to-build-a-gcc-cross-compiler/