SharedLibrary(so)的查找路径

来源:互联网 发布:算法的含义 编辑:程序博客网 时间:2024/05/21 17:35
翻译自:https://www.eyrie.org/~eagle/notes/rpath.html : Shared Library Search Paths

现在将所有的依赖都使用共享库(Shared Library)的方式来连接是一件越来越普遍的事情。事实上很多软件安装包(脑中闪现的如Tcl,Cyrus SASL)如果采用静态连接的话,基本上不能正确的工作。这意味着软件使用者会经常处理如果在执行期查找合适的依赖库问题。

下面是在Linux和Solaris中查找依赖库是如何工作的简单的总结。依赖的查找路径来自三个方面:环境变量中的LD_LIBRARY_PATH(如果设置了),任何一个编码到二进制程序中的rpath(后面会详述);系统的默认搜索路径。查找按前面所述的顺序进行,并且使用最先查找到的依赖库。

LD_LIBRARY_PATH已经被滥用了(is broken),如果可能,请尽量不要使用。因为它的修改不仅会影响你想让它生效的二进制,也会影响所有使用它的二进制程序。而且对于其它的和LD_LIBRARY_PATH同样作用的设置也会被覆盖(doesn't add easiliy)。它有可能破坏就的链接,推荐仅在没有可选方法的商业产品(例如Oracle)中使用(最好在指定的应用程序环境的包装器(wrapper)中设置相应的路径,不要在一般的shell环境中设置)。

接下来,我们详细解释下其它两种查找机制。

  • 系统默认路径
处理动态依赖库最不好的方式是:将每一个你安装的动态依赖库都放入系统默认路径。仅在极少数情况下(你安装了多个冲突的库)这种方法会失效。如果你只是把软件安装到/usr/local/lib下,那就仅把/usr/local/lib放到你的系统默认搜索路径。

在Linux上,将安装的路径添加到/etc/ld.so中,然后执行ldconfig;在Solaris上,通过执行crle命令实现相同的目的(具体请参考man手册)。

然后这种方式并不是万能的。主要的反例是:共享的库安装在一个企业或者集群使用的共享的网络文件系统中。这时, 你不会想把网络文件系统加入你的系统默认搜索路径,因为搜索路径会被系统上每一个程序包括与系统集成的软件都使用的。如果网络文件系统故障,且默认搜索路径包括它,那么整个系统就无法工作。

  • 将rpath编码进入程序
ELF程序(Solaris和Linux上的程序)可以包含共享库的补充路径(supplemental path)(共享库同样可以有他们自己的补充路径以用于查找其它的共享库,但是这个不常用)。这个路径会在查找系统默认搜索路径(如果设置LD_LIBRARY_PATH,会被覆写)前执行。

这个路径必须在编译期间编码进入程序,之后不再改变。这种方式相对于LD_LIBRARY_PATH的优势是:除了LD_LIBRARY_PATH造成的问题外,它是由编译这个程序的人设置的,而不是由使用程序的人设置的。这意味着,配置只需由相对了解程序的人设置一次即可,而不用每一个可能了解或者不了解这个问题的人来操作。

通常有两种方法将路径(rpath)告诉编译器来编码搜索路径:
  • 设置环境变量:LD_RUN_PATH。设置为你想编码的搜索路径。你不需要包含系统默认搜索路径(事实上不应该包括,因为你可能干扰了在原生程序的搜索算法的变化。(译者:因为你加入了默认搜索路径,但是如果默认搜索路径发生变化,这种把系统默认搜索路径加入的方式可能会影响这部分程序的正确查找))。编译器在链接程序时会将设置的环境变量编码进入程序。
  • 在链接命令中加入链接参数:-R /usr/local/lib(任何其它你需要的路径)。这个参数和-L一样,可以设置多次不同的路径。命令行中的顺序不重要。

不同的编译器和连接器组合需要不同的参数,-R对于大多数的GNU编译器和链接器组合都能工作,但是不是在所有的编译器和链接器组合上工作。在某些场景下,你需要使用-rpath来代替-R告诉编译器将路径传递给链接器。典型的应用方式为:-Wl, -rpath, /usr/local/lib

任何一种方式都可以,通常我会使用LD_RUN_PATH,因为它更具有更低的侵入性。然而,请注意如果在命令中有任何的-R或者-Wl, -rpath,通常LD_RUN_PATH会被忽略。这意味着如果原来的编译命令中使用了-R,你需要自己将路径使用同样的方式加入(除非编译器很友好的帮你讲安装路径加入,通常都会这么做)。如果软件使用Autoconf,一般情况下最简单的方式是在执行配置前设置LDFLAGS(不用-R /usr/local/lib),这些配置会传递至生成的Makefile中。如果做不到的话(译者:前面的在执行配置前设置LDFLAGS),我有时会做如下的设置:

make CC="gcc -R /usr/local/lib"

来编译软件,通常会正常工作。(如果必要的话,使用-Wl, -rpath同样可以)

使用libtool来链接自己的库和链接其它的库大多数情况下都没有问题,rpath在编译最后的程序时会编码进入程序,但不是绝对。幸运的是,libtool也可以理解-R和-Wl, -rpath,当传递这两个命令给他后,它也会正常工作。

  • 检查二进制程序
为了验证一个程序可以正常工作,我们可以使用ldd命令。这个命令会列出所有依赖的库(如果找到路径,输出路径;否则输出not found)。在Mac OSX上使用otool -L

Solaris中的ldd有个非常有用的选项-s,这个会额外输出程序的所有搜索路径,这样你就可以确认你把正确的rpath都编码进了程序。

Linux上一个类似的命令:

readelf -d <binary> | grep  RPATH

但是请注意这个命令只显示指定的程序中的rpath。如果一个共享库依赖于其它的共享库,这些共享库可能会由加载这些依赖库的程序中的rpath来搜索。为了确保结果正确,你可能需要将上述命令应用至指定的程序以及其它它所使用的库上。readelf是binutils自带的,所以如果你可以编译程序,说明你可能已经安装了readelf


  • 改变rpath
在Linux上有个应用patchelf可以修改、删除、增加rpath。这个应用取代老的chrpath应用(已经不再维护)。

patchelf在Solaris上不是直接可以编译。用它你可能仍然需要安装chrpath。但是chrpath维护者的ftp已经有段时间无法访问了。这种情况下,最好的源是Debian package。

Mac OSX自带一个叫intall_name_tool的应用可以实现最编码到程序的rpath类似的功能。但是改后的rpath不能比之前的rpath长,除非程序链接时使用了-headerpad_max_install_names标记。

  • 其它系统

恐怕关于其他系统(Tru64, HP-UX, AIX, IRIX. HP-UX和AIX不使用ELF格式,所以链接机制完全不一样;Tru64和IRIX通常会在编译时将找到的库编码进入程序,所以不需要上面的方法来辅助,但是我不清楚是如何工作的;HP-UX和AIX可能有类似的机制)我无法提供必要的帮助信息。
0 0
原创粉丝点击