LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 23975|回复: 59

[原创]CLFS2.0原理分析

[复制链接]
发表于 2006-8-12 18:20:17 | 显示全部楼层 |阅读模式
更新日志
2006年9月28日:修改错字一处。
2006年8月30日:修改错误命令一处。
2006年8月22日:修改文字中的笔误两处。
2006年8月18日:修改图片中的笔误两处。

前言:
  实际上,两三个星期前我第一次看CLFS2.0的时候有一种似曾相似的感觉,而且感觉看的非常顺,所以我只看到工具链完成后又看了两三个包的编译我就感觉想明白原理了,因为CLFS2.0的制作的方法我曾经自己尝试过,当时想研究出一种和LFS方法不同的方式来构建系统,虽然当时也做出来了一些成果,但感觉不成功(最重要的就是当时我不知道有--with-sysroot这个参数),但也有不少收获,而且我认为我当时的想法没有错,这可以在我以前的两篇文章《体积小巧、功能单一的LFS》和《LFS反向分析》中看到这个方法的影子,而后续包制作也和我在做憨牛包管理器中的打包方式方法类似,所以我感觉CLFS2.0方法不光适合用在交叉编译上,同样在非交叉的情况下也是适用的。
下面我就将我对CLFS2.0方法的理解总结一下,希望对CLFS感兴趣的朋友有所帮助,同时附加上我根据CLFS2.0的原理制作的PowerPC和MIPS的工具链部分,但因能力和理解力的限制,难免会有差错,也希望各位发现问题后能及时提出,以便及时更改,谢谢。


  更新,由于篇幅比较长所以难免出现一些错误或者笔误,也有可能加入新内容,因此难免会进行修正或增删一些内容,如果本文被转载或者发现文章的更新日期已经比较久了,可能已经有更新,您可以在www.linuxsir.cn的LFS版中或者在本人的Blog中查看最新版本。
  linuxsir:http://www.linuxsir.cn/bbs/showthread.php?t=267672
  我的Blog:http://youbest.cublog.cn
  如须转载请注明作者为冲天飞豹(youbest),并提供转载出处。

好的开始:
  设置好环境变量对于后面的编译参数变的通用,也便于输入方便不容易出错
  这里需要注意的是在
cat > ~/.bashrc << "EOF"
set +h
umask 022
CLFS=/mnt/clfs
LC_ALL=POSIX
PATH=${CLFS}/cross-tools/bin:/bin:/usr/bin
export CLFS LC_ALL PATH
EOF
里的PATH设置,这个PATH设置一直到制作结束也没有变化,这不同于LFS在第五章和第六章使用不同的PATH,这个问题之后在文章中会回答。

另一个重要设置就是
export CLFS_HOST="$(echo $MACHTYPE | sed "s/$(echo $MACHTYPE | cut -d- -f2)/cross/")"
export CLFS_TARGET="arm-unknown-linux-gnu"
这个设置对于不同的体系平台会不一样,在文章的附录中有另外两个体系平台的定义。

  一个说明:我们暂时把CLFS2.0中第五章的部分称为交叉工具链部分,而把第六章称为目标体系平台

CLFS2.0中的绝招sysroot参数
  sysroot参数一共出现了也就出现了三次,但这三次的使用可以说完全改变了以前的制作完整工具链的方法。
  sysroot的目的其实很简单:就是将--with-sysroot所指定的目录作为根目录来使用。
  我们会在文章中看到这三次sysroot是如何使用来达到目的的。


分析开始:
  首先开始复制一些头文件到${CLFS}中的usr/include中,这个步骤似乎和以前没什么区别,但实际上我个人觉得这里少了一步,应该在复制前增加一句
  1. make include/linux/version.h
复制代码

  接着我们就要安装binutils,这个包的目的就是为了连接目标文件为可执行文件,那么在第一次编译binutils的时候我们就要开始用--with-sysroot这个“神奇”的参数了。
../binutils-2.17/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --with-sysroot=${CLFS} \
--disable-nls --enable-shared --disable-multilib
  从参数上的理解是,binutls将被安装到${CLFS}/cross-tools中(--prefix=${CLFS}/cross-tools),编译出来的二进制文件将运行在${CLFS_HOST}所指定的平台上(--host=${CLFS_HOST}),而其连接出的可执行文件是运行在${CLFS_TARGET}所指定的平台上(--target=${CLFS_TARGET}),屏蔽国际化支持(--disable-nls),同时编译出共享链接库(--enable-shared),不支持多种架构库(--disable-multilib),同时将${CLFS}所定义的目录作为根目录(--with-sysroot)
  接着的make configure-host不要少了,这样可以利用前面设置的参数全面配置binutils所有需要编译的部分。
  编译这个binutils是使用主系统的binutils和gcc来实现的,主系统是i386结构的,因此,这次我们编译出的这个binutils是利用主系统这个工具链来完成编译的,编译出来的程序也是运行在i386上的

