8.29. GCC-14.2.0

GCC 软件包包含 GNU 编译器集合,其中有 C 和 C++ 编译器。

估计构建时间: 46 SBU (已计入测试时间)
需要硬盘空间: 6.3 GB

8.29.1. 安装 GCC

在 x86_64 上构建时,修改存放 64 位库的默认路径为 lib:

case $(uname -m) in
  x86_64)
    sed -e '/m64=/s/lib64/lib/' \
        -i.orig gcc/config/i386/t-linux64
  ;;
esac

GCC 文档建议在一个新建的目录中构建 GCC:

mkdir -v build
cd       build

准备编译 GCC:

../configure --prefix=/usr            \
             LD=ld                    \
             --enable-languages=c,c++ \
             --enable-default-pie     \
             --enable-default-ssp     \
             --enable-host-pie        \
             --disable-multilib       \
             --disable-bootstrap      \
             --disable-fixincludes    \
             --with-system-zlib

GCC 支持七种程序设计语言,但其中多数语言需要尚未安装的依赖项。阅读 BLFS 手册,以了解如何构建 GCC 支持的所有语言。

新的配置选项的含义:

LD=ld

该选项使得配置脚本使用之前在本章中构建的 Binutils 提供的 ld 程序,而不是交叉编译构建的版本。

--disable-fixincludes

默认情况下,在安装 GCC 时,一些系统头文件会被修复以供 GCC 使用。这对于现代的 Linux 系统来说是不必要的,而且如果一个软件包在安装 GCC 后被重新安装,还可能造成损害。这个开关阻止 GCC 尝试修复头文件。

--with-system-zlib

该选项使得 GCC 链接到系统安装的 Zlib 库,而不是它自带的 Zlib 副本。

[注意]

注意

PIE (位置无关可执行文件) 是能加载到内存中任意位置的二进制程序。在不使用 PIE 时,称为 ASLR (地址空间布局随机化) 的安全特性能被用于共享库,但不能被用于可执行程序本身。启用 PIE 使得 ASLR 在作用于共享库的同时,同样作用于可执行程序,以预防一些基于可执行程序中关键代码或数据的固定地址的攻击。

SSP (栈溢出防护) 是保证程序的调用栈不被破坏的技术。在调用栈被破坏时可能导致安全问题,例如子程序的返回地址可能被修改,进而执行一些危险代码 (这些危险代码可能已经存在于程序或共享库中,或被攻击者用某种方式注入)。

编译该软件包:

make
[重要]

重要

在本节中,GCC 的测试套件十分重要,但需要消耗较长的时间。我们建议首次编译 LFS 的读者运行测试套件。通过在以下命令中添加 -jx 参数,可以显著降低测试需要的时间,其中 x 表示系统 CPU 核心数。

GCC 在编译一些具有复杂模式的代码时,可能需要更多的栈空间。万一宿主系统的栈空间限制较为严格,我们需要手工将栈空间的硬上限设为无限大。在多数宿主发行版 (和最终的 LFS 系统中),默认的硬上限值已经是无限大,但手工调整限制无论如何都不会造成损害。我们不需要调整软上限,因为 GCC 会自动将软上限设为合适的值,只要确保该值不会超过硬限制即可。

ulimit -s -H unlimited

现在移除或修复若干已知会失败的测试:

sed -e '/cpython/d'               -i ../gcc/testsuite/gcc.dg/plugin/plugin.exp
sed -e 's/no-pic /&-no-pie /'     -i ../gcc/testsuite/gcc.target/i386/pr113689-1.c
sed -e 's/300000/(1|300000)/'     -i ../libgomp/testsuite/libgomp.c-c++-common/pr109062.c
sed -e 's/{ target nonpic } //' \
    -e '/GOTPCREL/d'              -i ../gcc/testsuite/gcc.target/i386/fentryname3.c

以非特权用户身份测试编译结果,但出错时继续执行其他测试:

chown -R tester .
su tester -c "PATH=$PATH make -k check"

