工具链解析

LFS/CLFS工具链是一套用于从C/C++源代码生成可执行文件的软件组件适当地组合在一起形成的系统。它包括4大部分,缺一不可:

1、一套头文件,包含了这些源代码所需要访问的系统接口。

2、binutils,包含一些处理二进制可执行文件所需的工具,如汇编器、连接器等等。

3、gcc,包含了编译C/C++源代码所需的工具,并且还能自动调用相关的binutils工具来完成生成源代码的工具

4、glibc,包含了系统接口的具体实现。

在上面的定义中,请注意这些软件必须适当地组合,才能形成完整的工具链。那么怎么才算适当组合呢?当然,这个工具链必须能够发挥作用,也就是确实能够编译出目标代码,然后才能算适当组合。

那么,一个适当组合的工具链应当具有什么特点呢?一般我们把工具链运行的机器称为host,所产生的代码称为target。那么对于工具链的要求便是:它必须要能在host上运行,所产生的代码必须能在target上运行。再进一步分析:

1、头文件:必须是针对target的,因为使用这个工具链编译的源代码需要访问的系统接口是trget上的。

2、binutils:必须能在host上运行,然而产生target的代码。

3、gcc:和binutils一样。不过这里要注意的是,gcc自身带有一个库,称为libgcc,它必须是target上的代码。

4、glibc:必须是target上的代码。

有了这些基本认识,让我们来看一看一些实际的工具链构造过程。

LFS工具链构造顺序:binutils p1->gcc p1->头文件->glibc->adjust->gcc p2->binutils p2

其中最后两步应该可以互换,但头两步不行。如果你有留意编译过程,会发现,当gcc编译的时候,一旦内部编译第一次gcc完成,产生了xgcc文件,随后的编译就会利用新编译的gcc以及第一遍的binutils,而不是宿主的gcc和binutils。

正如youbest指出的,第一遍的binutils和gcc存在的意义,仅仅是为了能编译出glibc。而glibc的编译需要工具链所有其它内容,包括binutils,gcc和头文件。

LFS不使用交叉编译,因此host和target永远是一样的。在编译参数中,我们永远看不到–host和–target的身影。但是,由于工具链的引用路径需要反复变化,因此我们需要通过修改specs来更改。这点是LFS比较容易出错的地方。

CLFS顾名思义,是要采用交叉编译的。其构造顺序如下:

头文件->cross binutils->cross-gcc c->glibc->gcc final (c/c++)

我们首先看到头文件被放在了开头。这不是必须的,它完全可以放在glibc之前。cross-gcc只能编译出C编译器,因为这时候工具链还不全,glibc还不存在,不可能编译出C++编译器。然后构建glibc。之后,我们才能编译出C++编译器,完成我们的工具链。

CLFS-SYSROOT的工具链稍有不同,顺序如下:

头文件->cross-binutils->glibc头文件->cross-gcc c->gibc->gcc-final (c/c++)

我们看到这里多了一个glibc头文件的安装。事实上,这是由于这里编译的gcc是要用sysroot的缘故。在gcc/configure里面有一段代码,大家在vi里面输入/inhibit_libc=false就可以发现,在交叉编译,而没有SYSROOT的情况下,inhibit_libc的值会成为true,而要是sysroot了,就false了。

这个变量的作用,如同有关的注释所述,是用来关闭gcc对glibc头文件的依赖。因此,在sysroot下我们才会需要这样一个安装glibc头文件的步骤。

我想在sysroot下消除这个步骤,之前测试过这个补丁可行:

使用这个sed命令打个补丁即可:

cp gcc/configure{,.orig}

sed -e ‘s/inhibit_libc=false/inhibit_libc=true/g’ gcc/configure.orig > gcc/configure

不过最近发现有更简单的方法:在configure的时候加个参数–with-newlib即可。建议采用这个方法,因为不需要打任何补丁。

在此再说一句,工具链构建过程中,编译脚本对于是否交叉编译的判断非常简单,只要host!=target,就会被认为正在交叉编译。这也就是为什么CLFS中使用的$CLFS_HOST通常都是i686-cross-linux-gnu这样的形式。原因很简单,因为要确保$CLFS_HOST与$CLFS_TARGET不同。这样,你完全可以在i686上”交叉编译”i686的代码。

这点非常重要,这意味着我们不需要打任何补丁,即可利用CLFS和CLFS-SYSROOT代替LFS来完成系统。对于想做Multilib的朋友,这也是一个福音。毕竟,CLFS-SYSROOT比起CLFS和LFS来,节省了大量的编译过程。

最后,我们来理解一下如何作CCLFS工具链。这意味着我们要在一个平台上构建在第二个平台上运行、生成第三个平台上代码的工具链。我们用build表示构建工具链的机器,host表示工具链要运行的机器,target表示工具链生成的代码所在机器。而在工具链组件上,host-target binutils表示该binutils在host上运行,要生成target的代码。

步骤如下:

host 头文件->build-host binutils->build-host gcc c->host glibc->target 头文件->build-target binutils->build-target gcc c->target glibc->host-target binutils->host-target gcc final (c/c++)

注意事项如下。首先,头文件必须在相应的glibc之前安装。其次,build-target的binutils和gcc在这里的目的是生成target glibc。而build-host binutils 还会被构建host-target binutils和gcc的过程使用到。最后,前两次的gcc都可以只生成c编译器,因为他们的目的都只是生成glibc。只有最后一次gcc需要完整生成。

By Lu Jun

80后男,就职于软件行业。习于F*** GFW。人生48%时间陪同电子设备和互联网,美剧迷,高清视频狂热者,游戏菜鸟,长期谷粉,临时果粉,略知摄影。

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.