本节解释一些总体构建方法的基本原理和技术细节。没有必要立刻理解其中的所有内容。大部分信息会在实际的构建操作后更加清晰,在此过程中可以随时查阅本节。
第 5 章 的总体目标是要构建一个临时的区域,这个区域包含一些好的、可以从宿主机分离的工具集。通过使用 chroot 命令,本章余下的命令都将被限制在该环境中,确保了目标LFS系统的构建干净且没有问题。构建过程已经被设计得在让新读者承担最小风险的同时提供最大有教育价值的信息。
在继续之前,请注意工作平台的名称,通常也被称作目标三联字串。一个简单的获取目标三联字串的方法是运行源码包中的 config.guess
脚本,解压Binutils的源码并且运行脚本: ./config.guess
,注意输出结果。例如,一个现代的32位因特尔处理器的输出会是 i686-pc-linux-gnu 。
同时也要注意平台的动态链接器名称,通常也被称作动态加载器(以防与Binutils中的 ld
混淆)。Glibc提供的动态链接器搜寻并加载一个程序所需要的共享库,准备运行程序然后运行程序。一个32位因特尔机器的动态链接器名称会是:
ld-linux.so.2
。一个万无一失的确定动态链接器的方法是通过运行
readelf -l <name of binary> |
grep interpreter
来查看宿主机上任何一个二进制可执行文件并注意输出结果。官方的参考覆盖了所有平台,它位于Glibc源码树根目录下的
shlib-versions
文件中。
有关第 5 章中的构建方法的一些核心技术细节如下:
轻微地调整工作平台的名字,改变 LFS_TGT
变量来改变三联字串中的"生产商",以确保首次构建的 Binutils 和 GCC
的产生兼容的交叉链接器和交叉编译器。交叉链接器和交叉编译器将产生兼容当前硬件的二进制文件而不是兼容另一个构架的二进制文件。
临时的库是交叉编译的。因为交叉编译器自然不能依赖宿主系统上的任何东西,所以这种方式减小了宿主机上的头文件和库被新工具包含的可能,从而避免了目标系统可能受到的污染。交叉编译也允许在64位兼容的硬件上既构建32位的库也构建64位的库。
谨慎地对 GCC 源码进行的操作告诉了编译器该使用哪个动态链接器。
Binutils首先被安装,因为运行 GCC 和 Glibc 的 configure 命令会对汇编器和链接器进行不同的特性测试来决定哪些软件特性会被启用,哪些会被禁用。这比你一开始就意识到的要重要。一个不正确的 GCC 或 Glibc 的配置可能会导致工具链细小的破损,这个破损所产生的影响可能会到构建整个发行版的过程快结束时才显现出来。在许多额外工作进行前,一个测试套件的失败通常会指出这个错误。
Binutils 把它的汇编器和链接器安装在两个位置: /tools/bin
和 /tools/$LFS_TGT/bin
。两个位置间的工具是硬链接。链接器很重要的一点是它搜索库的顺序。可以通过传递 --verbose
参数给 ld 获取详细信息。例如,ld --verbose | grep SEARCH
将会按顺序列出当前的搜索路径。通过编译一个伪程序并传递 --verbose
参数给链接器可以显示哪些文件被 ld 链接。例如 gcc dummy.c -Wl,--verbose 2>&1 | grep
succeeded
会显示链接过程中所有被成功打开的文件。
下一个被安装的软件包是 GCC 。在运行 configure 命令过程中能看到的输出的一例为:
checking what assembler to use... /tools/i686-lfs-linux-gnu/bin/as
checking what linker to use... /tools/i686-lfs-linux-gnu/bin/ld
对于上面提到的原因,这很重要。这也说明了GCC的配置脚本不会搜索PATH目录来寻找要用的工具。然而,在实际的 gcc 操作过程中,同样的搜索路径不必被使用。要找出
gcc 所使用的标准链接器,只要运行:
gcc
-print-prog-name=ld
。
详细信息可以通过在编译一个伪程序时传递 -v
参数给
gcc 来获得。例如,
gcc -v dummy.c
可以显示有关预处理、编译以及汇编阶段的详细信息,还包括了 gcc 的搜索路径以及其顺序。
接下来要安装的是干净的 Linux API 头文件。这些头文件将允许标准 C 库(Glibc)与 Linux内核提供的特性交互。
下一个安装的软件包是 Glibc 。构建 Glibc 最重要的考虑因素是编译器、二进制工具和内核头文件。编译器通常不是问题,因为
Glibc 将总是使用与传递给它配置脚本的参数 --host
相关的编译器,在我们的例子中是 i686-lfs-linux-gnu-gcc
。二进制工具和内核头文件会复杂一些。因此,不要冒险,使用可用的配置开关来确保正确的选择。在运行 configure 命令之后,检查在 glibc-build
中的 config.make
文件来检查所用重要信息。注意使用 CC="i686-lfs-gnu-gcc"
来控制要使用的二进制工具,使用
-nostdinc
和 -isystem
参数来控制编译器的头文件搜索路径。这些事项表明了 Glibc
的一个重要特征——从编译机理的角度来说,它非常的独立,并且通常不依赖于工具链的默认配置。
在第二遍构建 Binutils 的过程中,我们可以利用 --with-lib-path
配置开关来控制 ld 的库搜索路径。
对于 GCC 的第二遍构建,也需要调整它的源码来告诉 GCC 使用新的动态链接器。这一步的错误会导致 GCC 程序本身会把宿主系统
/lib
目录中动态链接器的名称嵌入进来,这样会有悖于与宿主分离的目标。从这时起,核心工具链就自我包含和自我寄宿了。余下的 第
5 章 的软件包的构建将依赖于 /tools
中新的Glibc。
在 第 6 章 中进入 chroot
环境时,第一个重要的要安装的软件包是 Glibc ,这是由于上面所提到的它自给自足的天性。一旦 Glibc 被安装入
/usr
,我们将进行一个快速的工具链默认配置的修改,然后继续构建 LFS
系统的余下部分。