LinuxSir.cn,穿越时空的Linuxsir!

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

LFS工具链构建的几个问题

[复制链接]
发表于 2006-3-2 20:43:08 | 显示全部楼层 |阅读模式
昨天刚成功编译出LFS6.0,不过在理清它的工具链构建的思路的过程中,我碰到几个问题,都是关于ld和ld-linux.so.2分别是如何寻找需要的库的,其中前者是在连接的时候由gcc调用,后者是在运行的时候被调用。

已知在指定LIB_PATH=/tools/lib编译binutils的情况下,ld会且只会去/tools/lib找库文件,然后把程序中的函数名和在/tools/lib下找到的库文件中此函数对应的偏移编入可执行文件中,另外它也会把用到的库文件名编入可执行文件(但只是文件名,没有路径,唯一的例外是ld-linux.so.2这个库文件在可执行文件中指定了绝对路径)。问题一:如果不指定LIB_PATH参数,那么它会去哪里找库文件?有没有默认值,或者配置文件?

到了程序执行的阶段,如果是动态连接,那么遇到一个可执行文件包含的外部函数符号,它会让包含在elf文件头的指定了绝对路径的ld-linux.so.2去查找库文件载入内存。已知它的默认搜索路径是/lib和/usr/lib(见第6章glibc节),并且会按照/etc/ld.so.conf的内容来查找。问题二:它是先按照/etc/ld.so.conf来查,还是先查/lib和/usr/lib,没有再按照/etc/ld.so.conf来查?照LFS book的意思,似乎是后者,因为book说Two directories that are commonly known to contain ADDITIONAL libraries are /usr/local/lib and /opt/lib, so add those directories to the dynamic loader's search path.如果是这样,那么第一次编译glibc后出来的ld-linux.so.2就是不对的,因为我们是想要让它到/tools/lib下去找,而按照默认,它先去/lib和/usr/lib找了,这是怎么回事?如果是--prefix=/tools和--prefix=/usr(分别是两次configure的参数)导致它的默认路径改变,那好像也不对,因为若指定--prefix=/usr,那它去/usr/lib找很正常,但是它怎么知道要到/lib去找呢?/lib在/usr的外面啊。

问题三:连接和运行中的动态连接是两个独立的过程,他们分别调用ld和ld-linux.so.2,由于ld把函数在库文件中的偏移编入了可执行文件,运行的时候连接的必须是同一个库文件才行,不然偏移不一样,肯定出错。但是ld和ld-linux.so.2分别有各自的查找库文件的方法,那是否意味着在任何正确的系统里(并且至少有两份版本不同的库文件副本,在不同的目录下),它们的搜索次序必须严格一致?
发表于 2006-3-2 20:56:57 | 显示全部楼层
思考的很深入啊,有些问题也没怎么想过,不过既然你提出来了就分析一下吧.
问题一:没有指定LIB_PATH,我想你的意思是想问在编译binutils的时候没有指定LIB_PATH会导致编译出来的ld到哪里去找库,我想可能/lib:/usr/lib就是默认的路径.

问题二:应该是先找LIB_PATH再到ld.so.conf中的路径去找,第一次编译glibc的时候/tools/lib中还没有任何库文件,所以当然要到/lib和/usr/lib中去找,而当编译安装完glibc后/tools/lib就有东西了,这时候再编译任何程序都应该到/tools/lib中,所以也就是在安装完glibc后就立刻调整工具链的原因,调整完后在编译任何程序都是到/tools/lib中去找了,不再需要/lib和/usr/lib了,这里没有任何问题.
     至于在指定--prefix=/usr会到/lib中找,那是因为LIB_PATH中有/lib这个路径.

问题三:虽然我没怎么研究过编译原理,但我觉得应该不是把偏移编译到可执行文件中的,至少不会是绝对偏移,所以后面的问题也就不存在了.

以上是我个人分析,不一定正确.
回复 支持 反对

使用道具 举报

发表于 2006-3-2 20:59:41 | 显示全部楼层
2.先按照/etc/ld.so.conf 找 ,这个我敢肯定.
安装 fglrx 的 libGL.so 时,就是通过让 fglrx 的 libGL.so 所在目录在 ld.so.conf 之前,来让程序调用 fglrx 的 libGL.so 而不是 /usr/lib 里 mesa 的 libGL.so .

3.
运行的时候连接的必须是同一个库文件才行,不然偏移不一样,肯定出错

这句话不对吧,就在 lfs 时,我经常把 host 的 工具直接 cp 到 lfs 里,原来的链接库和lfs里的肯定不同,但只要有版本相同的so,几乎所有的软件,都运行的非常好,只有极少数,core dump .

你也可以试验,把 host 的 一个 elf 拷贝到 lfs 里执行.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-2 21:38:35 | 显示全部楼层
Post by youbest
问题二:应该是先找LIB_PATH再到ld.so.conf中的路径去找,第一次编译glibc的时候/tools/lib中还没有任何库文件,所以当然要到/lib和/usr/lib中去找,而当编译安装完glibc后/tools/lib就有东西了,这时候再编译任何程序都应该到/tools/lib中,所以也就是在安装完glibc后就立刻调整工具链的原因,调整完后在编译任何程序都是到/tools/lib中去找了,不再需要/lib和/usr/lib了,这里没有任何问题.
     至于在指定--prefix=/usr会到/lib中找,那是因为LIB_PATH中有/lib这个路径.


