从零构建GCC编译工具链简记

前言

由于种种原因,最近有个需求,需要在Windows下用gcc交叉编译,但arch不是常见的arm,网上找不到现成的,刚好又在看龙书《编译原理》,于是索性自己尝试编译一个gcc。
本文仅对流程进行简要记录,方便自己以后回溯,不会提及太多细节以及特定arch相关的描述。

参考

本人小白,一开始天真的以为只需要下载一个gcc源码包进行编译就完了,后来发现并不是那么回事,编译gcc至少分为以下4部分:

  • binutils
  • Linux kernel header
  • c library
  • gcc

若不是可以从buildroot里借鉴一二,那基本就从入门到放弃了……

Buildroot

上小节提到,编译gcc至少分为4大部分,而且有先后顺序依赖,每部分的configure都有大量可选配置,这对我这种小白来说是灾难性的……
于是我突然灵机一动,想起来之前用buildroot构建rootfs时候,若toolchain type选择buildroot toolchain,在整体编译buildroot的时候会先进行buildroot gcc的编译,救命稻草啊!于是切到ubuntu下先进行一遍buildroot的编译,看看buildroot是怎么将gcc从零编译出来的。
buildroot --> Toolchain 的关键配置如下:

  • C library
  • Custom kernel headers series
  • Binutils Version
  • GCC compiler Version
  • [*] Enable C++ support
  • [*] Build cross gdb for the host

配置好后,执行make整体编译,保存完整的编译log,你会发现log中自有黄金屋。

网上资料

网上的一些资料也基本完整的描述了整个工具链的构建流程,流程是基本一致的,可供参考:

https://linux.cn/lfs/LFS-BOOK-7.7-systemd/chapter05/generalinstructions.html
https://zhuanlan.zhihu.com/p/110402378

前置准备

入正题前还是提提我踩到的坑,原本以为,既然要编译Windows下的工具链,就理应在Windows下开发比较稳妥,于是就搭建了个MSYS2开发环境,gcc工具链选用mingw64-7.3.0。
在MSYS2里就相当于Linux shell一样,安装好基本的编译组件后,就参照buildroot的log进行编译。一开始还比较顺利的将binutils编译好了,但紧接着进行gcc的编译时,就遇到了编译错误:

libgcc2.c:2096:1: internal compiler error: Segmentation fault

折腾了很久都无法解决,绝望……
于是果断悬崖勒马,想着要么还是回归ubuntu下搞吧,既然buildroot能将gcc编出来,理论上只需将host gcc换成x86_64-w64-mingw32-gcc再跑一次就行了。
x86_64-w64-mingw32-gcc软件源上就有现成的,直接通过apt安装即可:

sudo apt-get install gcc-mingw-w64-x86-64

于是,以下的流程均在ubuntu-20.04下进行。

编译流程

从buildroot的编译log中,可以总结出整个gcc工具链的构建流程如下:

  1. binutils编译
  2. gmp/mpfr/mpc编译
  3. gcc第一阶段编译
  4. Linux kernel header install
  5. c library编译
  6. gcc第二阶段编译
  7. gdb编译

上述源码包均可从buildroot dl目录下获得,下面简要说明从buildroot log中提取的每个部分的configure、make、make install命令传参。install路径遵循buildroot,以 <***>/host/ 描述。
(注:下述与本地路径以及arch相关的选项均以<***>替代,以标注提醒需要具体问题具体分析。)

binutils

GNU Binary Utilities或binutils是一整套的编程语言工具程序,用来处理许多格式的目标文件,包括常用的ld、as、objcopy、objdump等工具。
configure如下:

./configure --prefix=<***>/host --host=x86_64-w64-mingw32 --sysconfdir=<***>/host/etc --localstatedir=<***>/host/var --enable-shared --disable-static --disable-gtk-doc --disable-gtk-doc-html --disable-doc --disable-docs --disable-documentation --disable-debug --with-xmlto=no --with-fop=no --disable-nls --disable-dependency-tracking --disable-multilib --disable-werror --target=<***> --disable-shared --enable-static --with-sysroot=<***>/host/<***>/sysroot --enable-poison-system-directories --without-debuginfod --disable-sim --disable-gdb

make 及 make install 如下:

make -j2 MAKEINFO=true
make -j2 MAKEINFO=true install

gmp/mpfr/mpc

gcc依赖gmp、mpfr、mpc,需先进行上述3个库的编译。
gmp configure如下:

./configure --prefix=<***>/host --host=x86_64-w64-mingw32 --sysconfdir=<***>/host/etc --localstatedir=<***>/host/var --enable-static --disable-shared --disable-gtk-doc --disable-gtk-doc-html --disable-doc --disable-docs --disable-documentation --disable-debug --with-xmlto=no --with-fop=no --disable-nls --disable-dependency-tracking

mpfr configure如下:

./configure --prefix=<***>/host --host=x86_64-w64-mingw32 --sysconfdir=<***>/host/etc --localstatedir=<***>/host/var --enable-static --disable-shared --disable-gtk-doc --disable-gtk-doc-html --disable-doc --disable-docs --disable-documentation --disable-debug --with-xmlto=no --with-fop=no --disable-nls --disable-dependency-tracking --with-gmp=<***>/host

mpc configure如下:

./configure --prefix=<***>/host --host=x86_64-w64-mingw32 --sysconfdir=<***>/host/etc --localstatedir=<***>/host/var --enable-static --disable-shared --disable-gtk-doc --disable-gtk-doc-html --disable-doc --disable-docs --disable-documentation --disable-debug --with-xmlto=no --with-fop=no --disable-nls --disable-dependency-tracking --with-gmp=<***>/host --with-mpfr=<***>/host

make 及 make install 如下:

make -j2
make -j2 install

gcc第一阶段

