如何编一个gcc的交叉工具链

导论

自定义编译 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,需要准备如下的组件代码:

bash 复制代码
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)。

操作步骤

解压所有的压缩文件包:

bash 复制代码
for f in *.tar*; do tar xf $f; done
cd gcc-7.3.0
ln -s ../mpfr-3.1.4 mpfr
ln -s ../gmp-6.1.2 gmp
ln -s ../mpc-1.0.3 mpc
ln -s ../isl-0.18 isl
ln -s ../cloog-0.18.0 
cd ..

如果有需要编译其它gcc版本,在这个下载:gcc版本下载路径

选择一个系统的一个安装目标路径,确认有足够的写入权限,比如我的:

bash 复制代码
sudo mkdir -p /opt/usr/gcc_cross
sudo chown yz:yz  /opt/usr/gcc_cross
export PATH=/opt/usr/gcc_cross/bin:$PATH

编译Binutils

GNU Binutils(Binary Utilities) 是 GNU 工具链中专门用于处理二进制目标文件、汇编代码、静态/动态库以及可执行程序的工具集。在编译流程中,GCC 负责将高级语言(C/C++)翻译为汇编语言,而后续的汇编、链接、符号管理及二进制分析工作,均由 Binutils 完成。

bash 复制代码
mkdir build-binutils
cd build-binutils/
../binutils-2.32/configure --prefix=/opt/usr/gcc_cross --target=aarch64-linux --disable-multilib
make -j24
make install
cd ..

编译linux kernel 头文件

bash 复制代码
cd linux-5.10.1/
make ARCH=arm64 INSTALL_HDR_PATH=/opt/usr/gcc_cross/aarch64-linux headers_install
cd ..

编译c/c++编译器

bash 复制代码
mkdir -p build-gcc
../gcc-7.3.0/configure --prefix=/opt/usr/gcc_cross \
--target=aarch64-linux \
--enable-languages=c,c++ \
--disable-multilib \
--disable-nls \
--disable-libstdcxx-dual-abi
make -j24 all-gcc
make install-gcc

--disable-libstdcxx-dual-abi:强制禁用 GCC 5.1 引入的"C++11 双重 ABI(Dual ABI)"机制

--disable-nls:GCC 的所有诊断信息、报错和警告将强制统一使用原生英文输出

安装 Glibc 头文件和启动文件

bash 复制代码
mkdir -p build-glibc
cd build-glibc
../glibc-2.33/configure --prefix=/opt/usr/gcc_cross/aarch64-linux \
--build=$MACHTYPE --host=aarch64-linux --target=aarch64-linux \
--with-headers=/opt/usr/gcc_cross/aarch64-linux/include \
--disable-multilib libc_cv_forced_unwind=yes
make install-bootstrap-headers=yes install-headers
make -j24 csu/subdir_lib
install csu/crt1.o csu/crti.o csu/crtn.o /opt/usr/gcc_cross/aarch64-linux/lib
aarch64-linux-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o /opt/usr/gcc_cross/aarch64-linux/lib/libc.so
touch /opt/usr/gcc_cross/aarch64-linux/include/gnu/stubs.h
cd ..

编译gcc的库

bash 复制代码
cd buil-gcc
make -j24 all-target-libgcc
make install-target-libgcc
cd ..

编译C库

bash 复制代码
cd build-glibc
make -j24
make install

编译gcc

bash 复制代码
cd build-gcc
make -j24
make install
cd ..

编译中遇到的报错问题

最后的编译中,会遇到如下的报错:

  • 修改 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文件,去掉如下代码:
c 复制代码
#if !SANITIZER_ANDROID
PRE_SYSCALL(ustat)(long dev, void *ubuf) {}

POST_SYSCALL(ustat)(long res, long dev, void *ubuf) {
  if (res >= 0) {
    if (ubuf) POST_WRITE(ubuf, struct_ustat_sz);
  }
}
#endif  // !SANITIZER_ANDROID

ustat 是一个已被彻底废弃的远古系统调用,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版本信息:

bash 复制代码
aarch64-linux-g++ -v
Using built-in specs.
COLLECT_GCC=aarch64-linux-g++
COLLECT_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 --target=aarch64-linux --enable-languages=c,c++ --disable-multilib --disable-nls --disable-libstdcxx-dual-abi
Thread model: posix
gcc version 7.3.0 (GCC) 

参考

对于上面的操作,可以参考如下脚本,执行快速的自动化配置流程

相关推荐
shjita1 小时前
centos中安装docker
linux·docker·centos
Frank_refuel2 小时前
【C++】手撕JRpc框架(梳理RPC流程)
c++
程序猿老A2 小时前
阿里云linux服务器安装mysql并实现远程访问
linux·服务器·阿里云
wuminyu2 小时前
JVM虚拟线程的底层实现原理分析
java·linux·c语言·jvm·c++
keyipatience2 小时前
跳表详细解析
开发语言·数据结构·c++·算法·跳表
汉克老师2 小时前
GESP2026年9月认证C++一级( 第一部分选择题(8~15题)精讲
c++·gesp·小学生·学c++编程
M78佐菲2 小时前
ARM学习笔记(9)
linux·arm开发·笔记·嵌入式硬件·学习
赵民勇2 小时前
glib-compile-schemas命令详解
linux·运维
小小、码农2 小时前
〖Linux文件系统〗:彻底打通文件 IO 全链路
linux·开发语言·数据结构·c++·系统