
导论自定义编译 GCC 交叉工具链如使用 Crosstool-NG、Buildroot 或手动从源码构建相比直接使用发行版预编译包如 Ubuntu 源中的 gcc-arm-linux-gnueabihf核心优势在于对底层运行环境与硬件特性的绝对控制权。精确匹配目标系统的 C 运行时库硬件架构与指令集的深度优化定制组件版本与最新特性裁剪体积与消除非必要依赖统一的调试与内核接口支持。GCC 是“编译器的编译器框架”平时开发中执行的 gcc 命令只是一个预先编译好的成品Driver。而 GCC 源码本身是一个高度模块化的系统套件。本指南将演示如何构建一个交叉编译器即一种能在当前机器上运行、但为另一种目标机器生成程序的编译器。你所需要的仅仅是一个已预装较新版本 GCC 的Linux环境。编译环境使用ubuntu22.08 x86_64 GNU/Linux开发环境 需要安装组件:sudo apt-get install g make gawk准备编译的gcc 7.3.0(2018 年 1 月 25 日 正式发布) 的老版本适配版本适配内核kernel 5.10.1需要准备如下的组件代码:binutils-2.32.tar.bz2 cloog-0.18.0.tar.gz gcc-7.3.0.tar.xz glibc-2.33.tar.xz gmp-6.1.2.tar.xz isl-0.18.tar.bz2 linux-5.10.1.tar.xz mpc-1.0.3.tar.gz mpfr-3.1.4.tar.xz构建左侧的工具链随后使用这些工具来编译右侧的程序和运行库。实际上并不会去编译目标系统的 Linux 内核但为了编译目标系统的标准 C 库确实需要目标系统的内核头文件Kernel Headers。左侧的编译器在工作时会调用汇编器Assembler和链接器Linker。而下载的其他所有依赖包如 MPFR、GMP 和 MPC都会被链接到编译器本身之中。右侧的架构图展示了一个在目标操作系统上运行的示例程序 a.out它是由交叉编译器编译生成并链接了目标系统的标准 C 库与 C 库。标准 C 库会调用标准 C 库而 C 库则直接向 AArch64 Linux 内核发起系统调用System Calls。操作步骤解压所有的压缩文件包:forfin*.tar*;dotarxf$f;donecdgcc-7.3.0ln-s../mpfr-3.1.4 mpfrln-s../gmp-6.1.2 gmpln-s../mpc-1.0.3 mpcln-s../isl-0.18 islln-s../cloog-0.18.0cd..如果有需要编译其它gcc版本在这个下载:gcc版本下载路径选择一个系统的一个安装目标路径确认有足够的写入权限比如我的:sudomkdir-p/opt/usr/gcc_crosssudochownyz:yz /opt/usr/gcc_crossexportPATH/opt/usr/gcc_cross/bin:$PATH编译BinutilsGNU BinutilsBinary Utilities 是 GNU 工具链中专门用于处理二进制目标文件、汇编代码、静态/动态库以及可执行程序的工具集。在编译流程中GCC 负责将高级语言C/C翻译为汇编语言而后续的汇编、链接、符号管理及二进制分析工作均由 Binutils 完成。mkdirbuild-binutilscdbuild-binutils/../binutils-2.32/configure--prefix/opt/usr/gcc_cross--targetaarch64-linux --disable-multilibmake-j24makeinstallcd..编译linux kernel 头文件cdlinux-5.10.1/makeARCHarm64INSTALL_HDR_PATH/opt/usr/gcc_cross/aarch64-linux headers_installcd..编译c/c编译器mkdir-pbuild-gcc../gcc-7.3.0/configure--prefix/opt/usr/gcc_cross\--targetaarch64-linux\--enable-languagesc,c\--disable-multilib\--disable-nls\--disable-libstdcxx-dual-abimake-j24all-gccmakeinstall-gcc–disable-libstdcxx-dual-abi:强制禁用 GCC 5.1 引入的“C11 双重 ABIDual ABI”机制–disable-nls:GCC 的所有诊断信息、报错和警告将强制统一使用原生英文输出安装 Glibc 头文件和启动文件mkdir-pbuild-glibccdbuild-glibc../glibc-2.33/configure--prefix/opt/usr/gcc_cross/aarch64-linux\--build$MACHTYPE--hostaarch64-linux--targetaarch64-linux\--with-headers/opt/usr/gcc_cross/aarch64-linux/include\--disable-multiliblibc_cv_forced_unwindyesmakeinstall-bootstrap-headersyes install-headersmake-j24csu/subdir_libinstallcsu/crt1.o csu/crti.o csu/crtn.o /opt/usr/gcc_cross/aarch64-linux/lib aarch64-linux-gcc-nostdlib-nostartfiles-shared-xc /dev/null-o/opt/usr/gcc_cross/aarch64-linux/lib/libc.sotouch/opt/usr/gcc_cross/aarch64-linux/include/gnu/stubs.hcd..编译gcc的库cdbuil-gccmake-j24all-target-libgccmakeinstall-target-libgcccd..编译C库cdbuild-glibcmake-j24makeinstall编译gcccdbuild-gccmake-j24makeinstallcd..编译中遇到的报错问题最后的编译中会遇到如下的报错:修改libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc,第 157 行左右将 #include sys/ustat.h 注释掉第 250 行左右将 struct_ustat_sz 的定义注释掉修改libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h,第192行注释 extern unsigned struct_ustat_sz;修改sanitizer_common_syscalls.inc文件去掉如下代码:#if!SANITIZER_ANDROIDPRE_SYSCALL(ustat)(longdev,void*ubuf){}POST_SYSCALL(ustat)(longres,longdev,void*ubuf){if(res0){if(ubuf)POST_WRITE(ubuf,struct_ustat_sz);}}#endif// !SANITIZER_ANDROIDustat 是一个已被彻底废弃的远古系统调用ustat() 是早期 Unix/Linux 用来查询文件系统空闲块数和 inode 数量的接口。早在数十年前Linux 就已经全面推荐使用标准的 statvfs() 和 statfs() 来取代它。Glibc 在 2.28 版本彻底删除了 sys/ustat.h意味着在 Glibc 2.34 环境下编译的应用不可能、也无法调用 ustat()。ASan以及背后的 compiler-rt会拦截Intercept系统中的数百个标准库函数。ASan 之前包含 ustat唯一的目的只是在极少数老旧程序调用 ustat(dev, ubuf) 时顺便检查一下传入的 ubuf 结构体内存指针是否越界。交叉编译测试查看编译的gcc版本信息:aarch64-linux-g-vUsing built-in specs.COLLECT_GCCaarch64-linux-gCOLLECT_LTO_WRAPPER/opt/usr/gcc_cross/libexec/gcc/aarch64-linux/7.3.0/lto-wrapper Target: aarch64-linux Configured with:../gcc-7.3.0/configure--prefix/opt/usr/gcc_cross--targetaarch64-linux --enable-languagesc,c --disable-multilib --disable-nls --disable-libstdcxx-dual-abi Thread model: posix gcc version7.3.0(GCC)参考对于上面的操作可以参考如下脚本执行快速的自动化配置流程