gcc依赖于c库,而c库又是gcc编译出来的,这就会有鸡生蛋蛋生鸡的问题,于是gcc的编译分成了两个阶段。
第一阶段由于还没有c库,需要指定 --with-newlib ,以防止编译任何需要c库支持的代码。
configure如下(注:gcc不允许直接在源码目录下进行make,因此效仿buildroot,在源码中新建一个build文件夹用于编译):

…/configure --prefix=<***>/host --host=x86_64-w64-mingw32 --sysconfdir=<***>/host/etc --localstatedir=<***>/host/var --enable-shared --disable-static --disable-gtk-doc --disable-gtk-doc-html --disable-doc --disable-docs --disable-documentation --disable-debug --with-xmlto=no --with-fop=no --disable-nls --disable-dependency-tracking --target=<***> --with-sysroot=<***>/host/<***>/sysroot --enable-__cxa_atexit --with-gnu-ld --disable-libssp --disable-multilib --disable-decimal-float --with-gmp<***>/host --with-mpc=<***>/host --with-mpfr=<***>/host --with-pkgversion=’<***>’ --with-bugurl=“http://bugs.buildroot.net/” --without-zstd --disable-libquadmath --disable-libquadmath-support --enable-tls --enable-threads --without-isl --without-cloog --enable-languages=c --disable-shared --without-headers --disable-threads --with-newlib --disable-largefile

make 及 make install 如下:

make -j2 gcc_cv_libc_provides_ssp=yes all-gcc all-target-libgcc
make -j2 install-gcc install-target-libgcc

于是,临时的gcc工具链就编译完成了,可以用于接下来编译c库。

Linux kernel header

Linux 内核需要展示供系统c库使用的应用程序编程接口(API),因此在编译c库之前,需要先进行内核头文件的install。
make headers_install如下:

make -j2 ARCH=<***> HOSTCC=/usr/bin/gcc HOSTCFLAGS= HOSTCXX=/usr/bin/g++ INSTALL_HDR_PATH=<***>/build/linux-headers-5.10.4/usr headers_install
make -j2 ARCH=<***> HOSTCC=/usr/bin/gcc HOSTCFLAGS= HOSTCXX=/usr/bin/g++ INSTALL_HDR_PATH=<***>/host/<***>/sysroot/usr headers_install

c library

这里选择的c库是glibc,可按需选择uclibc或musl-libc等其他c库。此处编译c库所用的gcc是上述编译出的临时gcc,因此需将临时gcc的 install bin 路径添加到PATH。
参考buildroot,在glibc源码目录下新建一个build文件夹用于编译。
configure如下:

…/configure --target=<***> --host=<***> --build=x86_64-pc-linux-gnu --prefix=/usr --enable-shared --with-pkgversion=“Buildroot” --disable-profile --disable-werror --without-gd --enable-kernel=5.10 --with-headers=<***>/host/<***>/sysroot/usr/include

注:此处 --target 和 --host 一致,均指代目标arch,如 arm-linux-gnueabi
make 及 make install 如下:

make -j2
make -j2 install_root=<***>/host/<***>/sysroot install

gcc第二阶段

有了glibc,接下来就可以进行完整gcc的构建了。
configure如下(注:gcc不允许直接在源码目录下进行make,因此效仿buildroot,在源码中新建一个build文件夹用于编译):

…/configure --prefix=<***>/host --host=x86_64-w64-mingw32 --sysconfdir=<***>/host/etc --enable-static --target=<***> --with-sysroot=<***>/host/<***>/sysroot --enable-__cxa_atexit --with-gnu-ld --disable-libssp --disable-multilib --disable-decimal-float --with-gmp=<***>/host --with-mpc=<***>/host --with-mpfr=<***>/host --with-pkgversion=’<***>’ --with-bugurl=“http://bugs.buildroot.net/” --without-zstd --disable-libquadmath --disable-libquadmath-support --enable-tls --enable-threads --without-isl --without-cloog --enable-languages=c,c++ --with-build-time-tools=<***>/host/<***>/bin --enable-shared --disable-libgomp

make 及 make install 如下:

make -j2 gcc_cv_libc_provides_ssp=yes
make -j2 install

到此为止,已经完成了整个gcc编译工具链的构建,将install目录 <***>/host 整个打包,拷贝到Windows,就可以愉快的在Windows下交叉编译啦。

gdb

buildroot在构建完整个gcc后,才会进行gdb的编译,但gdb其实也包含在binutils里,buildroot将binutils和gdb的编译分开的原因本人由于时间关系没有深究,猜测gdb编译可能需要用到c库?有清楚的大哥麻烦留言指点一二,谢谢!
参考buildroot,在binutils源码目录下新建一个build文件夹用于编译。
configure如下:

…/configure --prefix=<***>/host --host=x86_64-w64-mingw32 --sysconfdir=<***>/host/etc --localstatedir=<***>/host/var --enable-shared --disable-static --disable-gtk-doc --disable-gtk-doc-html --disable-doc --disable-docs --disable-documentation --disable-debug --with-xmlto=no --with-fop=no --disable-nls --disable-dependency-tracking --target=<***> --enable-static --without-uiout --disable-gdbtk --without-x --enable-threads --disable-werror --without-included-gettext --with-curses --without-mpfr --disable-binutils --disable-install-libbfd --disable-ld --disable-gas --disable-gprof --disable-tui --without-python --disable-sim

make 及 make install 如下:

make -j2 MAKEINFO=true
make -j2 MAKEINFO=true install

后记

本文只是简要的记录下从零构建gcc编译工具链的流程,在大多数情况下,gcc都是有现成的可以使用,即使没有,也可以通过buildroot一站式进行编译,很少情况需要像本文一样一步步手动编译。
不过,人生之路长着,谁知道呢?

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值