但LIB_PATH只是在configure binutils时指定,在编译glibc时并没有指定,而ld-linux.so.2是编译glibc后得到的,所以ld-linux.so.2应该不知道LIB_PATH。安装完glibc后调整工具链调整的是让ld在连接的时候去指定的路径查找库和编译时用新的ld-linux.so.2这两项,这些都是运行前的工作,但并没有调整让ld-linux.so.2在运行时去/tools/lib找库,所以似乎ld-linux.so.2还是去/lib和/usr/lib找库,这明显和我们的希望不符。而且这时ld.so.conf还是空的。说到底,新的ld-linux.so2是从哪里得知要去/tools/lib,而不是/lib,/usr/lib找库的呢?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2006-3-2 21:48:13 | 显示全部楼层
Post by dyhml
3.
这句话不对吧,就在 lfs 时,我经常把 host 的 工具直接 cp 到 lfs 里,原来的链接库和lfs里的肯定不同,但只要有版本相同的so,几乎所有的软件,都运行的非常好,只有极少数,core dump .

你也可以试验,把 host 的 一个 elf 拷贝到 lfs 里执行.


那有没有可能就是那些少数的工具用到的函数在库中的偏移正好不同?或者就是我的关于gcc编译时连接和运行时连接的实现方法的假设错了,而这正是我想知道的。这其中最关键的我想是ld-linux.so.2的工作原理,只要知道了这个,其他的应该都能解释了。
回复 支持 反对

使用道具 举报

发表于 2006-3-2 22:58:54 | 显示全部楼层
Post by vanhu
那有没有可能就是那些少数的工具用到的函数在库中的偏移正好不同?或者就是我的关于gcc编译时连接和运行时连接的实现方法的假设错了,而这正是我想知道的。这其中最关键的我想是ld-linux.so.2的工作原理,只要知道了这个,其他的应该都能解释了。



我觉得,动态链接的话,肯定不能用偏移来作为寻找代码的方式,因为不可能每个系统上库上相同函数的偏移都一样,这样所有的软件都必须下源码编译,预编译之类的二进制文件肯定没法用的吧……

动态链接的函数定位应该是通过函数的名称来完成的吧,看下下面的代码:
  1. #include <dlfcn.h>
  2. #include <stdio.h>

  3. main()
  4. {
  5.         void *libc;
  6.         void (*printf_call)();
  7.         char* error_text;

  8.         if(libc=dlopen("/lib/libc.so.5",RTLD_LAZY))
  9.         {
  10.                 printf_call=dlsym(libc,"printf");
  11.                 (*printf_call)("hello, world\n");
  12.                 dlclose(libc);
  13.                 return 0;
  14.         }
  15.        
  16.         error_text= dlerror();
  17.         printf(error_test);
  18.         return -2;
  19. }
复制代码


先把动态链接库映射到内存,再通过dlsym,以“printf”为参数找到函数在内存中的地址,之后调用,我想和win下的LoadLibrary然后再GetProcAddress的道理是一样的~上面这段代码是显式的调用动态链接库,不这样的话,编译器应该做类似的处理,运行的时候和上面应该是差不多的……

动态链接库里面应该有symbol section这一节的,通过symbol可以找到函数地址,运行时内部的运作好像很复杂的说,我也不是很清楚,貌似是这样汗……
回复 支持 反对

使用道具 举报

发表于 2006-3-2 23:27:57 | 显示全部楼层
运行的时候连接的必须是同一个库文件才行,不然偏移不一样,肯定出错

如果是这样,非本distro 的预编译软件都会不能用,商业软件就没法跑了.

Realplayer 和 AdobeReader 都是预编译的.
回复 支持 反对

使用道具 举报

发表于 2006-3-3 08:31:43 | 显示全部楼层
问题真深。;) 。你说的,我基本都看过了,也稍微研究了一下。

Post by vanhu

已知在指定LIB_PATH=/tools/lib编译binutils的情况下,ld会且只会去/tools/lib找库文件,然后把程序中的函数名和在/tools/lib下找到的库文件中此函数对应的偏移编入可执行文件中,另外它也会把用到的库文件名编入可执行文件(但只是文件名,没有路径,唯一的例外是ld-linux.so.2这个库文件在可执行文件中指定了绝对路径)。问题一:如果不指定LIB_PATH参数,那么它会去哪里找库文件?有没有默认值,或者配置文件?


不指定的话,是去 /lib:/usr/lib 这2个目录找。
配置文件是 ld.so.conf,在 glibc 安装的时候包括。