下面我们图解这个部分


  由上图我们可以看出,实际上binutils编译过程中是没有用到目标体系的内核头文件的,因此复制内核的目标体系头文件和编译binutils的顺序是无所谓的。


  接着我们就要准备开始编译GCC(不要以为我这里漏掉了glibc-2.4的headers这部分,这样安排正是为了说明glibc-2.4的headers部分为什么要在编译GCC之前处理),我们来看一下它的编译参数
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --disable-shared \
--enable-languages=c
  从参数上理解就是,gcc将被安装到${CLFS}/cross-tools,而且只编译c语言部分,同时将${CLFS}所定义的目录作为根目录(--with-sysroot)
  这里有必要解释一下一个比较重要的概念就是“交叉编译”,交叉编译简单的讲就是在一种体系的机器上编译出运行于另一种体系的代码(两种体系也可以是相同的,但通常是以不同的体系来说明),如下图:


  这里很重要的一个部分就是编译器和链接器,这二者在这里对应的是GCC和binutils,它们即要运行于当前体系的系统下,又要能生成和处理目标体系的文件,因此称它们为交叉编译工具链。
  这里比较重要的是GCC(并不是binutils不重要,只是我们重点要说明的是GCC),那么GCC是如何进行编译的呢?
  这个问题要说清楚的话可以写一本书了,这里我只是从CLFS的角度来说明这个问题。GCC要编译出其它体系的代码,除了需要要编译的程序的源代码外还需要符合该体系的头文件,这里最重要的就是内核的头文件和glibc的头文件,所以作为交叉编译用的GCC,除了在编译其它程序的时候用到头文件,其自身也必须“了解”这些头文件,它必须掌握目标体系的全部细节,这已经由Linux的内核头文件给出了,另一点需要注意的是,GCC不仅可以用来在Linux下用,也可以用在其它支持的系统上,比如BSD,而C库也不局限于glibc,有可能是其它的C库,比如uclibc,所以要能够生成在目标体系的目标系统下运行的二进制代码,那么就必须对这一切都要了解,所以在编译GCC的时候必须有目标体系及目标系统相对应的头文件,这就解释了为什么要在编译GCC之前要先编译一个glibc-2.4的头文件部分。
  下面来图解这段内容



  现在我们回过头来,编译glibc-2.4符合目标系统的头文件,这部分我感觉也是各个体系差异比较大的部分,所以我们这里参考CLFS2.0第一个完成的体系ARM来说明(我只说重点,全部内容可以参考手册说明)
tar -jxvf ../glibc-ports-2.4.tar.bz2
mv -v glibc-ports-2.4 ports
这部分实际上是为了glibc能够支持arm而安装的,标准的glibc并不支持ARM等几个体系,而glibc-ports就是为了补充这部分的支持而出现的(这里必须将其改为ports,否则编译无法找到)。
echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache
echo "libc_cv_arm_tls=yes" >> config.cache
这部分实际上是强制指定一些编译时候的参数,因为目前还没有交叉编译用的GCC,所以用主系统做./configure的时候有些参数会设置一些不正确的参数,因此这里强制声明是非常必要的,到工具链中第一遍的GCC完成后就可以正确设置这些参数了,所以再编译glibc就可以不用指定这些参数。
echo "install_root=${CLFS}" > configparms
这个就是为了指定安装目录,方便以后用make install直接装到${CLFS}目录下,其实不要这句也没问题,后面的make install改为make install_root=${CLFS} install-headers就可以了。
CC=gcc ../glibc-2.4/configure --prefix=/usr \
--host=${CLFS_TARGET} --build=${CLFS_HOST} \
--with-headers=${CLFS}/usr/include --cache-file=config.cache
这个编译参数说明gcc用的是主系统的gcc,这里需要注意的是--prefix=/usr虽然指定的是/usr目录,但实际上最后安装到的是${install_root}/usr下,如果没有指定install_root,那自然是安装到了/usr,而因为前面指定了install_root的目录,实际上最后安装到了${CLFS}/usr下,这里还需要注意的是--host和--build的指定(--host=${CLFS_TARGET} --build=${CLFS_HOST}),对比前面编译的binutils的参数(--host=${CLFS_HOST} --target=${CLFS_TARGET}),和后面GCC的参数(--host=${CLFS_HOST} --target=${CLFS_TARGET}),看到没有:

glibc-2.4 :--host=${CLFS_TARGET} --build=${CLFS_HOST}
binutils :--host=${CLFS_HOST} --target=${CLFS_TARGET}
gcc  :--host=${CLFS_HOST} --target=${CLFS_TARGET}

这里--host的指定刚好binutils和gcc相反,指定为目标体系,这点很重要,这是为了让后面即将编译的GCC能够准确的了解其要编译出的符合目标系统的二进制所需要的信息。
--with-headers指定了使用的头文件的目录,glibc唯一必须要的头文件就是内核的头文件,这点很有意义,因为glibc也是可以支持多种内核平台的,比如BSD,所以它也必须了解所服务的内核的所有特征细节,因此就不难理解为什么内核头文件必须先于glibc的头文件安装,只有这样glibc才能“了解”到准确的内核信息。
--cache-file则没什么好说的,就是让./configure对于--cache-file指定的文件中设置的参数强制使用。
make install-headers
到这里,我们还不需要一个完整的glibc,其实也无法进行编译的,因为目前的交叉编译用的GCC还没有,所以是无法编译的,但编译一个交叉编译的GCC又必须要一组C库的头文件,好在安装目标平台的glibc的头文件并不需要交叉编译器,所以这里直接安装头文件即可。
cp -v ../glibc-2.4/ports/sysdeps/unix/sysv/linux/arm/nptl/bits/pthreadtypes.h \
${CLFS}/usr/include/bits
这个是为了安装支持NPTL的头文件,这部分根据不同的平台复制的文件是不一样的,文章的附录中将介绍PowerPC和MIPS两种体系需要复制的文件,可作为参考。
如果你前面没有仔细看,我提个醒,上面提到的glibc-headers部分和GCC第一次编译部分是反过来讲的,因此和书上没有冲突。

  好了,到目前为止已经有了一个交叉编译用的GCC和一个交叉链接用的binutils,以及一组目标平台和目标系统用的库文件,现在就可以正式的开始编译Linux平台的基础部分——C库(glibc)
  在编译glibc的时候,同样指定了两个参数
  echo "libc_cv_forced_unwind=yes" > config.cache
  echo "libc_cv_c_cleanup=yes" >> config.cache
  这两个参数于体系无关,而第三个参数
  echo "libc_cv_arm_tls=yes" >> config.cache
  则省略了,因为交叉编译环境已经完全了解你要编译的目标体系,所以可以自行检测出来。

  这里着重需要明白的是glibc的编译参数
