LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
楼主: youbest

[原创]CLFS2.0原理分析

[复制链接]
发表于 2008-7-22 12:01:51 | 显示全部楼层
好吧,我们再深入一层,我们略去第二遍gcc的c++部分。

循环依赖,形象的说就是:鸡生蛋蛋生鸡的问题,是进化的问题,也就是自举的问题。

宿主是大环境,
工具链源代码是生成鸡和蛋的原始材料,
glibc-header,这个是蛋,最原始的蛋,
gcc第一遍,这个是鸡,最原始的鸡,
glibc,这个是第一代鸡下的蛋,第一个正常的蛋,其中包含一份完整、正常的glibc-header,
gcc第二遍,这个是第二代鸡,第一个正常的鸡,
之后,如果你原意,可以继续生成第二代鸡下的蛋,和第三代的鸡,不过他们已经是正常的鸡、蛋,与第一个正常的蛋、第一个正常的鸡没有区别,这也是工具链为什么只需编译gcc两遍、glibc一遍的真正原因。

如果在正常安装的基础上,再利用fakeroot方式单独安装每一步的结果到不同目录用做比较,自然会明白为什么不应拿第一遍gcc编译除glibc、linux-kernel一类自给自足的软件包。

用fakeroot方式比较,你会发现最早安装的glibc-header与安装glibc时再次安装的header稍有区别,这种区别对gcc的影响主要在libgcc和crtXXX.o部分,会影响gcc行为,这也是为什么我强调[color="Red"]能运行、能正常运行本质区别的真正原因。
回复 支持 反对

使用道具 举报

发表于 2008-7-22 12:38:22 | 显示全部楼层
第一次的交叉gcc只支持C,但可以用来编译glibc,这个基本上所有其它程序的基础库,因此如果想编译其它的程序就必须先将glibc完成,那么第二次的交叉gcc需要用到了C++,而C++用到了glibc库,所以C++的支持必须在glibc完成之后,但这里你要注意一点,第二次的交叉gcc并不是第一次的交叉所编译产生的,而是由主系统的gcc来完成的,但第二次的gcc的C++支持确是由第二次生成的gcc在编译过程中生成的。


多谢老大,明白,完全明白,我也是这么想的,只是不知道对不对,还有就是一些细节不是太清楚,呵呵,现在终于了了解了。

太感谢了!
回复 支持 反对

使用道具 举报

发表于 2008-7-22 12:40:15 | 显示全部楼层
用fakeroot方式比较,你会发现最早安装的glibc-header与安装glibc时再次安装的header稍有区别,这种区别对gcc的影响主要在libgcc和crtXXX.o部分,会影响gcc行为,这也是为什么我强调能运行、能正常运行本质区别的真正原因。


学习了,怪不得您一直这么说了,呵呵,如果早点这么说的话,我也就早明白了,呵呵。

多谢!!
回复 支持 反对

使用道具 举报

发表于 2008-7-22 18:21:05 | 显示全部楼层
Post by 聚焦深空;1877238
好吧,我们再深入一层,我们略去第二遍gcc的c++部分。

循环依赖,形象的说就是:鸡生蛋蛋生鸡的问题,是进化的问题,也就是自举的问题。

宿主是大环境,
工具链源代码是生成鸡和蛋的原始材料,
glibc-header,这个是蛋,最原始的蛋,
gcc第一遍,这个是鸡,最原始的鸡,
glibc,这个是第一代鸡下的蛋,第一个正常的蛋,其中包含一份完整、正常的glibc-header,
gcc第二遍,这个是第二代鸡,第一个正常的鸡,
之后,如果你原意,可以继续生成第二代鸡下的蛋,和第三代的鸡,不过他们已经是正常的鸡、蛋,与第一个正常的蛋、第一个正常的鸡没有区别,这也是工具链为什么只需编译gcc两遍、glibc一遍的真正原因。

如果在正常安装的基础上,再利用fakeroot方式单独安装每一步的结果到不同目录用做比较,自然会明白为什么不应拿第一遍gcc编译除glibc、linux-kernel一类自给自足的软件包。

用fakeroot方式比较,你会发现最早安装的glibc-header与安装glibc时再次安装的header稍有区别,这种区别对gcc的影响主要在libgcc和crtXXX.o部分,会影响gcc行为,这也是为什么我强调[color="Red"]能运行、能正常运行本质区别的真正原因。


有一点小补充:GCC编译第二遍的时候,并不是仅仅用到Glibc头文件就完了,还确确实实用到了Glibc的二进制代码。

在某种意义上,第一次编译的GCC是不完全的,你可以试试在安装第一遍GCC后
echo "main(){}" > dummy,c && gcc dummy.c
这是会出错的。因为一个关键的工具链组件还不存在:启动代码crt?.o。这些代码其实包括了程序初始化进入main函数之前和退出main函数之后结束所需的步骤。

