LinuxSir.cn,穿越时空的Linuxsir!

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

工具链构造彻底剖析

[复制链接]
发表于 2008-3-24 20:03:45 | 显示全部楼层 |阅读模式
尽管是老生常谈,但是在比较之前,还是先明确一下什么叫做工具链。

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需要完整生成。

暂时也没想到别的可写的了,就到这。大家有疑问的我尽量解答。
发表于 2008-3-24 21:58:07 | 显示全部楼层
大哥写的不错
回复 支持 反对

使用道具 举报

发表于 2008-3-25 08:48:57 | 显示全部楼层
挺一下发动机
回复 支持 反对

使用道具 举报

发表于 2008-3-25 10:49:14 | 显示全部楼层
写的好。赞一个。思路清晰。
最后一个 CC 的我没试过,以前琢磨过,可是后来没去研究了,太懒了。hoho。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-3-26 19:30:04 | 显示全部楼层
Post by 晨想;1830604
写的好。赞一个。思路清晰。
最后一个 CC 的我没试过,以前琢磨过,可是后来没去研究了,太懒了。hoho。


我就是发现你们之前的讨论没到点子上。要生成一个交叉编译器,host和target的glibc一个都不能少。这正是为什么CC工具链需要三步而不是两步的原因。
回复 支持 反对

使用道具 举报

发表于 2008-3-27 09:43:07 | 显示全部楼层
很好,很强大
回复 支持 反对

使用道具 举报

发表于 2008-3-27 17:23:04 | 显示全部楼层
关于CLFS想问个问题!
建立工具链后,编译的application运行在低版本的linux上会有问题吗?

因为编译gcc时用了linux的头文件和glibc。
而编译目标板的application用的库文件也肯定是工具链中用到的,这个如果和目标板上的Linux版本不同会怎样呢?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-3-27 18:37:40 | 显示全部楼层
Post by foo2008;1831661
关于CLFS想问个问题!
建立工具链后,编译的application运行在低版本的linux上会有问题吗?

因为编译gcc时用了linux的头文件和glibc。
而编译目标板的application用的库文件也肯定是工具链中用到的,这个如果和目标板上的Linux版本不同会怎样呢?


你这个可能有问题。如果你能确定目标版本最好在同一版本上编译CLFS。不过一般来说glibc用到的内核头文件并不会有太大变化,glibc应该没有问题。应用程序大多数通过glibc调用内核,也没什么。但是util,module-tools,udev等组件和内核关系密切,估计版本不同是有问题的。我没试过。

此外,要更正你的一点是,视编译参数的不同,gcc的编译可能需要glibc的头文件(可以参见主贴,介绍了如何关闭这个依赖性的方法),但绝对不会需要内核头文件。
回复 支持 反对

使用道具 举报

发表于 2008-3-27 19:29:25 | 显示全部楼层
楼主所说的CCLFS叫Canadian cross compilation ,具体定义在这里:  http://www.airs.com/ian/configure/configure_6.html
和这里解释了为什么叫这么怪异的名字:http://www.mewburn.net/luke/papers/build.sh.pdf
一个可以做Canadian cross compilation 的工具crosstool http://kegel.com/crosstool/current/doc/crosstool-howto.html
-----------------------------
Canadian cross compilation 小贴示
The sources of a compiler are compiled (i.e. built) on machine X
X is called as the Build system
• The built compiler runs on machine Y
Y is called as the Host system
• The compiler compiles code for target Z
Z is called as the Target system
• Note: The built compiler itself runs on the Host machine and
generates executables that run on Target machine!!!
The built compiler itself runs on the Host machine and
generates executables that run on Target machine!!!
A few interesting permutations of X, Y and Z are:
X = Y = Z Native build
X = Y != Z Cross compiler
X != Y != Z Canadian Cross compiler
回复 支持 反对

使用道具 举报

发表于 2008-3-27 19:35:49 | 显示全部楼层
比如你可以在x86_64的机器上(build)编译以Cygwin为主系统(host)而以i386为目标(target)的系统。
回复 支持 反对

使用道具 举报

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

本版积分规则

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