【引言:进一步推进工作,开始工具链的制作,其间命令较多且较复杂,需谨慎细心。】
本节主要记录如何构建一个新的不依赖于宿主系统的工具链(编译器、汇编器 、 连接器、库文件以及一些有用的软件),并对每步操作进行相应说明,外加笔者遇到的一些问题及解决方案。
首先什么叫工具链?
LFS/CLFS工具链是一套用于从C/C++源代码生成可执行文件的软件组件适当地组合在一起形成的系统。它包括4大部分,缺一不可:
1. Linux API 一套头文件,包含了这些源代码所需要访问的系统接口;
2. binutils 包含一些处理二进制可执行文件所需的工具,如汇编器、连接器等;
3. gcc 包含了编译C/C++源代码所需的工具,并且还能自动调用相关的binutils工具来完成生成源代码的工具;
4. glibc 包含了系统接口的具体实现。
在上面的定义中,这些软件适当地组合形成完整的工具链。
本节编译的文件将安装在 $LFS/tools 目录下,编译出来的软件包起临时作用,后面还会继续调整工具链。
注:上节已说明,所有为输入命令都为蓝色标识,输入文本为灰色标识。
3.1 安装 Binutils (10分钟)
安装的第一个软件包是 Binutils,它包含一个链接器,一个汇编器和其他处理目标文件的工具。
cd $LFS/sources 进入工作目录;
tar xvf /lfs-sources/binutils-2.17.tar.bz2 解压binutils压缩包到当前目录;
mkdir binutils-build 创建一个专用目录来编译;
cd binutils-build 进入编译目录;
../binutils-2.15.94.0.2.2/configure --prefix=/tools --disable-nls
参数说明:
--prefix=/tools 参数告诉 configure 脚本,准备把 Binutils 软件包中的程序安装到 /tools 目录中;
--disable-nls 禁止了国际化(通常简称i18n),因为临时工具不需要i18n特性;
此步操作是为编译做准备,在当前目录下生成编译需要的 Makefile。
make 开始编译;
make install 安装;
make -C ld clean 表示进入ld目录,执行ld/Makefile中的clean编译生成的文件;
make -C ld LIB_PATH=/tools/lib 进入ld目录进行make,将make的环境变量LIB_PATH设置为/tools/lib;
cp -v ld/ld-new /tools/bin 拷贝ld目录下的ld-new文件到/tools/bin;
cd .. 回到上层目录$LFS/sources。
注意:这里暂时不要删除binutils-build目录,因为后面需要这个编译过的binutils-build
3.2 安装 GCC-4.1.2 (30分钟)
tar xvf /lfs-sources/gcc-4.1.2.tar.bz2 解压压缩包到当前目录下;
mkdir gcc-build 同上,创建一个单独的目录来编译;
cd gcc-build 进入编译目录;
../gcc-4.1.2/configure --prefix=/tools --libexecdir=/tools/lib \
--with-local-prefix=/tools --disable-nls \
--enable-shared --enable-languages=c
参数说明:
--disable-shared 该参数强制 GCC 链接其内部的静态库,这样做是为了避免和宿主系统产生问题;
--enable-languages=c 此参数确保只建立 C 编译器,因为现在只需要这一种编译器。
由于上面命令过长,因此用连接符"\"进行连接,在输入时也可直接全部输入,注意参数与参数之间的空格。
make bootstrap 编译GCC编译器;
命令说明:
该命令一般在编译编译器时用。bootstrap不只是编译GCC,而要连着编译多次,它第一次用主机的GCC进行编译,第二次用刚编译好的GCC编译,
然后再用编译好的GCC编译,然后会毕较第二次和第二次的结果以确保它能正确无误的生成它自己。
make install 安装GCC;
ln -vs gcc /tools/bin/cc 第一遍编译GCC时,创建了符号链接,是把/tools/bin/cc 链接到 /tools/bin/cc/gcc;
cd ..
rm -rf gcc-build gcc-4.1.2 删除gcc编译目录和解压的源文件。
注意:这里不要图省事而不删gcc目录,因为这样可能会给后面的编译产生一些意外的错误。
3.3 安装 Linux API Headers (5分钟)
这里必须得说明一下,也是笔者遇到的一个很大的困惑:
LFS6.3以前的版本是使用linux-libc-headers-2.6.11.2.tar.bz2,但此版本LiveCD没有此文件,而是使用内核包linux-2.6.22.5.tar.bz2,通过处理Linux内核源程序tar包中的各种C头文件而实现应用程序接口 (API)获取,以供系统C库(在LFS中是Glibc)使用。
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 拷贝目标目录中的所有原文件到/tools/include;
cd ..
rm -rf linux-2.6.22.5 删除解压的内核目录。
3.4 安装 Glibc (30分钟)
tar xvf /lfs-sources/glibc-2.5.1.tar.bz2 解压;
mkdir 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
参数说明:
--disable-profile 关掉了分析(profiling)信息相关的库文件编译;
--enable-add-ons 告诉 Glibc 使用附加的 NPTL 包作为它的线程库;
--enable-kernel=2.6.0 告诉Glibc 编译支持2.6.0版和更新版Linux 内核的库;
--with-headers=/tools/include 指示Glibc 按照前面刚刚安装到tools 目录中的内核头文件编译自己,
从而准确的知道内核的特性以根据这些特性对自己进行最佳化编译。
make 编译;
mkdir /tools/etc 创建etc目录;
touch /tools/etc/ld.so.conf 创建ld.so.conf文件,因为touch创建不存在的文件,避免编译安装出现警告;
make install 安装;
cd ..
rm -rf glibc-build glibc-2.5.1 删除glibc编译和解压目录。
3.5 调整并测试工具链 (10分钟)
mv -v /tools/bin/{ld,ld-old} 备份/tools/bin/ld文件;
mv -v /tools/$(gcc -dumpmachine)/bin/{ld,ld-old} 同样备份ld文件;
mv -v /tools/bin/{ld-new,ld} 更新ld文件;
ln -sv /tools/bin/ld /tools/$(gcc -dumpmachine)/bin/ld 创建新的ld连接;
以上4步操作结果如下图:
现在已经安装好了临时C库,接下来本章中要编译的所有工具都应该连接到这些库上。为了达到这个目标,需要调整交叉编译器的 specs 文件,以便指向 /tools
目录中的新的动态链接器。
将编译器的 “specs” 文件转存到一个位置,在那里可以按默认值找到它。然后,一个简单的sed替代工具更改GCC 将用到的动态链接器。这里的原则是,在/lib 中找到所有相关的动态链接器文件,并调整它们,以便指向新的位置 /tools。
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
工具链的的调整方法有好几种,而且不同版本GCC的specs可能会有不同,但实际上都是把specs文件中的/lib/ld-linux.so.2替换成了/tools/lib/ld-linux.so.2,也可直接用gcc -dumpspecs导出后手工直接编辑specs文件。这里就是采用的这种方法。
确认新工具链的基本功能(编译和连接)是否按预期工作,运行下面的命令做一个简单的合理性检查:
echo 'main(){}' > dummy.c
cc dummy.c
readelf -l a.out | grep 'tools'
查看a.out的依赖库,输出大致如下
[Requesting program interpreter: /tools/lib/ld-linux.so.2]
则表示调整成功,因为所有的库已经连接到了/tools/lib下。
rm -rf a.out dummy.c
下面介绍笔者在此过程中所遇到的一些问题,以供参考:
1.编译GCC编译器 makebootstrap 时报错:
mkdir -p --/usr/local/lib/gcc/i686-pc-linux-gnu/3.4.3
mkdir: cannot create directory`/usr/local/lib/gcc': Permission denied
make[1]: *** [installdirs] Error 1
make[1]: Leaving directory`/mnt/lfs/sources/gcc-build/gcc'
make: *** [install-gcc] Error 2
原因出在GCC参数配置错误。
2.
--------------------------------------------------------------------------------------
2013年最后一天,写完跨年,依旧坚持细节,需更严谨。
2013-12-31