到了程序执行的阶段,如果是动态连接,那么遇到一个可执行文件包含的外部函数符号,它会让包含在elf文件头的指定了绝对路径的ld-linux.so.2去查找库文件载入内存。已知它的默认搜索路径是/lib和/usr/lib(见第6章glibc节),并且会按照/etc/ld.so.conf的内容来查找。
问题二:它是先按照/etc/ld.so.conf来查,还是先查/lib和/usr/lib,没有再按照/etc/ld.so.conf来查?照LFS book的意思,似乎是后者,因为book说Two directories that are commonly known to contain ADDITIONAL libraries are /usr/local/lib and /opt/lib, so add those directories to the dynamic loader's search path.如果是这样,那么第一次编译glibc后出来的ld-linux.so.2就是不对的,因为我们是想要让它到/tools/lib下去找,而按照默认,它先去/lib和/usr/lib找了,这是怎么回事?如果是--prefix=/tools和--prefix=/usr(分别是两次configure的参数)导致它的默认路径改变,那好像也不对,因为若指定--prefix=/usr,那它去/usr/lib找很正常,但是它怎么知道要到/lib去找呢?/lib在/usr的外面啊。

这个是写在配置文件里边的,不是--prefix 指定的。

默认的话,就是 先找 ld.so.conf,再找 /lib:/usr/lib。

指定了 LIB_PATH 的话,就是先找 ld.so.conf,再找 LIB_PATH。就是把 /lib:/usr/lib 用 LIB_PATH 替换了。

问题三:连接和运行中的动态连接是两个独立的过程,他们分别调用ld和ld-linux.so.2,由于ld把函数在库文件中的偏移编入了可执行文件,运行的时候连接的必须是同一个库文件才行,不然偏移不一样,肯定出错。但是ld和ld-linux.so.2分别有各自的查找库文件的方法,那是否意味着在任何正确的系统里(并且至少有两份版本不同的库文件副本,在不同的目录下),它们的搜索次序必须严格一致?


我不懂那么深奥的偏移编入这些概念,我的理解只是把文件名记录,至于文件的本身是什么,似乎并不在乎,只要有一个文件在那里做相应工作就可以了。

后边的这个问题,我不明白。你说清楚一点。
回复 支持 反对

使用道具 举报

发表于 2006-3-3 13:37:23 | 显示全部楼层
这个几个问题,偶曾经深刻的思考过,也发过相关的帖子。
说的简单点,就是静态链接器和动态链接器的工作机制。
答案还不确定,但我自己算是满意了:)
1。静态链接器ld
LFS有详细提到:
ld --verbose | grep SEARCH 查看搜索路径。
至于如何修改其搜索路径,一直没有定论。参考LFS的binutils安装过程,每次ld为了改变默认的搜索路径都重新make -C ld LIB_PATH=/tools/lib一次。判定这个路径是锁定到程序ld内部了。验证的方法很简单,拷贝ld到另一个host上。如果有配置文件,就没有必要再编译ld了!
然而还有一个奇怪的文件ldscript,这个文件里面有ld的搜索路径,他会不会是ld的配置文件呢?有可能是,但又有好多ld没有这个文件。
综合考虑,ld的默认搜索路径存在上面两种方式,当然都是可以在机上验证。了解了,知道如何寻找答案我就满足了,答案是什么对我来说并不重要。所以最后验证一步一直没做:)

2。动态链接器ld-linux.so.2
通过ldd可以查看程序动态链接库的位置。这里一般都是绝对路径定位的。动态链接器负责装入所用的库。至于ldd的时候有没有使用ld-linux.so.2的搜索路径,不清楚。
ld-linux-so.2有自己的默认,也要使用/etc/ld.so.conf。谁先谁后其实问题不大,肯定是确定的。程序内使用的不会是绝对地址的,根据的是符号规则。所以编译的时候用的库可以和运行时用的不一样,只要可以找到那个符号就可以运行。

3。lib本身的问题
这个你没有提到。不过你有没有想过,一个系统上怎么会同时出现两个一摸一样的库呢?在所有的搜索路径中,都不会存在提供同一个功能的库。也就是说ld在链接时,文件系统里面只有一个.so与之对应!不然不是画蛇添足嘛?出于版本考虑,老版本也不应该和新版本并存!作为研究,可以随便考虑,真正的应用,这些都不是问题:)所以我没有去验证他们
回复 支持 反对

使用道具 举报

发表于 2006-3-3 14:06:18 | 显示全部楼层
3。可以出现两个一模一样的库的,拷贝一下就好了。所以才会产生谁先谁后的问题。。同一功能的库,更是存在,而且是最重要的库:libc-2.3.X.so。你装一个发行版(我装的是 RHEL4和参考了Gentoo)看看就知道了。
新老版本的共存,的确不应该,不过一般保证连接的那个文件是新的,就基本没问题。当然,头文件也要对应,不然会出现编译错误的。比如 openssl 0.9.7 和 0.9.8 共存的时候。我就弄过。

建议兄弟看看 LFS-SVN,里边的方法已经改过了,更直观。尤其是 ld 的这个文件的处理方式。
回复 支持 反对

使用道具 举报

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

本版积分规则

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