BUILD_CC="gcc" CC="${CLFS_TARGET}-gcc" \
AR="${CLFS_TARGET}-ar" RANLIB="${CLFS_TARGET}-ranlib" \
../glibc-2.4/configure --prefix=/usr --libexecdir=/usr/lib/glibc \
--host=${CLFS_TARGET} --build=${CLFS_HOST} \
--disable-profile --enable-add-ons \
--with-tls --enable-kernel=2.6.0 --with-__thread \
--with-binutils=${CLFS}/cross-tools/bin --with-headers=${CLFS}/usr/include \
--cache-file=config.cache
  BUILD_CC所指定的是用来建立在编译过程中需要运行的程序用什么gcc来编译,这个很重要,可以看出BUILD_CC指定的是主系统的gcc,主系统的gcc是一个运行于i386,编译出i386体系的编译器,因此它编译出来的程序可以在当前系统下运行,因为在编译过程中会编译一些临时使用的程序,而这些程序是编译完后就马上要用的,所以这些程序必须能在当前体系的平台上运行,因此必须以一个运行于当前体系平台,又编译出当前体系平台的编译器来完成,所以主系统的gcc自然成了最合适的选择。而CC所指定的是${CLFS_TARGET}-gcc,很明显是交叉编译用的GCC,原因很简单,最后编译出来的glibc的二进制库都是要在目标体系平台上运行的。AR和RANLIB是binutils中的工具,它们也是在编译过程中需要用到的,而且是用来处理目标平台的二进制文件,所以它们也是使用交叉编译用的版本。
  --prefix和--libexecdir已经在前面编译glibc-headers部分说明过了,不要被表面现象所“迷惑”,最后安装到的是${install_root}/usr和${install_root}/usr/lib/glibc下。
  --host指定也表明这里最后编译出来的库是用在目标体系平台下的。
  --with-binutils则是为了表明在需要使用binutils的时候使用交叉版本所在的目录(这里不会影响BUILD_CC编译的临时程序,因为交叉版本在交叉工具链阶段是有前缀的)。
  --with-headers和--cache-file解释同glibc-headers部分。
  其余参数于LFS中含义相同,这里就不做解释了。
  后面的make和make install自然没什么好说明的,只是这里因为前面设置了install_root的原因,在make install阶段会安装到install_root下。
  下面按道理应该要安装locales了,但这里运行make localedata/install-locales并不能正确完成,原因很简单localedef是目标体系平台的程序,所以无法在本阶段完成,这里需要和前面说明的BUILD_CC编译的是可以在当前体系平台运行的临时程序分开看,因为localedef并不是一个临时用的文件,而是glibc的标准程序,所以这里也可以算是一个未解决的问题。我的建议是暂时跳过安装localedata部分
  在完成了glibc的编译和设置后,我们要进行第二次编译交叉版本的GCC了。
这里我想说明一下为什么要进行两次GCC编译的目的,这里和我们已经熟知的LFS中工具链里的两次GCC编译有着不同的原理。这一部分也算是CLFS2.0中的重点部分,我将以我的理解来说明这个问题:
  在LFS里,工具链中第一遍编译gcc,是为了编译工具链中的glibc而编译的,而且为了能够保证编译器的正确则使用了make bootstrap来编译,在编译完glibc后,则再编译一遍gcc,目的是为了让这个gcc使用刚刚在工具链中编译好的glibc,为了是在后面完成工具链后chroot时保证工具链的可用,并且可以用来编译目标系统的glibc和LFS其他需要的软件。
  而在CLFS2.0中,第一遍编译gcc,也确实为了编译glibc,而这个glibc却不是工具链中要用的,这是目标系统用的(这一点也可以通过第六章中没有编译glibc的部分来间接验证),并不是为了第二次编译gcc,让gcc链接到这个glibc用的,而且gcc也不能链接到这个glibc上,因为第一遍编译的交叉版本的gcc来编译出的glibc必然是目标体系平台的代码,所以在当前的体系平台上是运行不起来的,如果第二次编译的gcc链接到这个glibc上,那么这个gcc也就不能在当前的体系平台上运行了,所以CLFS2.0在工具链阶段的两次编译gcc,不是为了让gcc连接到新编译的glibc上。
  那么究竟是什么原因呢?
  我们先来看CLFS2.0第一编译GCC,没有使用make bootstrap来编译,而是使用make all-gcc来编译,也就是只编译了一次,这是合情合理的,要知道这个gcc是交叉版本,也就是说它再编译出来的是目标体系平台的二进制文件,虽然可以完成make bootstrap的第二步,但第三步是无法进行的,因为目标体系平台无法在当前平台上运行(make bootstrap,就是用第一遍编译的gcc来编译第二遍的gcc,再用第二遍的gcc编译第三遍的gcc,然后比较第二遍和第三遍的gcc,来确定编译是否正确),因此这里只需要也只能编译一遍。
  在LFS里,第一遍gcc只编译了c语言部分,是因为编译glibc只需要c语言就行了,你当然也可以编译其它的语言支持,但没有什么意义,因为第一遍的gcc会被第二遍替换掉,而且第一遍是依赖于主系统glibc的,所以在chroot后就不能用了。
  在CLFS2.0里没有chroot的过程,所以无论是工具链中的第一遍还是第二遍编译,gcc所依赖的glibc都是主系统的glibc,但对后面的编译并不造成影响。
  那为什么要编译两次呢?
  CLFS2.0的工具链中第一遍只编译一个支持c的gcc,原因是要编译出一个支持交叉的c++,必须有一个编译好的用于目标体系平台的glibc,而不是只有glibc的头文件就可以的,好在编译glibc有c支持就够了,所以编译glibc也成了第一遍的gcc唯一的理由和作用。
  在LFS中,工具链里第二遍的gcc是由第一遍的gcc来完成编译的。
  在CLFS2.0中,我们知道第一遍编译的gcc就是交叉版本的gcc,如果由它来编译第二次的gcc,那么编译出来的就是目标体系平台的二进制文件,是无法在当前体系平台上运行的,而我们还要用第二次编译的交叉版本的gcc来编译后面的内容,所以绝对不能用第一遍的gcc来编译第二遍的gcc。
  那么是谁来编译第二次的gcc呢?
  现在看这个问题应该是有点废话,目前就只剩下主系统的gcc了,也只有主系统的gcc现在能编译出在当前体系平台运行的交叉版本的gcc了。
  现在明白了吧,工具链中gcc的第一次和第二次编译都是由主系统的gcc和binutils来完成的(之前没有提及binutils,只是为了理解方便,但实际上编译后是少不了链接过程的,这个过程是要binutils来完成的)。

  到目前为止只有在编译glibc的时候用到了交叉版本的binutils,其它部分的链接都是由主系统的binutils来完成的。
  现在对工具链中gcc的两次编译的目的和原因差不多搞清楚了,我们来看一下gcc两次编译参数的对比
