完整的gcc交叉工具链编译过程
原因:官网提供的预编译包使用的glibc版本为2.28,而16年编译的Pine A64系统上使用的还是glibc2.23。由于缺乏高版本的符号支持,使用新版本的编译器编译的程序可能会出现找不到符号一类的问题。(在重新编译系统和编译器之间,我选择了后者,因为实在不知道该如何配置多环境glibc了。。)
参考:https://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/
基本上就是翻译了
>>> 本文中的所有操作都假设在用户的家目录下的gnu-aarch64文件夹~/gnu-aarch64/
下执行 <<<
>>> 用于编译的文件夹以build-
作为前缀,如build-gcc
<<<
>>> 编译完成后的工具链安装在dist
目录下 <<<
系统为Ubuntu 16.04 Server
这里使用/home/esper/
作为用户的家目录。
一套完整的编译工具链由三个部分组成,binutils
,gcc
,glibc
。
三者的功能分别如下:
binutils
:顾名思义,与二进制binary
相关,负责对二进制文件相关的操作,比如常见的ld
(可能全称是link dynamics),即链接动态库,此时的文件已经是编译为二进制的对象文件object file
了;在之前的曾经提到过的strip
,删除二进制文件中不必要的符号信息,等等。
gcc
:全称GNU Compiler Collection
,是GNU开源计划的编译器套件。完成的功能是将高级语言转化为对应的机器代码/二进制文件。stdc++库是和gcc一同发布的 -> 想要降级libstdc++,就要降级gcc。
glibc
:glibc
是GNU发布的libc库,即c运行库。glibc
是linux系统中最底层的应用程序编程接口Application Programming Interface, API
,几乎其它任何运行库都会依赖于glibc。(摘自百度百科)
事先说明:
- 如果编译过程中出现了错误,不要慌!在排查完自己步骤上的错误之后,别忘记怀疑软件和系统!!(惨痛的教训)
- 如果遇到了
error: cannot compute suffix of object files
问题的话,说明你可能是提前开始了gcc的编译,或者没有配置好必要的环境变量。
第一步 准备必要环境
这一步包括所有相关的软件及其相关配置等。
必要软件包
通过包管理器安装-辅助软件包:texinfo
, gettext
, bison
, 用于编译的gcc
, make
源码-主要软件包:目标版本的gcc
, binutils
, glibc
, 目标系统对应版本的linux内核源码
源码-辅助软件包:gmp
, isl
, mpc
, mpfr
, libiconv
其中软件的版本可通过包内的INSTALL
文件中对软件包的要求进行确定。
其中不同发行网站(比如gnu官方和arm官方)提供的gcc源码包内,有的直接提供了可用版本的辅助软件包,有的可以通过contrib
目录下的download_prerequisites
脚本进行下载。
必要环境变量
需要确保编译完的输出文件夹的bin目录在搜索路径search path
中。
export PATH=/home/esper/gnu-aarch64/dist/bin:$PATH
因为交叉工具链的编译是一个可以称为自举的过程,即先编译一部分,然后这一部分参与编译另一部分,直至编译完成。从文首的引用中借图可以清晰展示如下:
gcc
和glibc
都是交叉工具链的一部分,作为一个整体的工具链,像一个人走路一样左脚、右脚地迈进,而不是各自独立完成编译然后组合在一起。因而,在过程步骤上有点复杂。
第二步 编译安装binutils
为了编译安装软件,对于二进制文件的操作是必不可少的。因而,要先编译用于目标平台的binutils
以使得接下来可以修改能在目标平台上运行的程序。
tar xf binutils-2.26.1.tar.gz
mkdir build-binutils
cd build-binutils
../binutils-2.26.1/configure --prefix=/home/esper/gnu-aarch64/dist --target=aarch64-linux-gnu --disable-multilib
make -j4
make install
cd ..
由于configure
命令不允许使用相对路径,故需要使用绝对路径。将其替换为自己的用户名。
完成后,直接在命令行输入aarch64,然后多按几次tab键,应该会出现诸如aarch64-linux-gnu-ld
的命令补全提示。
如果现象与上述一致,则该步骤成功完成。
第三步 准备linux内核头文件
参考我的这篇博客以获得pine64对应的内核源码。
但是由于在这里,我们的内核头文件不是为系统配置使用的,因而无需使用文中提到的来自longsleep大神的安装脚本。
tar xf linux-pine64-3.10.104-2-pine64.tar.gz
cd linux-pine64-3.10.104-2-pine64
make ARCH=arm64 INSTALL_HDR_PATH=/home/esper/gnu-aarch64/dist/aarch64-linux-gnu headers_install
cd ..
将头文件安装到以架构命名的子文件夹中。这样做的好处是允许系统中同时配置有多个不同环境的编译套件,而且似乎gcc也贯彻落实了这一点——如果不这么做的话,往往会出现意料之外的错误。
第四步 编译安装gcc套件
gcc套件只是工具链的一部分,获得的gcc源码包中还含有libstdc++一类同捆发行的依赖库,在此时并不能完成编译。
可以将gmp
, isl
, mpc
, mpfr
, libiconv
的源码文件夹链接或是直接解压到gcc源码目录下,configure
命令若是识别到了不带版本后缀的文件夹名的话,会试图将该源码包编译,并用于后续的编译过程。否则需要在系统路径提供安装好的、满足版本要求的对应软件包。
tar xf gcc-arm-src-snapshot-8.3-2019.03.tar.xz
mkdir build-gcc
cd build-gcc
../gcc-arm-src-snapshot-8.3-2019.03/configure --target=aarch64-linux-gnu --prefix=/home/esper/gnu-aarch64/dist --with-bugurl=https://bugs.linaro.org/ --enable-gnu-indirect-function --enable-shared --disable-libssp --disable-libmudflap --enable-checking=release --enable-languages=c,c++,fortran --enable-fix-cortex-a53-843419 --disable-multilib
make -j4 all-gcc
make install-gcc
cd ..
如果需要提供ada支持的话,需要安装flex
包。由于我的后续工作中用不到,且为了节约时间,就没有编译部分语言的支持。
如果对于configure
的参数有疑问的话,可以通过gcc -v
获得对应gcc编译器的编译配置参数以参考。
第五步 构建glibc
初始环境
根据参考文章的作者描述,除开configure
之外,该步骤属于临时性但又不得不做的措施。
tar xf glibc-2.23.tar.xz
mkdir build-glibc
cd build-glibc
../glibc-2.23/configure --prefix=/home/esper/gnu-aarch64/dist/aarch64-linux-gnu --build=x86_64-linux-gnu --host=aarch64-linux-gnu --target=aarch64-linux-gnu --with-headers=/home/esper/gnu-aarch64/dist/aarch64-linux-gnu/include --disable-multilib --disable-werror
make install-bootstrap-headers=yes install-headers
make -j4 csu/subdir_lib
install csu/crt1.o csu/crti.o csu/crtn.o /home/esper/gnu-aarch64/dist/aarch64-linux-gnu/lib
aarch64-linux-gnu-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o /home/esper/gnu-aarch64/dist/aarch64-linux-gnu/lib/libc.so
touch /home/esper/gnu-aarch64/dist/aarch64-linux-gnu/include/gnu/stubs.h
cd ..
其中根据参考文章的作者描述,glibc的configure
必须显式指明build
, host
, target
三个平台参数值。
这里的configure
添加了--disable-werror
参数,目的在于取消编译器将警告作为错误处理,进而终止编译过程的行为。该参数在软件的编写过程中十分有效,但是在编译一个已经公开发行且没有出现大问题的软件的时候,emm...还是关掉它吧
第六步 编译gcc支持库
cd build-gcc
make -j4 all-target-libgcc
make install-target-libgcc
cd ..
第七步 编译glibc
此时才能使用目标平台的gcc编译glibc。
cd build-glibc
make -j4
make install
cd ..
第八步 完成gcc编译
cd build-gcc
make -j4
make install
cd ..
然后dist
目录下就是一套完整的交叉编译工具链了。经过测试,该工具链已经可以生成能在目标平台运行的程序了,但在移动位置之后使用我还没试过。理论上是没问题的,因为arm官方发布的预编译包似乎就是这样。祝好运!
===========================================================
当场补充
相信看到这里,并且在机器上一步一步跟着做的读者(如果有的话)应该是长舒了一口气。然而,在下要在这里提一句,ubuntu上有预编译好的交叉编译工具链,如果你不知道的话。
sudo apt install gcc-5-aarch64-linux-gnu g++-5-aarch64-linux-gnu
(逃