输入以下命令提取测试结果的摘要:

../contrib/test_summary

如果只想看摘要,将输出用管道送至 grep -A7 Summ

可以将结果与 https://www.linuxfromscratch.org/lfs/build-logs/12.2/https://gcc.gnu.org/ml/gcc-testresults/ 的结果进行比较。

少量意外的失败有时无法避免。某些情况下特定的硬件配置可能触发测试失败。我们可以放心地忽略测试失败并继续构建系统,除非测试结果和上述 URL 提供的结果截然不同。

安装该软件包:

make install

GCC 构建目录目前属于用户 tester,导致安装的头文件目录 (及其内容) 具有不正确的所有权。将所有者修改为 root 用户和组:

chown -v -R root:root \
    /usr/lib/gcc/$(gcc -dumpmachine)/14.2.0/include{,-fixed}

创建一个 FHS 因 “历史原因” 要求的符号链接。

ln -svr /usr/bin/cpp /usr/lib

许多软件包使用 cc 这一名称调用 C 编译器。在第二遍的 GCC 中我们已经将 cc 创建为符号链接,这里将其手册页也创建为符号链接:

ln -sv gcc.1 /usr/share/man/man1/cc.1

创建一个兼容性符号链接,以支持在构建程序时使用链接时优化 (LTO):

ln -sfv ../../libexec/gcc/$(gcc -dumpmachine)/14.2.0/liblto_plugin.so \
        /usr/lib/bfd-plugins/

现在最终的工具链已经就位,重要的是再次确认编译和链接像我们期望的一样正常工作。为此,进行下列完整性检查:

echo 'int main(){}' > dummy.c
cc dummy.c -v -Wl,--verbose &> dummy.log
readelf -l a.out | grep ': /lib'

上述命令不应该出现错误,最后一行命令输出的结果应该 (不同平台的动态链接器名称可能不同) 是:

