2.3构建工具链
2.3.1进入LFS包编译目录
代码:
cd $LFS/sources
从现在开始一直到构建工具链结束,重新启动恢复工作状态的步骤如下:
1.重启进入 Ubuntu 系统,开启 bash
2.加载分区
export LFS=/mnt/lfs
sudo mkdir -pv $LFS
sudo mount -v -t ext3 /dev/sda3 $LFS
3.建立工具链的链接
sudo ln -sv $LFS/tools /
4.创建lfs用户
sudo groupadd lfs
sudo useradd -s /bin/bash -g lfs -m -k /dev/null lfs
sudo passwd lfs
sudo chown -v lfs $LFS/tools
sudo chown -v lfs $LFS/sources
su - lfs
5.建立lfs用户的环境
cat > ~/.bash_profile << "EOF"
exec env -i HOME=$HOME TERM=$TERM PS1='/u:/w/$ ' /bin/bash
EOF
cat > ~/.bashrc << "EOF"
set +h
umask 022
LFS=/mnt/lfs
LC_ALL=POSIX
PATH=/tools/bin:/bin:/usr/bin
export LFS LC_ALL PATH
EOF
source ~/.bash_profile
可以用export命令查看输出,结果应如前所示。
6.进入编译目录
cd $LFS/sources
基本上就恢复工作状态了。
2.3.2工具链开发部分相应代码及注释
参考LFS手册《LFS-BOOK-6.3-rc1-HTML》和《[原创]第二版 手把手教你如何建立自己的Linux系统(LFS速成手册)》中构建工具链的章节我们来进行本部分工具链的开发,命令行含义解释参考了《LFS-BOOK-6.3-rc1-HTML》和金步国等翻译的《Linux From Scratch 版本6.2》中的说明。为节约篇幅,一些基本的命令,如cp等,我们不进行解释,同时如果后续步骤的一些命令在前面的步骤中已作出解释则不再重复。
2.3.2.1Binutils-2.17 - Pass 1
代码:
tar xvf /lfs-sources/binutils-2.17.tar.bz2
cd binutils-2.17
mkdir -v ../binutils-build
cd ../binutils-build
CC="gcc -B/usr/bin/" ../binutils-2.17/configure /
--prefix=/tools --disable-nls --disable-werror
make
make install
make -C ld clean
make -C ld LIB_PATH=/tools/lib
cp -v ld/ld-new /tools/bin
cd ..
rm -rf binutils-build
rm -rf binutils-2.17
命令行含义:
tar xvf
早期的tar命令需要根据包的压缩方式来指定解压参数,如bz2使用j,gz使用z,因为此处用的tar程序较新,具有自动识别后缀名并自动调用相应的解压缩工具的能力,所以用xvf参数解压即可;
CC="gcc -B/usr/bin/"
强制gcc优先使用宿主系统/usr/bin目录下的连接器,在一些此处新编译出的ld连接器和主系统的gcc编译器不兼容的主系统上该选项是必要的;
--prefix=/tools
该参数告诉 configure 脚本把 Binutils 包中的程序安装到/tools 目录中;
--disable-nls
这个参数禁止了国际化(通常简称i18n),静态程序不需要国际化的特性;
--disable-werror
这个参数可以防止由于你的系统提供的gcc在产生警告事件时停止创建过程;
make
进行编译;
make install
进行安装;
-C ld clean
告诉 make 程序删除所有 ld 子目录中编译生成的文件;
-C ld LIB_PATH=/tools/lib
这个选项重新编译 ld 子目录中的所有文件。在命令行中指定 Makefile 的 LIB_PATH 变量值,使它明确指向临时工具目录,以覆盖默认值。这个变量的值指定了连接器的默认库搜索路径,它在稍后部分会用到。
2.3.2.2 GCC-4.1.2 - Pass 1
代码:
tar xvf /lfs-sources/gcc-4.1.2.tar.bz2
mkdir -v gcc-build
cd gcc-build
CC="gcc -B/usr/bin/" ../gcc-4.1.2/configure --prefix=/tools /
--with-local-prefix=/tools --disable-nls /
--enable-shared --enable-languages=c
make bootstrap
make install
ln -vs gcc /tools/bin/cc
cd ..
rm -rf gcc-build
rm -rf gcc-4.1.2
命令行含义:
--with-local-prefix=/tools
该参数将 /usr/local/include 从 gcc 的 include 搜索路径里删除,这样做并不是绝对必要的,但可以帮助最小化宿主系统的影响;
--enable-shared
该参数使能编译出 libgcc_s.so.1 和 libgcc_eh.a 。可用的libgcc_eh.a 保证了下面编译Glibc包时配置脚本产生正确的结果;
--enable-languages=c
该参数使只编译 GCC 软件包中的 C 编译器;
bootstrap
make使用这个参数的目的不仅仅是编译 GCC ,而是重复编译它几次。它用第一次编译生成的程序来第二次编译自己,然后又用第二次编译生成的程序来第三次编译自己,最后比较第二次和第三次编译的结果,以确保编译器能毫无差错的编译自身,这通常表明编译是正确的;
ln -vs gcc /tools/bin/cc
创建符号链接,/tools/bin/cc是gcc的符号链接
2.3.2.3Linux-2.6.22.5 API Headers
代码:
tar xvf /lfs-sources/linux-2.6.22.5.tar.bz2
cd linux-2.6.22.5
make mrproper
make headers_check
make INSTALL_HDR_PATH=dest headers_install
cp -rv dest/include/* /tools/include
cd ..
rm -rf linux-2.6.22.5
命令行含义:
make mrproper
清除目录下所有配置文件和先前产生的中间文件,为编译提供一个纯净的环境。
make headers_check
检查内核头文件。
make INSTALL_HDR_PATH=dest headers_install
安装内核头文件到dest目录下。
2.3.2.4Glibc-2.5.1
代码:
tar xvf /lfs-sources/glibc-2.5.1.tar.bz2
cd glibc-2.5.1
mkdir -v ../glibc-build
cd ../glibc-build
../glibc-2.5.1/configure --prefix=/tools --disable-profile /
--enable-add-ons --enable-kernel=2.6.0 /
--with-binutils=/tools/bin --without-gd /
--with-headers=/tools/include --without-selinux
make
此处编译出现错误,错误信息为:
make[1]: *** No rule to make target ‘/mnt/lfs/sources/glibc-build/Versions.all’,needed by ‘/mnt/lfs/sources/glibc-build/abi-versions.h’. Stop.
make[1]: Leaving directory ‘/mnt/lfs/sources/glibc-2.4’
make: *** [all] Error 2
通过上网搜索得知这是主系统的问题,解决方法:卸载主系统原先的mawk,卸载方法:sudo apt-get remove --purge mawk ,再在主系统中安装gawk,然后运行上述configure,make两步,就没有错误了。
代码(接上):
mkdir -v /tools/etc
touch /tools/etc/ld.so.conf
make install
cd ..
rm -rf glibc-build
rm -rf glibc-2.5.1
命令行含义:
--disable-profile
它关掉了 profiling 信息相关的库文件编译。如果你打算做 profiling ,就省掉这个参数。
--enable-add-ons
这个指示 Glibc 使用附加的 NPTL 包作为线程库。
--enable-kernel=2.6.0
这个告诉 Glibc 编译支持 2.6.x 内核的库。
--with-binutils=/tools/bin
这个参数并不是必需的。但它们能保证在编译 Glibc 时不会用错 Binutils 程序。
--without-gd
这个参数保证不生成 memusagestat 程序,这个程序会顽固地连接到宿主系统的库文件(libgd, libpng, libz 等等)。
--with-headers=/tools/include
这个参数指示 Glibc 按照前面刚刚安装到 tools 目录中的内核头文件编译自己,从而精确的知道内核的特性以根据这些特性对自己进行最佳化编译。
--without-selinux
当从一个含有 SELinux 特性的宿主系统(如 Fedora Core 3)编译时,Glibc 将会将 SELinux 支持编译进来。由于 LFS 工具链并不包含 SELinux 支持,所以一个含有 SELinux 特性的 Glibc 将会导致许多操作失败。所以这里明确禁用它。
touch /tools/etc/ld.so.conf
在安装 Glibc 的过程中,它会警告缺少 /tools/etc/ld.so.conf 文件,所以使用上述命令防止产生该警告,touch命令用来改变档案的时间记录,如档案不存在,则建立一个新的档案。
2.3.2.5调整工具链
代码:
mv -v /tools/bin/{ld,ld-old}
mv -v /tools/$(gcc -dumpmachine)/bin/{ld,ld-old}
mv -v /tools/bin/{ld-new,ld}
ln -sv /tools/bin/ld /tools/$(gcc -dumpmachine)/bin/ld
gcc -dumpspecs | sed ‘s@^/lib/ld-linux.so.2@/tools&@g’/
> `dirname $(gcc -print-libgcc-file-name)`/specs
GCC_INCLUDEDIR=`dirname $(gcc -print-libgcc-file-name)`/include
find ${GCC_INCLUDEDIR}/* -maxdepth 0 -xtype d -exec rm -rvf '{}' /;
rm -vf `grep -l "DO NOT EDIT THIS FILE" ${GCC_INCLUDEDIR}/*`
unset GCC_INCLUDEDIR
测试工具链的调整:
代码:
echo 'main(){}' > dummy.c
cc dummy.c
readelf -l a.out | grep ': /tools'
如果输出大致如下
[Requesting program interpreter: /tools/lib/ld-linux.so.2]
则表示调整成功,因为所有的库已经连接到了/tools/lib下。
代码:
rm -rf a.out dummy.c
2.3.2.6测试工具安装
代码:
tar xvf /lfs-sources/tcl8.4.15-src.tar.gz
cd tcl8.4.15/unix
./configure --prefix=/tools
make
make install
make install-private-headers
ln -sv tclsh8.4 /tools/bin/tclsh
cd $LFS/sources
tar xvf /lfs-sources/expect-5.43.0.tar.gz
cd expect-5.43
patch -Np1 -i /lfs-sources/expect-5.43.0-spawn-1.patch
cp configure{,.bak}
sed 's:/usr/local/bin:/bin:' configure.bak > configure
./configure --prefix=/tools --with-tcl=/tools/lib /
--with-tclinclude=/tools/include /
--with-x=no
make
make SCRIPTS="" install
cd $LFS/sources
tar xvf /lfs-sources/dejagnu-1.4.4.tar.gz
cd dejagnu-1.4.4
./configure --prefix=/tools
make install
cd ..
rm -rf tcl8.4.15
rm -rf expect-5.43
rm -rf dejagnu-1.4.4
命令行含义:
make install-private-headers
安装 Tcl 头文件,下一个包(Expect)要使用 Tcl 的头文件。
sed 's:/usr/local/bin:/bin:' configure.bak > configure
强制Expect的配置脚本使用/bin/stty代替可能在主系统中找到的/usr/local/bin/stty。
--with-tcl=/tools/lib
这个选项确保配置脚本找到的是安装在临时工具目录下的 Tcl ,而不是宿主系统里的。
--with-tclinclude=/tools/include
这个选项显式告诉 Expect 到哪里寻找 Tcl 的内部头文件。使用这个选项可以避免 configure 脚本因为找不到 Tcl 的头文件位置而导致失败。
--with-x=no
这个选项告诉 configure 脚本不要搜索 Tk(Tcl的图形界面组件)或者 X Window 系统的库,这两者都可能位于宿主系统上而不存在于目前的临时环境中。
SCRIPTS=""
make的这个选项防止安装 Expect 所补充的一些并不需要的脚本。
2.3.2.7GCC-4.1.2 - Pass 2
代码:
tar xvf /lfs-sources/gcc-4.1.2.tar.bz2
cd gcc-4.1.2
cp -v gcc/Makefile.in{,.orig}
sed 's@/./fixinc/.sh@-c true@' gcc/Makefile.in.orig > gcc/Makefile.in
cp -v gcc/Makefile.in{,.tmp}
sed 's/^XCFLAGS =$/& -fomit-frame-pointer/' gcc/Makefile.in.tmp /
> gcc/Makefile.in
patch -Np1 -i /lfs-sources/gcc-4.1.2-specs-1.patch
mkdir -v ../gcc-build
cd ../gcc-build
../gcc-4.1.2/configure --prefix=/tools /
--with-local-prefix=/tools --enable-clocale=gnu /
--enable-shared --enable-threads=posix /
--enable-__cxa_atexit --enable-languages=c,c++ /
--disable-libstdcxx-pch
make
make -k check //花费时间很长,此处有两个错误
make install
cd ..
rm -rf gcc-build
rm -rf gcc-4.1.2
命令行含义:
sed 's@/./fixinc/.sh@-c true@' gcc/Makefile.in.orig > gcc/Makefile.in
在 GCC 编译过程中会运行 fixincludes 脚本来扫描系统头文件目录,并找出需要修正的头文件,然后把修正后的头文件放到 GCC 专属头文件目录里。因为现在 GCC 和 Glibc 已经安装完毕,而且它们的头文件已知无需修正,所以这里并不需要 fixincludes 脚本。另外,由于 GCC 专属头文件目录会被优先搜索,结果就是 GCC 使用的头文件是宿主系统的头文件,而不是新安装的那个,从而导致编译环境被"污染"。因此必须通过下面的命令来禁止 fixincludes 脚本运行;
sed 's/^XCFLAGS =$/& -fomit-frame-pointer/' gcc/Makefile.in.tmp /
> gcc/Makefile.in
在2.3.2.2 GCC-4.1.2 - Pass 1中进行的 bootstrap 编译使用了 -fomit-frame-pointer 选项,而非 bootstrap 编译则默认忽略了该选项,所以使用上述 sed 命令来确保在非 bootstrap 编译时也同样使用 -fomit-frame-pointer 选项,以保持一致性;
--enable-clocale=gnu
本参数确保 C++ 库在任何情况下都使用正确的 locale 模块。如果配置脚本查找到 de_DE 这个 locale ,它就会使用正确的 gnu locale 模块。然而,如果没有安装 de_DE ,就有可能创建出应用程序二进制接口(ABI)不兼容的 C++ 库文件,这是因为选择了错误的通用locale 模块;
--enable-threads=posix
使 C++ 异常能处理多线程代码;
--enable-__cxa_atexit
用 __cxa_atexit 代替 atexit 来登记 C++ 对象的本地静态和全局析构函数,这是为了完全符合标准对析构函数的处理规定。它还会影响到 C++ ABI ,因此致使 C++ 共享库和C++程序在其他 Linux 发行版上也能使用;
--enable-languages=c,c++
本参数确保C 和 C++ 语言编译器被编译;
--disable-libstdcxx-pch
不为 libstdc++ 编译预编译头(PCH),它占用了很大空间,但是我们用不到它;
make -k check
-k 参数是让测试套件即使遇到错误,也继续运行,直到完成。
再次测试工具链的调整,以确保刚刚编译gcc的工作正确:
代码:
echo 'main(){}' > dummy.c
cc dummy.c
readelf -l a.out | grep ': /tools'
如果输出大致如下
[Requesting program interpreter: /tools/lib/ld-linux.so.2]
则表示调整成功,因为所有的库已经连接到了/tools/lib下。
代码:
rm -rf a.out dummy.c
如果输出不是像上面那样或者根本没有输出,那么就有大问题了。返回并检查前面的操作,找出问题,并改正过来。在改正之前,不要继续后面的部份。首先,再次进行上述检查,用 gcc 代替 cc ,如果工作正常,那么是因为 /tools/bin/cc 这个符号链接丢失了。回头看看2.3.2.2 GCC-4.1.2-Pass1并建立符号链接。接下来,确保 PATH 正确。检查时,运行 echo $PATH 并检查 /tools/bin 是否在列表的最前面。如果 PATH 错误,可能是因为你没有以 lfs 用户登录,或者在2.2.3.6设置环境部分出错了。另外一个原因可能是上面修正 specs 文件时出错,如果这样,重新修改 specs 文件。
2.3.2.8Binutils-2.17 - Pass 2
代码:
tar xvf /lfs-sources/binutils-2.17.tar.bz2
mkdir -v binutils-build
cd binutils-build
../binutils-2.17/configure --prefix=/tools --disable-nls /
--with-lib-path=/tools/lib
make
make check
make install
make -C ld clean
make -C ld LIB_PATH=/usr/lib:/lib
cp -v ld/ld-new /tools/bin
cd ..
rm -rf binutils-build
rm -rf binutils-2.17
命令行含义:
--with-lib-path=/tools/lib
这个选项指示 configure 脚本在 Binutils 编译过程中将传递给连接器的库搜索路径设为 /tools/lib ,以防止连接器搜索宿主系统的库目录。
2.3.2.9Ncurses-5.6
代码:
tar xvf /lfs-sources/ncurses-5.6.tar.gz
cd ncurses-5.6
./configure --prefix=/tools --with-shared /
--without-debug --without-ada --enable-overwrite
make
make install
cd ..
rm -rf ncurses-5.6
命令行含义:
--with-shared
使能建立共享的ncurses库文件;
--without-ada
这个选项让 Ncurses 在即使宿主系统上安装了 Ada 编译器的情况下也不要编译其 Ada 支持。需要这样做的原因是一旦我们进入 chroot 环境,Ada 就不能使用了;
--enable-overwrite
这个选项让 Ncurses 把它的头文件安装到 /tools/include 目录,而不是 /tools/include/ncurses 目录,以确保其它软件包可以顺利找到 Ncurses 的头文件。
2.3.2.10Bash-3.2
代码:
tar xvf /lfs-sources/bash-3.2.tar.gz
cd bash-3.2
patch -Np1 -i /lfs-sources/bash-3.2-fixes-5.patch
./configure --prefix=/tools --without-bash-malloc
make
此处编译错误,提示
yacc -d ./parse.y
make: yacc: Command not found
make: *** [y.tab.c] Error 127
经检查依然是宿主系统的问题,在新立德中安装bison后重新编译即可通过。
代码(接上):
make install
ln -vs bash /tools/bin/sh
cd ..
rm -rf bash-3.2
命令行含义:
--without-bash-malloc
这个选项禁用了 Bash 的内存分配函数(malloc),这个函数已知会造成段错误,通过设置这个选项,Bash 将使用更为稳定的 Glibc 里的 malloc 函数。
2.3.2.11Bzip2-1.0.4
代码:
tar xvf /lfs-sources/bzip2-1.0.4.tar.gz
cd bzip2-1.0.4
make
make PREFIX=/tools install
cd ..
rm -rf bzip2-1.0.4
2.3.2.12Coreutils-6.9
代码:
tar xvf /lfs-sources/coreutils-6.9.tar.bz2
cd coreutils-6.9
./configure --prefix=/tools
make
make install
cp -v src/su /tools/bin/su-tools
cd ..
rm -rf coreutils-6.9
命令行含义:
cp -v src/su /tools/bin/su-tools
因为当前不是根用户,用上述命令无法安装su这个工具,所以我们拷贝su工具到工具链目录中,但是用了一个新的名字su-tools。
2.3.2.13Diffutils-2.8.1
代码:
tar xvf /lfs-sources/diffutils-2.8.1.tar.gz
cd diffutils-2.8.1
./configure --prefix=/tools
make
make install
cd ..
rm -rf diffutils-2.8.1
2.3.2.14Findutils-4.2.31
代码:
tar xvf /lfs-sources/findutils-4.2.31.tar.gz
cd findutils-4.2.31
./configure --prefix=/tools
make
make install
cd ..
rm -rf findutils-4.2.31
2.3.2.15Gawk-3.1.5
代码:
tar xvf /lfs-sources/gawk-3.1.5.tar.bz2
cd gawk-3.1.5
./configure --prefix=/tools
cat >> config.h << "EOF"
#define HAVE_LANGINFO_CODESET 1
#define HAVE_LC_MESSAGES 1
EOF
make
make install
cd ..
rm -rf gawk-3.1.5
命令行含义:
cat >> config.h << "EOF"
#define HAVE_LANGINFO_CODESET 1
#define HAVE_LC_MESSAGES 1
EOF
由于 configure 脚本的一个 bug,Gawk 不能正确检测某些 Glibc 支持的 locale,这将会导致一些问题,比如,Gettext 的测试程序会失败。所以在 config.h 文件结尾追加丢失的宏定义修复这个 bug。
2.3.2.16Gettext-0.16.1
代码:
tar xvf /lfs-sources/gettext-0.16.1.tar.gz
cd gettext-0.16.1
cd gettext-tools
./configure --prefix=/tools --disable-shared
make -C gnulib-lib
make -C src msgfmt
cp -v src/msgfmt /tools/bin
cd $LFS/sources
rm -rf gettext-0.16.1
命令行含义:
--disable-shared
当前我们不需要安装任何 Gettext 共享库,因此也就不需要编译它们。
2.3.2.17Grep-2.5.1a
代码:
tar xvf /lfs-sources/grep-2.5.1a.tar.bz2
cd grep-2.5.1a
./configure --prefix=/tools --disable-perl-regexp
make
make install
cd ..
rm -rf grep-2.5.1a
命令行含义:
--disable-perl-regexp
这个选项确保 grep 程序不连接可能在宿主系统上存在的 PCRE(Perl 兼容正则表达式)库,因为进入 chroot 环境后,它就不能使用了。
2.3.2.18Gzip-1.3.12
代码:
tar xvf /lfs-sources/gzip-1.3.12.tar.gz
cd gzip-1.3.12
./configure --prefix=/tools
make
make install
cd ..
rm -rf gzip-1.3.12
2.3.2.19Make-3.81
代码:
tar xvf /lfs-sources/make-3.81.tar.bz2
cd make-3.81
./configure --prefix=/tools
make
make install
cd ..
rm -rf make-3.81
2.3.2.20Patch-2.5.4
代码:
tar xvf /lfs-sources/patch-2.5.4.tar.gz
cd patch-2.5.4
./configure --prefix=/tools
make
make install
cd ..
rm -rf patch-2.5.4
2.3.2.21Perl-5.8.8
代码:
tar xvf /lfs-sources/perl-5.8.8.tar.bz2
cd perl-5.8.8
patch -Np1 -i /lfs-sources/perl-5.8.8-libc-2.patch
./configure.gnu --prefix=/tools /
-Dstatic_ext='Data/Dumper Fcntl IO POSIX'
make perl utilities
cp -v perl pod/pod2man /tools/bin
mkdir -pv /tools/lib/perl5/5.8.8
cp -Rv lib/* /tools/lib/perl5/5.8.8
cd ..
rm -rf perl-5.8.8
命令行含义:
-Dstatic_ext='Data/Dumper Fcntl IO POSIX'
这个选项让 Perl 编译静态扩展的最小集,稍后构建目标系统中安装测试 Coreutils 和Glibc软件包的时候需要用到;
make perl utilities
仅编译这个软件包中的一小部分必要工具。
2.3.2.22Sed-4.1.5
代码:
tar xvf /lfs-sources/sed-4.1.5.tar.gz
cd sed-4.1.5
./configure --prefix=/tools
make
make install
cd ..
rm -rf sed-4.1.5
2.3.2.23Tar-1.18
代码:
tar xvf /lfs-sources/tar-1.18.tar.bz2
cd tar-1.18
./configure --prefix=/tools
make
make install
cd ..
rm -rf tar-1.18
2.3.2.24Texinfo-4.9
代码:
tar xvf /lfs-sources/texinfo-4.9.tar.bz2
cd texinfo-4.9
./configure --prefix=/tools
make
make install
cd ..
rm -rf texinfo-4.9
2.3.2.25Util-linux-2.12r
代码:
tar xvf /lfs-sources/util-linux-2.12r.tar.bz2
cd util-linux-2.12r
sed -i 's@/usr/include@/tools/include@g' configure
./configure
make -C lib
make -C mount mount umount
make -C text-utils more
cp -v mount/{,u}mount text-utils/more /tools/bin
cd ..
rm -rf util-linux-2.12r
命令行含义:
sed -i 's@/usr/include@/tools/include@g' configure
Util-linux 默认不使用刚才安装在 /tools 目录下的头文件和库文件,我
们更改配置脚本来修正这个问题;
make -C lib
编译一些支持例程;
make -C mount mount umount
make -C text-utils more
我们只需要包中少数几个工具,因此只编译这几个工具即可。
2.3.3退出lfs用户
代码:
exit