第一次:
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --disable-shared \
--enable-languages=c
第二次:
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --enable-shared \
--enable-languages=c,c++ --enable-__cxa_atexit \
--enable-c99 --enable-long-long --enable-threads=posix
  看来没什么特别需要说明的,非交叉编译用的参数基本上和LFS没什么太大区别,反正最重要的就是这个--with-sysroot,好了,三次--with-sysroot都出现了,虽然前面对这个参数也说明了一下,但不够详细,下面我就来说说对这个关键参数的理解
  我们在做LFS的过程中了解到gcc在编译过程中是默认从/usr/include中找头文件的来编译的,而binutils中的工具ld是从/lib /usr/lib、LD_LIBRARY_PATH、/etc/ld.so.conf等设置中所指定的路径搜索动态库或者静态库进行链接操作的,而要改变这种默认情况则可以通过参数指定、打补丁等方式来达到目的,但是这样非常烦琐,所以就产生了LFS中工具链的方法,通过先做一个能自我编译的工具链,但这些工具链中的程序都是连接到类似/tools/lib这样的目录下的库中,而且也是通过参数指定或者打补丁的方式来实现的,并不符合标准的/lib /usr/lib,所以后面在chroot后再用这个工具链来生成目标系统。
  而现在我们用CLFS2.0的方法不需要再建立这个完整的工具链了,只是建立一个交叉用的工具链,而这个工具链中的程序都是链接到/lib和/usr/lib里的库的,这样一个工具链是不能chroot,但现在我们要利用这个“不健全”的工具链来完成目标体系平台,就必须要用到更改默认路径的方式,这个方式就是--with-sysroot。
  一个简单的理解就是,默认的路径实际上都是{--with-sysroot}/usr {--with-sysroot}/usr/lib {--with-sysroot}/usr/include这样的形式,只是在默认的情况下{--with-sysroot}表示的是空字符串,这样就变成了/usr /usr/lib /usr/include,而如果我们指定了--with-sysroot,比如--with-sysroot=/mnt/clfs,则默认路径就变成了/mnt/clfs/usr /mnt/clfs/usr/lib /mnt/clfs/usr/include,这样我们在编译时查找头文件以及在链接时查找动态或静态库就自动到--with-sysroot指定的路径下来完成。这个就是--with-sysroot参数的目的。
  这里需要注意的是,--with-sysroot默认是只支持交叉编译的情况的,我们可以从代码中印证
  在gcc-core解压后的Makefile.in文件(你可以理解为是Makefile的一个摸板文件)中有一段代码

  1. # Default native SYSTEM_HEADER_DIR, to be overridden by targets.
  2. NATIVE_SYSTEM_HEADER_DIR = /usr/include
  3. # Default cross SYSTEM_HEADER_DIR, to be overridden by targets.
  4. CROSS_SYSTEM_HEADER_DIR = @CROSS_SYSTEM_HEADER_DIR@
复制代码

而对于CROSS_SYSTEM_HEADER_DIR的赋值在
configure中有如下代码

  1. CROSS_SYSTEM_HEADER_DIR='$(TARGET_SYSTEM_ROOT)$(NATIVE_SYSTEM_HEADER_DIR)'
复制代码

而同在configure中对TARGET_SYSTEM_ROOT的赋值

  1. TARGET_SYSTEM_ROOT=$with_sysroot