[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

下面确认我们在使用正确的启动文件:

grep -E -o '/usr/lib.*/S?crt[1in].*succeeded' dummy.log

以上命令应该输出:

/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/../../../../lib/Scrt1.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/../../../../lib/crti.o succeeded
/usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/../../../../lib/crtn.o succeeded

以上结果可能随您的机器体系结构不同而略微不同。差异在于 /usr/lib/gcc 之后的目录名。我们关注的重点是,gcc 应该找到所有三个 crt*.o 文件,它们应该位于 /usr/lib 目录中。

确认编译器能正确查找头文件:

grep -B4 '^ /usr/include' dummy.log

该命令应当输出:

#include <...> search starts here:
 /usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/include
 /usr/local/include
 /usr/lib/gcc/x86_64-pc-linux-gnu/14.2.0/include-fixed
 /usr/include

同样要注意,以您的目标三元组命名的目录由于您体系结构的不同,可能和以上不同。

下一步确认新的链接器使用了正确的搜索路径:

grep 'SEARCH.*/usr/lib' dummy.log |sed 's|; |\n|g'

那些包含 '-linux-gnu' 的路径应该忽略,除此之外,以上命令应该输出:

SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib64")
SEARCH_DIR("/usr/local/lib64")
SEARCH_DIR("/lib64")
SEARCH_DIR("/usr/lib64")
SEARCH_DIR("/usr/x86_64-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");

在 32 位系统上可能使用不同的目录。例如,下面是 i686 机器上的输出:

SEARCH_DIR("/usr/i686-pc-linux-gnu/lib32")
SEARCH_DIR("/usr/local/lib32")
SEARCH_DIR("/lib32")
SEARCH_DIR("/usr/lib32")
SEARCH_DIR("/usr/i686-pc-linux-gnu/lib")
SEARCH_DIR("/usr/local/lib")
SEARCH_DIR("/lib")
SEARCH_DIR("/usr/lib");

之后确认我们使用了正确的 libc:

grep "/lib.*/libc.so.6 " dummy.log

以上命令应该输出:

attempt to open /usr/lib/libc.so.6 succeeded

确认 GCC 使用了正确的动态链接器:

grep found dummy.log

以上命令应该输出 (不同平台的动态链接器名称可能不同):

found ld-linux-x86-64.so.2 at /usr/lib/ld-linux-x86-64.so.2

如果输出和以上描述不符,或者根本没有输出,那么必然有什么地方出了严重错误。检查并重新跟踪以上步骤,找到问题的原因,并修复它。在继续构建前必须解决这里发现的所有问题。

在确认一切工作良好后,删除测试文件:

rm -v dummy.c a.out dummy.log

最后移动一个位置不正确的文件:

mkdir -pv /usr/share/gdb/auto-load/usr/lib
mv -v /usr/lib/*gdb.py /usr/share/gdb/auto-load/usr/lib

8.29.2. GCC 的内容

安装的程序: c++, cc (到 gcc 的链接), cpp, g++, gcc, gcc-ar, gcc-nm, gcc-ranlib, gcov, gcov-dump, gcov-tool, 以及 lto-dump
安装的库: libasan.{a,so}, libatomic.{a,so}, libcc1.so, libgcc.a, libgcc_eh.a, libgcc_s.so, libgcov.a, libgomp.{a,so}, libhwasan.{a,so}, libitm.{a,so}, liblsan.{a,so}, liblto_plugin.so, libquadmath.{a,so}, libssp.{a,so}, libssp_nonshared.a, libstdc++.{a,so}, libstdc++exp.a, libstdc++fs.a, libsupc++.a, libtsan.{a,so}, 以及 libubsan.{a,so}
安装的目录: /usr/include/c++, /usr/lib/gcc, /usr/libexec/gcc, 以及 /usr/share/gcc-14.2.0

简要描述

c++

C++ 编译器

cc

C 编译器

cpp

C 预处理器;编译器使用它展开源文件中的 #include、#define,以及类似指令

g++

C++ 编译器

gcc

C 编译器

gcc-ar

ar 的一个包装器,它在命令行中添加一个插件。这个程序只被用于提供链接时优化功能,对于默认的构建选项来说没有作用。

gcc-nm

nm 的一个包装器,它在命令行中添加一个插件。这个程序只被用于提供链接时优化功能,对于默认的构建选项来说没有作用。

gcc-ranlib

ranlib 的一个包装器,它在命令行中添加一个插件。这个程序只被用于提供链接时优化功能,对于默认的构建选项来说没有作用。

gcov

一个覆盖率测试工具;用于分析程序以确定在哪里优化最有效

gcov-dump

离线 gcda 和 gcno 性能剖析数据显示工具

gcov-tool

离线 gcda 性能剖析预处理工具

lto-dump

用于转储 GCC 启用 LTO 时生成的目标文件

libasan

地址完整性检查库

libatomic

GCC 内建原子操作运行库

libcc1

允许 GDB 使用 GCC 功能的库

libgcc

包含 gcc 的运行时支持

libgcov

在 GCC 被指示启动性能剖析时,这个库被链接到程序中

libgomp

OpenMP API 的 GNU 实现,用于 C/C++ 和 Fortran 的跨平台共享内存并行编程

libhwasan

硬件辅助地址完整性检查库

libitm

GNU 事务内存库

liblsan

内存泄露清理检查库

liblto_plugin

GCC 的 LTO 插件,允许 Binutils 处理 GCC 在启用 LTO 时生成的目标文件

libquadmath

GCC 四精度数学 API 库

libssp

包含 GCC 的栈溢出保护功能支持子程序。一般不使用它,因为 Glibc 也提供这些子程序。

libstdc++

C++ 标准库

libstdc++exp

实验性的 C++ Contracts 库

libstdc++fs

ISO/IEC TS 18822:2015 文件系统库

libsupc++

包含 C++ 编程语言支持子程序

libtsan

线程完整性检查库

libubsan

未定义行为清理检查库