为了创建一个干净的工作环境,在宿主系统中新创建一个lfs用户组,并添加了lfs用户,在安装过程中将一直使用该用户。
首先编译Binutils ,这时是使用宿主系统的环境。毫无疑问现在利用Binutils生成的程序会受到宿主系统的影响。例如:使用生成的ld(标准连接器)程序将会连接到默认的/lib目录(宿主系统)下的二进制文件。
然后编译Gcc,仍然需要宿主系统的环境。显然Gcc也受宿主系统的影响,这可以从它的编译来看,它依赖的是宿主的Glibc,而Binutils可以使用刚生成的。Glibc提供了动态连接器,用来找到并加载一个程序运行时所需的共享库,在做好程序运行的准备之后,运行这个程序。此时生成的Gcc会使用/lib(宿主系统)下的动态连接器,而不是$LFS/tools/lib下的。
通过上面两步,我们就可以使用刚生成的bintuils和Gcc来编译Glibc了。现在我们将bintuils、Gcc和Glibc都重新编译了一次。接下来就要通过调整工具链来解决刚提到的两个问题。一是重新编译ld,将ld连接到$LFS/tools/lib下的函数库。二是调整动态连接器,修改Gcc的SPEC文件,将动态连接器连接到/tools/lib/ld-Linux.so.2(ld-Linux.so.2是动态连接器的名字)。
到这里看似可以编译其它的工具了,但是接下来的工作并不是如此,而是再次编译了bintuils和Gcc,然后用第一次生成的Glibc和现在生成的bintuils、Gcc来编译其它的工具,整个临时环境才搭建成功。
这里有个问题是为什么要将Bintuils和Gcc编译两次,可以直接用宿主系统?第一次编译bintuils和Gcc的目的一方面是为了编译Glibc;另一方面是为了能自己编译出第二遍的Gcc,即使得Gcc是自我编译的。如果直接使用宿主系统可以满足编译Glibc的要求,但是Gcc就不是自我编译了。这里为了保证制造的正确性以及使Gcc是自我编译,所以Binutils和Gcc比其它的工具都多编译一次。
接下面其它工具的编译都是使用第二次编译的Binutils和Gcc以及第一次编译的Glibc。至此,工具链就准备好了,我们可以利用这些工具生成最终的系统。同样最先生成的软件还是Binutils和Gcc,不过在编译它们之前,我们先编译出Glibc,它也是我们最终需要的C库。再次调整工具链,让随后编译的工具都连接到这个库上。不难理解,在前面的调整中我们将工具链使用的库从宿主系统转向新安装的库目录。同样,现在将工具链所使用的库从临时的库转向LFS系统最终的库目录。
glibc是自包含:
These items highlight an important
aspect of the Glibc package—it is very self-sufficient in terms
of its build machinery and generally does not rely on toolchain
defaults.
其实用制作交叉编译链更容易理解,试想在x86平台,制作arm的工具链。此交叉工具链是运行在x86平台,但是编译出的代码是只能运行在arm平台。
那么包括交叉的二进制工具,gcc和glibc。二进制工具包括as和ld等。
gcc是个应用程序,必然会使用glibc的相关库函数,明显编译的时候就要使用malloc,所以gcc以来glibc。
第一遍用本地x86 gcc编译出binutils,gcc,可想这两个工具依赖的是host的glibc库,然后使用刚编译出来的两个工具编译glibc:
../glibc-2.10.1/configure --prefix=/tools \
--host=$LFS_TGT --build=$(../glibc-2.10.1/scripts/config.guess) \
--enable-kernel=2.6.18 --with-headers=/tools/include
由于PATH路径中,第一个写的就是/tools,所以编译glibc的gcc和bin是/tools中新生成的工具。
--with-headers指定使用/tools/include,也就是新的内核头文件。
然后调整工具链指向新的glibc:
SPECS=`dirname $($LFS_TGT-gcc -print-libgcc-file-name)`/specs
$LFS_TGT-gcc -dumpspecs | sed \
-e 's@/lib\(64\)\?/ld@/tools&@g' \
-e "/^\*cpp:$/{n;s,$, -isystem /tools/include,}" > $SPECS
echo "New specs file is: $SPECS"
unset SPECS
使glibc已经安装完毕,修改gcc的specs文件使gcc连结库的路径修改为/tools目录。
这是再编译任何elf的时候,就会把/tools/lib/ld-linux.so.2硬编码进入elf文件。这就是指定了新的连接器。
工具链调整完成后,第二次编译binutils和gcc。这样就脱离了host的库,实现了自包含自依赖。
使用qemu做lfs的时候,经常出现out of memory。后来google后发现,原来是内存不足造成的,原来我为了图省事并没有使用swap空间。给qemu只分配了200M内存。编译gcc的时候内存不足。