复制代码

  现在明白了吧,对于交叉方式,是默认支持--with-sysroot的,而普通的编译方式是不行的,但也不是说我们就没办法了,其实办法说起来也很简单,就是改代码、打补丁。

  我们来看三次使用--with-sysroot的作用和目的
  第一次,binutils下使用,目的是让binutils在查找库的时候到--with-sysroot指定的地方查,接着的glibc-headers和gcc都没有用到这个binutils,我们先放一下,看第二次使用;
  第二次,第一次编译gcc下使用,目的是让这个gcc在编译的时候默认到{--with-sysroot}/usr/include下找头文件。
  接着我们就开始编译目标体系平台下的glibc了,这个时候交叉版本的binutils和第一次编译的gcc都用上了,则我们也就清楚了,在编译这个glibc的时候是到${CLFS}/usr/include里找头文件,到${CLFS}/lib等目录下链接库的,不过glibc是目标系统的第一个软件包,因此,他并不需要到${CLFS}/lib等目录下的库链接,但交叉版本的binutils还是顺利的完成了glibc编译目录下自己众多库文件的链接工作。这里binutils的--with-sysroot没有体现出来,但gcc的--with-sysroot已经发挥作用了。
  第三次,实际上这次是为了替换掉第一次编译的gcc而“重复”的(原因前面已经讲过了),所以可以理解和第二次使用--with-sysroot是一样的。
  到现在为止,gcc中的--with-sysroot已经体现出其作用了,但binutils什么时候才能发挥作用呢?
  不要着急,很快就到了它的用武之地了。

  现在我们就完成了交叉工具链了……(画外音:等等,还有两个包没说呢,怎么工具链就完成了?)
  这里我们先把--with-sysroot的问题放下,现在出现了另外一个问题,在LFS过程中我们知道工具链除了binutils和gcc外还有很多大量的工具包,而在CLFS2.0中就只有file和groff两个包,是什么意思呢?
  这里我们要全面了解工具链的作用以及这些工具包的作用,在LFS中的工具链的目的不光是为了能编译,而且是为了能够成为一个完整的自已自足的“系统”,再进入(chroot)这个“系统”后,能够利用这种自已自足的能力创造新系统,而这个过程中,大量的工具包是少不了的,这也就是为什么LFS的方法中需要在工具链阶段里加入大量的工具包。
  而在CLFS2.0之所以没有加入大量的包是因为,CLFS2.0的方法里没有chroot这个环节,所以使用主系统的工具就可以了,因此只需要gcc和binutils就可以完成任务了,而file和groff其实我觉得也是没有必要的,只要主系统中的file和groff是符合要求的版本就可以了,如果没有符合的版本编译一个也是可以的,这里要注意的是file和groff是用主系统的gcc和binuitls完成编译链接的并依赖于主系统的glibc

  好了,下面我们就要开始编译目标系统的文件了。
  之前我们已经在第五章的部分完成了glibc的编译,所以我们就可以直接编译各个包了,从CLFS2.0的手册来看,似乎还是遵循着LFS的先编译binutils和gcc,然后再编译其它的包,但这里又是和LFS的方法有着本质上的区别,下面我们就来对照一下看看。
  在LFS中,我们在完成了工具链后,利用工具链来编译目标系统的glibc,之后是编译binutils,然后利用刚刚编译的binutils和工具链中的gcc来编译目标系统中的gcc,之后就利用刚刚编译的binutils和gcc来编译后面的部分,而之后每编译一个包,在后面的编译过程中需要用到这个包就是用刚刚编译好的,而不是工具链中的,直到把工具链中的所有包都替换成目标系统中的包,工具链就算完成目的了。比如在编译sed包之前,使用的是工具链中的sed命令,而当sed包编译完成之后,再用sed命令的时候,就是用刚刚编译好的目标系统中的sed命令了。
  而在CLFS2.0中,我们知道现在用的是交叉版本的工具链,所以编译出来的是目标体系平台的代码,那么是不能在当前的体系平台下运行的,因此,也不会出现LFS中的替换过程,所以在CLFS2.0的方法中,还以sed命令为例,在交叉工具链编译目标体系平台的sed包前用的是主系统的sed命令,而编译完sed后,再用sed命令时,依然用的还是主系统的sed命令,因为刚刚编译的sed根本就不能在当前体系平台上运行。
  现在我们就清楚了,我们也得出了几个结论:
  1、第六章编译的包都是运行于目标体系平台的,所以不可以在当前体系平台上运行。
  2、在CLFS2.0中第六章部分的binutils和gcc不是必须编译的,除非你需要在这个系统完成后移植到目标体系的机器中运行并要继续编译其它程序才需要编译binutils和gcc(这点和我以前写的《体积小巧、功能单一的LFS》思路是一样的);
  3、第六章的包编译顺序是不需要这么严格的,特别是只有命令的包,是可以随便摆放顺序的,比如coreutils这个包,在手册中是位于gcc之后就编译了,但实际上即使放在最后一个包编译也没问题,因为这个包编译出来的命令根本就在这里用不上,部分命令如果觉得在目标系统中根本不用,即使不编译都可以,比如你不打算在目标体系平台中用patch命令,你就可以不安装这个命令,这是不会影响编译效果的。
  4、不是所有的包都可以随意改变编译顺序的,对于有动态或者静态库的文件,如果有其它的包需要,那么就必须先于依赖于它的包编译,比如ncurses和readline,因为readline要链接ncurses的库,所以ncurses必须比readline先编译,否则会导致编译问题。
  5、在整个第六章的过程中都是由主系统中的工具包和交叉编译工具链来完成编译的,这个时候binutils和gcc的--with-sysroot发挥着重要的作用(binutils中的--with-sysroot终于开始用上了),因为这个参数,使得整个第六章的编译过程在没有路径补丁的情况下变的相对“顺利”起来。

关于系统的“纯净度”
  说到这里不得不提我对这里编译过程中使用主系统的工具包来完成编译的看法,就是系统“纯净度”的问题,在LFS的方法中,利用独立的工具链来生成目标系统以保证和原系统“不相干”,而这里没有这样一个完整的工具链是否会影响它的“纯净度”呢?
  我的回答是:不影响任何“纯净度”。
  原因很简单,对纯净度的影响最主要的就是编译过程中对原系统中库的依赖,但实际上我们知道交叉编译的目的就是为了编译出运行于其它体系平台的代码,如果编译出的代码和原系统还有关系的话,这个结果就是完全失败的东西,因为有部分代码无法在目标体系平台中运行,这是非常失败的。在CLFS2.0中交叉编译用的binutils和gcc都新编译出来的,不是主系统的,因此这方面没什么问题(实际上即使主系统提供了交叉编译的工具,也没有问题。),接着就是这些主系统提供的工具包是否会带来“纯净度”的问题,我的一个观点就是这些工具只是处理诸如文本之类的于平台无关的文件,所以只要能够正确处理这类文件,就没问题,这是完全跟“纯净度”无关的因素。当然如果主系统提供的命令不适合或者版本太低,则需要象前面的提到的file和groff的方式一样——编译一个。

  好了,在讲完第六章的的几个结论后,再来谈谈第六章编译的包的安装问题,对编译过程比较清楚的朋友基本上都了解一般软件包的编译过程
./configure 若干参数
make
make install
  前两个命令没什么问题,最后一个make install,一般会将其安装到--prefix指定的目录中,而一般我们在参数中都是用--prefix=/usr这样的,这个参数是很重要的,有些包在运行过程中都可能受这个参数的影响,那么我们现在要安装到${CLFS}才行,如果用这样的--prefix=/usr参数,又用make install来安装的话,但愿你不是用root来执行的,否则你的系统就要被破坏了。这里正确的方法是用DESTDIR来指定“根”目录,如make DESTDIR=${CLFS} install,这很类似前面说明的--with-sysroot的概念,这里就不多说了,我在做憨牛包管理器的时候就用到了这个方法,很实用,但不是所有的包都支持,但大多数标准的gun包是支持的,可参考各个包的说明,也可以查看代码来确定。

  最后要说的就是关于目标系统如何启动了,在发稿时为止CLFS2.0还是开发中的版本,而它目前也只有arm和x86两中体系,x86我们知道可以用grub来做启动,而arm却没有提到,各种体系都有各自的启动系统,可以参考CLFS1.0中的相应部分。

  最后我们来尝试画一个CLFS2.0的逻辑图(图中未表达出--with-sysroot的作用,但不影响表达CLFS2.0的方法)。

  如果观察仔细的话会发现这副图里没有提到任何Linux的字样,因为我觉得这样的方法是适合其它类似的开源系统的,比如BSD。

