返回

快速构建 GCC 并自定义编译标志 (禁用 Bootstrap)

Linux

如何在不进行 Bootstrap 的情况下构建 GCC 并指定编译标志?

遇到一个棘手的问题:想要快速构建 GCC,同时还想精确控制编译选项,尤其是去除调试信息。常规的 Bootstrap 流程耗时太长,而直接禁用 Bootstrap 又无法应用自定义的编译标志。 真是左右为难!

问题根源:GCC 的构建机制

GCC 的构建过程比较复杂。为了确保编译器的稳定性和可靠性,默认采用了一种称为“Bootstrap”的机制。这个过程大致是:

  1. 用系统现有的编译器(通常也是 GCC)编译一个初始版本的 GCC(stage1)。
  2. 用 stage1 版本的 GCC 编译第二个版本的 GCC(stage2)。
  3. 用 stage2 版本的 GCC 编译最终版本的 GCC(stage3)。
  4. 比较stage2 和 stage3 产物。

这样做的目的是检测编译器自身的缺陷。如果 stage2 和 stage3 编译出来的程序行为不一致,说明编译器可能存在 bug。

--disable-bootstrap 选项直接跳过了这个多阶段的编译过程,只进行一次编译。 这样速度是快了,但是无法像使用--with-build-config来通过 BOOT_CFLAGS 精确的修改 flag.

问题在于,不同的构建阶段会使用不同的编译标志变量。直接设置 CFLAGSCFLAGS_FOR_BUILD 等变量可能不会对最终安装的 GCC 生效。需要找到正确的变量。

解决方案:精确定位编译标志变量

经过一番探索和试验, 以及大量搜索, 发现要达到目标,需要组合拳。以下几种方法供您参考:

1. 使用 make install-strip (最简便,推荐)

这是最简单粗暴的办法。虽然不能在编译时就去除调试信息,但是可以在安装阶段解决:

  • 原理: make install-strip 会在安装 GCC 的同时,自动调用 strip 命令移除可执行文件中的符号表和调试信息。

  • 操作:

    ./configure --disable-bootstrap  # 仍然禁用 Bootstrap
    make
    make install-strip
    
  • 额外安全建议 : 这个操作, 文件只会在被strip过的阶段受到保护. 之前还是有可能拿到带有调试符号的版本.

  • 进阶使用:
    如果您只需要strip特定的target(例如libgcc, libstdc++),可以只单独strip他们:

      make install-libgcc-strip install-libstdc++-strip
    

    (仅为举例,不一定准确. 自行查阅对应target名称).

2. 修改 Makefile (细粒度控制,复杂)

如果上面的方法不能满足你的需求,想要更精细地控制编译选项,可以尝试修改生成的 Makefile

  • 原理: GCC 的构建系统会根据 configure 脚本的配置生成一系列的 Makefile 文件。这些文件中定义了编译目标、依赖关系和编译选项。通过直接修改 Makefile,可以更直接地控制编译过程。

  • 操作:

    1. 先执行 configuremake
      ./configure --disable-bootstrap
      make
      
    2. 找到相关的 Makefile: 通常,要修改的目标文件会位于 gcc 目录下。比如, 要想控制最终生成的 cc1 的编译选项,需要找到其相关的Makefile(比如 gcc/Makefile).
    3. 修改编译选项: 在找到的 Makefile 中,搜索并修改相关的编译标志变量.
      • 举例: 你可以搜索类似于 cc1$(exeext): $(objpfx)cc1.o (注意这是例子! 不保证通用).找到类似这样的语句:
        $(CC) $(CFLAGS) $(INCLUDES) -o cc1$(exeext) $(objpfx)cc1.o .... 此时, CFLAGS就是需要修改的点, 你可以直接加到CFLAGS上, 比如: $(CC) $(CFLAGS) -s $(INCLUDES) ...
    4. 继续进行makemake install.
  • 额外安全建议 :直接编辑Makefile, 修改难度大,出错几率也高.

  • 进阶使用:

    这种方法灵活性很高,你可以控制任意的flag,甚至单独控制部分文件的编译flag. 但是每次 ./configure 之后, 都需要重复这些编辑操作. 你可以考虑自己写脚本来进行自动编辑.

3. 利用 CFLAGS_FOR_TARGETLDFLAGS_FOR_TARGET (可能有用,不保证)

根据有限的文档, 和我的部分尝试, 可以试试看使用CFLAGS_FOR_TARGETLDFLAGS_FOR_TARGET.

  • 原理: 虽然文档没说清楚, 但是从名字判断, 这些变量可能是用来控制编译目标库和程序的标志.

  • 操作:

    ./configure --disable-bootstrap CFLAGS_FOR_TARGET="-O2 -s" LDFLAGS_FOR_TARGET="-s"
    make
    make install
    
  • 额外建议: 这个效果我没有完整的测试出来. 在一些情况下, 对最终的install 阶段产物, 可能并没有生效. 请务必自己测试确认.

  • 进阶使用: 同Makefile 部分的分析, 如果发现这个确实会影响到部分的Makefile, 但不完全是想要的, 可以结合 Makefile 编辑来最终达成效果.

4. 使用自定义的配置文件(config.make)

GCC允许你在顶层目录创建一个名为 config.make的文件, 你可以在此指定某些flag.

  • 原理config.make文件提供了一种在不改变 configure 脚本的情况下更改默认设置的方式.

  • 操作
    1. 在GCC源代码根目录中创建config.make文件。
    2. 添加相应的选项:
    makefile CFLAGS_FOR_TARGET = -O2 -s LDFLAGS_FOR_TARGET = -s

  • 注意事项 : 如同方法3, 这个也可能有局限性, 请充分测试。

其他提示

  • 如果你对具体的 flag 来源感到困惑, 一个debug 的小技巧是利用 make -n, 或者 make --dry-run. 这个命令不会实际编译, 但是会打出实际要执行的编译命令. 从中你就可以找到flag是如何一步步添加进去的。

总结下, 这几个方法, 从易到难. make install-strip 最省事。 修改 Makefile 最灵活, 但需要仔细寻找。 最后的几个flag设置有不确定性。 请大家自行按需选择!