这些代码在编译Glibc的时候并不需要,因为Glibc自己就能提供这些。但在编译GCC的时候,除非是使用第一遍所用的“不完全”参数,否则它还是需要启动代码。
回复 支持 反对

使用道具 举报

发表于 2009-5-19 10:03:29 | 显示全部楼层
Post by panly;1876319
感谢回答!!

确实,我对工具链的源码结构现在不太了解,我想我的答案肯定在这里面,而工具链自举这个概念我也只是听说过而已,如果我完全弄明白了,肯定也就成高手了,但我现在没有那么多时间,暂时不想那么深入下去,所以只是想求证一下,然后明白结论就行了,但我知道,以后肯定我还是会去弄明白这些的,这些是绕不过去的。

youbest 的解释对我们的理解确实有很大帮助,可以说我从他那儿学了不少,你说是表层原因说的很对。

你前面解释的那些,确实是为什么整个工具链为什么要这么操作的根本原因,就好像我们的计算机是冯氏结构一样,编译、汇编、链接、加载,头文件和链接库,动态链接和静态链接,这些才是这所有操作的最最决定性的东西。

说实话,我对编译器、链接器和汇编器的工作原理还是比较清楚的,对 elf、exe 文件的结构也都分析过,链接、重定位我也研究过,就是有了这些了解,所以我才能容易的理解交叉工具链的实质。

你说的实践和理论结合,我承认,我保证我上面的做法就是《构建嵌入式Linux系统》(英文名《Building Embedded Linux》这本经典教材中介绍的做法,这也是很多嵌入式相关的书籍上介绍的办法,然后我又结合了 CLFS 的一些理论依据,最后完成的整个工具链,并且我一直在使用这个环境,交叉编译了内核、gcc、glibc、uclibc、busybox 等一大堆的程序,都能够很好的在目标板上运行,并且整个目标板上的系统就是我用这个环境建立起来的。

所以我不认为我的做法是偷懒,如果有区别,那我认为是 CLFS 和嵌入式开发的区别,我构建工具链的过程对 CLFS 有借鉴,但绝对不是完全拿过来用的,我使用的是它的思想,它提供的工具。

前面 youbest 也提过,用第一遍的 Gcc 编译出的 glibc 完全是目标板系统的,所以我用交叉编译器(第一遍的 gcc)来编译代码时,只要让它链接这个目标板 glibc,那么它肯定是在目标板上运行的。我想不出我这么做有什么地方不对,我的目的并不完全的做 clfs,而是建立交叉工具链,然后交叉编译 kernel、apps,最后让 kernel 和 apps 在目标板上跑起来,并且这一切都是我通过反复的实验验证和深入思考得到的结果。

我记得 youbest 的那个 5M 系统就提到过类似的做法,这不是偷懒,我觉得这更能体现出一个人思考的深度,根据实际的情况,我们要做一些调整,不能一些死守着那些步骤和理论上的解释,登山千条道,同仰一月高,但哪条道最近、最安全、最快捷、最好走才是重要的。

真的很感谢你的回复,让我思维更加活跃,对这些的理解也更加深入,作为一个技术人员,交流是提高的一个重要手段。


我顶你,中国最缺少的是思考的人,而不是缺乏一些拿别人东西来学的人。
回复 支持 反对

使用道具 举报

发表于 2010-4-14 10:46:06 | 显示全部楼层
“在CLFS2.0中交叉编译用的binutils和gcc都新编译出来的,不是主系统的”

“在CLFS2.0中交叉编译用的binutils和gcc重新编译出来的,不是主系统的”
回复 支持 反对

使用道具 举报

发表于 2010-4-14 10:46:58 | 显示全部楼层
“在CLFS2.0中交叉编译用的binutils和gcc都新编译出来的,不是主系统的”

“在CLFS2.0中交叉编译用的binutils和gcc重新编译出来的,不是主系统的”
回复 支持 反对

使用道具 举报

发表于 2010-4-20 15:51:36 | 显示全部楼层
如果没有高人的指导,clfs的原理真的有点比较难理解,尤其是gcc为什么要编译第二遍的问题,看完主贴以及后面的跟贴,终于豁然开朗了,谢谢youbest以及ls各位前辈的深入讨论。
回复 支持 反对

使用道具 举报

发表于 2010-5-31 00:36:51 | 显示全部楼层
谢谢!!!!!!!!!!!!!!!!!!!!!1
回复 支持 反对

使用道具 举报

发表于 2011-12-14 14:39:32 | 显示全部楼层
额...刚开始看LFS,一看这帖子都头晕了...
回复 支持 反对

使用道具 举报

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

本版积分规则

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