(转载请保持文章的完整性,请注明作者和出处)

                               作者:冲天飞豹(youbest)
                               Email:youbest@sina.com
                               2006年8月12日


附录:
[color="red"]PowerPC的工具链制作过程及内核编译
环境设置过程(略)
export CLFS_HOST="$(echo $MACHTYPE | sed "s/$(echo $MACHTYPE | cut -d- -f2)/cross/")"
export CLFS_TARGET="powerpc-unknown-linux-gnu"
echo export CLFS_HOST=\""${CLFS_HOST}\"" >> ~/.bashrc
echo export CLFS_TARGET=\""${CLFS_TARGET}\"" >> ~/.bashrc

Linux-Kernel-headers
make include/linux/version.h
install -dv ${CLFS}/usr/include/{asm,asm-generic,linux,net,mtd,scsi,sound}
cp -av include/asm-generic/* ${CLFS}/usr/include/asm-generic
cp -av include/linux/* ${CLFS}/usr/include/linux
cp -av include/mtd/* ${CLFS}/usr/include/mtd
cp -av include/net/* ${CLFS}/usr/include/net
cp -av include/scsi/* ${CLFS}/usr/include/scsi
cp -av include/sound/* ${CLFS}/usr/include/sound
install -dv ${CLFS}/usr/include/asm-ppc
cp -av include/asm-powerpc/* ${CLFS}/usr/include/asm
cp -av include/asm-ppc/* ${CLFS}/usr/include/asm-ppc

binutils-2.17
patch -Np1 -i ../binutils-2.17-posix-1.patch
mkdir -v ../binutils-build
cd ../binutils-build
../binutils-2.17/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --with-sysroot=${CLFS} \
--disable-nls --enable-shared --disable-multilib
make configure-host
make
make install
cp -v ../binutils-2.17/include/libiberty.h ${CLFS}/usr/include

glibc-2.4-headers
cp configure{,.orig}
sed -e 's/3.4/3.[0-9]/g' configure.orig > configure
mkdir -v ../glibc-build
cd ../glibc-build
echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache
echo "libc_cv_powerpc32_tls=yes" >> config.cache
echo "libc_cv_ppc_machine=yes" > config.cache
echo "libc_cv_mlong_double_128=yes" >> config.cache
echo "libc_cv_mlong_double_128ibm=yes" >> config.cache
echo "install_root=${CLFS}" > configparms
CC=gcc ../glibc-2.4/configure --prefix=/usr \
--host=${CLFS_TARGET} --build=${CLFS_HOST} \
--with-headers=${CLFS}/usr/include --cache-file=config.cache
make install-headers
install -dv ${CLFS}/usr/include/bits
cp -v bits/stdio_lim.h ${CLFS}/usr/include/bits
touch ${CLFS}/usr/include/gnu/stubs.h
cp -v ../glibc-2.4/nptl/sysdeps/unix/sysv/linux/powerpc/bits/pthreadtypes.h \
${CLFS}/usr/include/bits

gcc-4.1.1第一遍
patch -Np1 -i ../gcc-4.1.1-posix-1.patch
patch -Np1 -i ../gcc-4.1.1-cross_search_paths-1.patch
mkdir -v ../gcc-build
cd ../gcc-build
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --disable-shared \
--enable-languages=c
make all-gcc
make install-gcc

glibc-2.4
patch -Np1 -i ../glibc-2.4-libgcc_eh-1.patch
patch -Np1 -i ../glibc-2.4-localedef_segfault-1.patch
patch -Np1 -i ../glibc-2.4-crosscompile_timezone_fix-1.patch
patch -Np1 -i ../glibc-2.4-iconv_fix-1.patch
mkdir -v ../glibc-build
cd ../glibc-build
echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache
echo "install_root=${CLFS}" > configparms
BUILD_CC="gcc" CC="${CLFS_TARGET}-gcc" \
AR="${CLFS_TARGET}-ar" RANLIB="${CLFS_TARGET}-ranlib" \
../glibc-2.4/configure --prefix=/usr --libexecdir=/usr/lib/glibc \
--host=${CLFS_TARGET} --build=${CLFS_HOST} \
--disable-profile --enable-add-ons \
--with-tls --enable-kernel=2.6.0 --with-__thread \
--with-binutils=${CLFS}/cross-tools/bin --with-headers=${CLFS}/usr/include \
--cache-file=config.cache
make
make install
cat > ${CLFS}/etc/nsswitch.conf << "EOF"
# Begin /etc/nsswitch.conf
passwd: files
group: files
shadow: files
hosts: files dns
networks: files
protocols: files
services: files
ethers: files
rpc: files
# End /etc/nsswitch.conf
EOF
TZDIR="${CLFS}/usr/share/zoneinfo" ${CLFS}/usr/bin/tzselect
cp -v --remove-destination ${CLFS}/usr/share/zoneinfo/[xxx] \
${CLFS}/etc/localtime
cat > ${CLFS}/etc/ld.so.conf << "EOF"
# Begin /etc/ld.so.conf
/usr/local/lib
/opt/lib
# End /etc/ld.so.conf
EOF

gcc-4.1.1第二遍
patch -Np1 -i ../gcc-4.1.1-posix-1.patch
patch -Np1 -i ../gcc-4.1.1-PR20425-1.patch
patch -Np1 -i ../gcc-4.1.1-cross_search_paths-1.patch
mkdir -v ../gcc-build
cd ../gcc-build
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --enable-shared \
--enable-languages=c,c++ --enable-__cxa_atexit \
--enable-c99 --enable-long-long --enable-threads=posix
make
make install

file-4.17
./configure --prefix=${CLFS}/cross-tools
make
make install

Groff-1.19.2
PAGE=A4 ./configure --prefix=${CLFS}/cross-tools --without-x
make
make install

工具链制作完成
目标系统制作
export CC="${CLFS_TARGET}-gcc"
export CXX="${CLFS_TARGET}-g++"
export AR="${CLFS_TARGET}-ar"
export AS="${CLFS_TARGET}-as"
export RANLIB="${CLFS_TARGET}-ranlib"
export LD="${CLFS_TARGET}-ld"
export STRIP="${CLFS_TARGET}-strip"
echo export CC=\""${CC}\"" >> ~/.bashrc
echo export CXX=\""${CXX}\"" >> ~/.bashrc
echo export AR=\""${AR}\"" >> ~/.bashrc
echo export AS=\""${AS}\"" >> ~/.bashrc
echo export RANLIB=\""${RANLIB}\"" >> ~/.bashrc
echo export LD=\""${LD}\"" >> ~/.bashrc
echo export STRIP=\""${STRIP}\"" >> ~/.bashrc

Linux-2.6.17.6
make ARCH=powerpc CROSS_COMPILE=${CLFS_TARGET}- menuconfig
make ARCH=powerpc CROSS_COMPILE=${CLFS_TARGET}-
make ARCH=powerpc CROSS_COMPILE=${CLFS_TARGET}- \
INSTALL_MOD_PATH=${CLFS} modules_install
cp vmlinux ${CLFS}/boot/clfskernel-2.6.17.6
cp System.map ${CLFS}/boot/System.map-2.6.17.6
cp .config ${CLFS}/boot/config-2.6.17.6


[color="Red"]MIPS的工具链制作过程及内核编译
环境设置过程(略)
export CLFS_HOST="$(echo $MACHTYPE | sed "s/$(echo $MACHTYPE | cut -d- -f2)/cross/")"
export CLFS_TARGET="mips-unknown-linux-gnu"
echo export CLFS_HOST=\""${CLFS_HOST}\"" >> ~/.bashrc
echo export CLFS_TARGET=\""${CLFS_TARGET}\"" >> ~/.bashrc

Linux-Kernel-headers
make include/linux/version.h
install -dv ${CLFS}/usr/include/{asm,asm-generic,linux,net,mtd,scsi,sound}
cp -av include/asm-generic/* ${CLFS}/usr/include/asm-generic
cp -av include/linux/* ${CLFS}/usr/include/linux
cp -av include/mtd/* ${CLFS}/usr/include/mtd
cp -av include/net/* ${CLFS}/usr/include/net
cp -av include/scsi/* ${CLFS}/usr/include/scsi
cp -av include/sound/* ${CLFS}/usr/include/sound
cp -av include/asm-mips/* ${CLFS}/usr/include/asm

binutils-2.17
patch -Np1 -i ../binutils-2.17-posix-1.patch
mkdir -v ../binutils-build
cd ../binutils-build
../binutils-2.17/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --with-sysroot=${CLFS} \
--disable-nls --enable-shared --disable-multilib
make configure-host
make
make install
cp -v ../binutils-2.17/include/libiberty.h ${CLFS}/usr/include


glibc-2.4-headers
cp configure{,.orig}
sed -e 's/3.4/3.[0-9]/g' configure.orig > configure
tar -jxvf ../glibc-ports-2.4.tar.bz2
mv -v glibc-ports-2.4 ports
echo "" > ports/sysdeps/mips/mips32/Makefile
sed -i ‘s/wordsize.h>$/wordsize.h>\n#define __WORDSIZE 32/g’ bits/types.h
mkdir -v ../glibc-build
cd ../glibc-build
echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache
echo "libc_cv_mips_tls=yes" >> config.cache
echo "install_root=${CLFS}" > configparms
CC=gcc ../glibc-2.4/configure --prefix=/usr \
--host=${CLFS_TARGET} --build=${CLFS_HOST} \
--with-headers=${CLFS}/usr/include --cache-file=config.cache
make install-headers
install -dv ${CLFS}/usr/include/bits
cp -v bits/stdio_lim.h ${CLFS}/usr/include/bits
touch ${CLFS}/usr/include/gnu/stubs.h
cp -v ../glibc-2.4/ports/sysdeps/unix/sysv/linux/mips/nptl/bits/pthreadtypes.h \
${CLFS}/usr/include/bits

gcc-4.1.1第一遍
patch -Np1 -i ../gcc-4.1.1-posix-1.patch
patch -Np1 -i ../gcc-4.1.1-cross_search_paths-1.patch
mkdir -v ../gcc-build
cd ../gcc-build
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --disable-shared \
--enable-languages=c
make all-gcc
make install-gcc

glibc-2.4
patch -Np1 -i ../glibc-2.4-libgcc_eh-1.patch
patch -Np1 -i ../glibc-2.4-localedef_segfault-1.patch
patch -Np1 -i ../glibc-2.4-crosscompile_timezone_fix-1.patch
patch -Np1 -i ../glibc-2.4-iconv_fix-1.patch
ln -s nptl/sysdeps/unix/sysv/linux/i386 ports/sysdeps/unix/
mkdir -v ../glibc-build
cd ../glibc-build
echo "libc_cv_forced_unwind=yes" > config.cache
echo "libc_cv_c_cleanup=yes" >> config.cache
echo "install_root=${CLFS}" > configparms
BUILD_CC="gcc" CC="${CLFS_TARGET}-gcc" \
AR="${CLFS_TARGET}-ar" RANLIB="${CLFS_TARGET}-ranlib" \
../glibc-2.4/configure --prefix=/usr --libexecdir=/usr/lib/glibc \
--host=${CLFS_TARGET} --build=${CLFS_HOST} \
--disable-profile --enable-add-ons \
--with-tls --enable-kernel=2.6.0 --with-__thread \
--with-binutils=${CLFS}/cross-tools/bin --with-headers=${CLFS}/usr/include \
--cache-file=config.cache
make
make install

cat > ${CLFS}/etc/nsswitch.conf << "EOF"
# Begin /etc/nsswitch.conf
passwd: files
group: files
shadow: files
hosts: files dns
networks: files
protocols: files
services: files
ethers: files
rpc: files
# End /etc/nsswitch.conf
EOF
TZDIR="${CLFS}/usr/share/zoneinfo" ${CLFS}/usr/bin/tzselect
cp -v --remove-destination ${CLFS}/usr/share/zoneinfo/[xxx] \
${CLFS}/etc/localtime
cat > ${CLFS}/etc/ld.so.conf << "EOF"
# Begin /etc/ld.so.conf
/usr/local/lib
/opt/lib
# End /etc/ld.so.conf
EOF

gcc-4.1.1第二遍
patch -Np1 -i ../gcc-4.1.1-posix-1.patch
patch -Np1 -i ../gcc-4.1.1-PR20425-1.patch
patch -Np1 -i ../gcc-4.1.1-cross_search_paths-1.patch
mkdir -v ../gcc-build
cd ../gcc-build
../gcc-4.1.1/configure --prefix=${CLFS}/cross-tools \
--host=${CLFS_HOST} --target=${CLFS_TARGET} --disable-multilib \
--with-sysroot=${CLFS} --disable-nls --enable-shared \
--enable-languages=c,c++ --enable-__cxa_atexit \
--enable-c99 --enable-long-long --enable-threads=posix
make
make install

file-4.17
./configure --prefix=${CLFS}/cross-tools
make
make install

Groff-1.19.2
PAGE=A4 ./configure --prefix=${CLFS}/cross-tools --without-x
make
make install

工具链制作完成
目标系统制作
export CC="${CLFS_TARGET}-gcc"
export CXX="${CLFS_TARGET}-g++"
export AR="${CLFS_TARGET}-ar"
export AS="${CLFS_TARGET}-as"
export RANLIB="${CLFS_TARGET}-ranlib"
export LD="${CLFS_TARGET}-ld"
export STRIP="${CLFS_TARGET}-strip"
echo export CC=\""${CC}\"" >> ~/.bashrc
echo export CXX=\""${CXX}\"" >> ~/.bashrc
echo export AR=\""${AR}\"" >> ~/.bashrc
echo export AS=\""${AS}\"" >> ~/.bashrc
echo export RANLIB=\""${RANLIB}\"" >> ~/.bashrc
echo export LD=\""${LD}\"" >> ~/.bashrc
echo export STRIP=\""${STRIP}\"" >> ~/.bashrc

Linux-2.6.17.6
cp arch/mips/configs/qemu_defconfig .config
make ARCH=mips CROSS_COMPILE=${CLFS_TARGET}- menuconfig
make ARCH=mips CROSS_COMPILE=${CLFS_TARGET}-
make ARCH=mips CROSS_COMPILE=${CLFS_TARGET}- \
INSTALL_MOD_PATH=${CLFS} modules_install
cp vmlinux ${CLFS}/boot/clfskernel-2.6.17.6
cp System.map ${CLFS}/boot/System.map-2.6.17.6
cp .config ${CLFS}/boot/config-2.6.17.6

(转载请保持文章的完整性,请注明作者和出处)

                               作者:冲天飞豹(youbest)
                               Email:youbest@sina.com
                               2006年8月12日

[color="Red"]更新日志:
2006年8月18日:
修改了图片中的笔误两处,将MIPS写成了IMPS,已更正。
由cublog上的flw10000报告。

2006年8月22日:
修改了文字中的笔误两处,将MIPS写成了IMPS,已更正。

2006年8月30日:
修改错误命令
cp -av include/asm-mips/* /usr/include/asm

cp -av include/asm-mips/* ${CLFS}/usr/include/asm
由cublog上的flw10000发现并报告

2006年9月28日:
修改错字一处
“有这不同的原理”修正为“有着不同的原理”
由cublog上的bee_slayer发现并报告
发表于 2006-8-12 19:03:15 | 显示全部楼层
顶,youbest 好文。

我的理解,就是完全的 Cross-Compile 了?
回复 支持 反对

使用道具 举报

发表于 2006-8-12 19:12:36 | 显示全部楼层
还在我理解范围之外.........
回复 支持 反对

使用道具 举报

发表于 2006-8-12 21:24:45 | 显示全部楼层
又想起那句经典:youbest的帖子就是精华贴的代名词!
先顶了再看!
回复 支持 反对

使用道具 举报

发表于 2006-8-12 21:27:48 | 显示全部楼层
Post by csfrank
又想起那句经典:youbest的帖子就是精华贴的代名词!
先顶了再看!

偶的专利,,哼哼,,,:)。。。

终于见到了大作了,太好了。。。仔细看看先。。。。
回复 支持 反对

使用道具 举报

发表于 2006-8-12 23:38:14 | 显示全部楼层
:eek:
先顶~

然后慢慢学习~
回复 支持 反对

使用道具 举报

发表于 2006-8-12 23:55:14 | 显示全部楼层
吓死了:eek:
回复 支持 反对

使用道具 举报

发表于 2006-8-14 12:36:19 | 显示全部楼层
我小菜鸟还不知道cross的意义
回复 支持 反对

使用道具 举报

发表于 2006-8-16 17:40:54 | 显示全部楼层
又見 youbest 大作,吾自愧不如,學習學習。。。
Post by 终极幻想
偶的专利,,哼哼,,,:)。。。

终于见到了大作了,太好了。。。仔细看看先。。。。
堅決打倒一切無理的 patent!。。。:p
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-8-16 22:52:18 | 显示全部楼层
Post by d00m3d
又見 youbest 大作,吾自愧不如,學習學習。。。

我很羡慕d00m3d对Linux系统知识的广泛,俺找工作现在就怕人家问在Linux如何用某个工具做一件什么事情,用什么命令或者工具可以完成某个任务什么的,保证是一问三不知。
要向d00m3d学习,问什么基本都能回答个一二三来,这才叫精通呢,吾自愧不如,學習學習。。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表