linux 下库的深入调研
linux操作系统中,linux库文件路径还是比较常用的,于是我研究了一下linux库文件路径,在这里拿出来和大家分享一下,希望对大家有用。
库文件在连接(静态库和共享库)和运行(仅限于使用共享库的程序)时被使用,其搜索路径是在系统中进行设置的。一般 Linux 系统把 /lib 和 /usr/lib 两个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置搜索路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:
1、在环境变量 LD_LIBRARY_PATH 中指明库的搜索路径。
2、在 /etc/ld.so.conf 文件中添加库的搜索路径。
将自己可能存放linux库文件的路径都加入到/etc/ld.so.conf中是明智的选择
添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:
/usr/X11R6/lib
/usr/local/lib
/opt/lib
需要注意的是:第二种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。这是因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件 /etc/ld.so.cache 从中进行搜索的。/etc/ld.so.cache 是一个非文本的数据文件,不能直接编辑,它是根据 /etc/ld.so.conf 中设置的搜索路径由 /sbin/ldconfig 命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig 命令要以 root 权限执行)。因此,为了保证程序执行时对库的定位,在 /etc/ld.so.conf 中进行了库搜索路径的设置之后,还必须要运行 /sbin/ldconfig 命令更新 /etc/ld.so.cache 文件之后才可以。ldconfig ,简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件缓存到/etc/ld.so.cache 以供使用。因此当安装完一些linux库文件路径,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下 /sbin/ldconfig使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用的,结果编译过程中抱错,缺少xxx库,去查看发现明明就在那放着,搞的想大骂computer蠢猪一个。
在程序连接时,对于linux库文件(静态库和共享库)的搜索路径,除了上面的设置方式之外,还可以通过 -L 参数显式指定。因为用 -L 设置的路径将被优先搜索,所以在连接的时候通常都会以这种方式直接指定要连接的库的路径。
前面已经说明过了,搜索linux库文件路径的设置有两种方式:在环境变量 LD_LIBRARY_PATH 中设置以及在 /etc/ld.so.conf 文件中设置。其中,第二种设置方式需要 root 权限,以改变 /etc/ld.so.conf 文件并执行 /sbin/ldconfig 命令。而且,当系统重新启动后,所有的基于 GTK2 的程序在运行时都将使用新安装的 GTK+ 库。不幸的是,由于 GTK+ 版本的改变,这有时会给应用程序带来兼容性的问题,造成某些程序运行不正常。为了避免出现上面的这些情况,在 GTK+ 及其依赖库的安装过程中对于库的搜索路径的设置将采用第一种方式进行。这种设置方式不需要 root 权限,设置也简单:
$ export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH
可以用下面的命令查看 LD_LIBRAY_PATH 的设置内容:
$ echo $LD_LIBRARY_PATH
至此,库的两种设置就完成了。
以上给大家讲解的是linux库文件,关于Linux系统中如何添加自己的库文件路径。
----------------------------------------------------------------------
下面这篇文章写的非常不错。
经常见有人提起关于库的种种问题,今天我也终于按捺不住,根据自己的经验,实验,学习中得到的一些,来说说自己的一点看法. 我们都知道库对系统的重要.没了它,系统几乎无法运转,包括LFS整个过程至少是对工具链调整来调整去的过程是以对库的倚赖为核心的.这其中又以动态库为精华. 那先来说简单的静态库.它简单到只是ar打包的目标文件的集合罢了,于是,它的作用也就和目标文件没什么区别了,链接进目标文件,ok,使命完成,至于程序以后的事包括运行则和这个静态库没有关系了.其实我觉的最有说服力的就是例子了,那我们就举最简单的例子. cat >say.c<<eof #include "stdio.h" void say() { printf("Say!"); } eof cat >test.c <<eof #include "stdio.h" void say(); main(){ say(); } eof gcc -c say.c ar -r say.a say.o gcc test.c say.a -o test ldd test 输出结果让我们看不到任何跟say.a这个我们自己写的静态库的关系.说明程序运行时已经不需要这个静态库了,它已经被ld链接进最终的程序了. 那么动态库,我们继续 gcc -fPIC -shared say.c -o say.so gcc test.c say.so -o test ldd test 如果不出意外的话,会出现say.so => not found.这时的./test是不能运行的.但至少说明程序运行时是需要这个库的.那为什么找不到这个库呢?那就让我们看看系统是怎样寻找这些库的吧. 首先是ld-linux.so.2这个不能不说,它太重要了,以至于也决定了后面的搜索方式. 先是程序内部决定的. strings test 还好我们这个test程序不大,不用过滤输出,好,你看见什么,/lib/ld-linux.so.2,say.so,libc.so.6,对,用到的库! 但我们发现不同,有的有路径,有的没有,先不管没有路径的怎么寻找,有路径的肯定是能找到了,那好,我们让say.so也有了路径. gcc test.c ./say.so -o test2 strings test2 我们发现原来的输出中原来的say.so已经变成了./say.so.运行一下./test2,可以运行了!好,找到库了,这里用的相对路径,无疑,我们将say.so移动到非当前文件夹.那test就又不能运行了.这样无疑是把我们用到的库硬编码进了程序里.我不喜欢硬编码,太死板.那不硬编码系统怎么找到我们需要的文件呢. 在程序没有把库地址硬编码经进去的前提下,系统会寻找LD_LIBRARY_PATH环境变量中的地址. LD_LIBRARY_PATH=./ ./test2 如我们所愿,程序正常运行. 如果系统在这一步也没发现我们需要的库呢. /etc/ld.so.cache这个由ldconfig生成的文件,记载着在/etc/ld.so.conf文件中指明的所有库路径加上/lib,/usr/lib里的所有库的信息. 其实以上这句话只是在大多数情况下是正确的,是否是这个文件由ld-linux.so.2决定.如过你的LFS中的第一遍工具链/tools还在的话, strings /tools/lib/ld-linux.so.2|grep etc 输出很可能是/tools/etc/ld.so.cache.那么它用的哪个文件我们就清楚了吧. 可这个路径前面的/tools到底和什么有关呢?首先我们可能会想到与ld-linux所在的位置有关.还好我们有3套glib,感谢LFS,现在我们拿第二遍的工具链下手.假设我们的LFS在/lfsroot strings /lfsroot/lib/ld-linux.so.2 很奇怪的是输出竟然是/etc/ld.so.cache!那这到底和什么有关呢,没错就是我们编译时候的--prefix有关. 现在再看这个/etc/ld.so.conf,和/lib,/usr/lib这些默认ldconfig路径.也都要加上个这个prefix了. strings /tools/sbin/ldconfig|grep etc strings /tools/sbin/ldconfig|grep /lib 验证一下吧. 那要是ld.so.cache里也没有记载这个库的地址怎么办呢. 最后在默认路径里找.这个路径一般是/lib,/usr/lib,但也不全是. strings /tools/lib/ld-linux.so.2|grep /lib 还是要加个prefix. 现在我们反过来思考,不用程序中硬编码的/lib/ld-linux.so.2做动态加载器了.这也可以?!是的!虽然不一定成功. LD_TRACE_LOADED_OBJECTS=y /tools/lib/ld-linux.so.2 /bin/test LD_TRACE_LOADED_OBJECTS=y /lib/ld-linux.so.2 /bin/test LD_TRACE_LOADED_OBJECTS=y /lfsroot/lib/ld-linux.so.2 /bin/test 试着比较结果吧. 不出意外的话第一个是在/tools/lib中搜索的库,二三个都是在/lib中的库.原因我想上面已经说清楚了. 下面以第二个为例说明问题: LD_LIBRARY_PATH=./ /tools/lib/ld-linux.so.2 ./test /tools/sbin/ldconfig ./;/tools/lib/ld-linux.so.2 ./test cp ./say.so /tools/lib/;/tools/lib/ld-linux.so.2 ./test 三种方法应该都会出现我们想要的结果,这里说明/tools/lib/ld-linux.so.2在这里的含义,是用/tools/lib/ld-linux.so.2这个做动态装载器.不信把这个去掉,后两种方法一定不行.因为以./test自己硬编码进去的/lib/ld-linux.so.2来说,是不会去管/tools/etc/ld.so.cache,和/tools/lib下的库的. 为了说明顺序,我们做如下很危险的实验: ldconfig /lfsroot/lib; ldconfig -p 会出现很多内容,但不要试着过滤,因为这时的系统应该很多程序不能运行了.先踏下心来观察.你会发现很多库出现两次/lfsroot/lib,和/lib而且/lfsroot/lib在前,说明ldconfig先处理参数给出的地址,最后是默认地址.但顺序也不一定,应该还和编译glibc时我们的参数--enable-kernel有关(我根据种种表现猜测). 加上export LD_LIBRARY_PATH=/lib 环境变量在前面,不能运行的程序又能运行了,说明LD_LIBRARY_PATH变量的优先级优于ld.so.cache unset LD_LIBRARY_PATH echo >/etc/ld.so.cache ldconfig -p 应该什么都不出现,可大部分程序能运行.说明ld-linux.so.2决定的默认路径起了作用(注意,这里的ldconfig的默认路径没有作用) ldconfig 恢复系统正常. 如果你原意,可以chroot /lfsroot后,再做类似的操作看有什么不同. 懂了原理我们就来应用一下. 拿./test2为例. 我们把它的库给换了!!! cat >saa.c <<eof #include "stdio.h" say(){ printf("I can do something here!!!"); } eof gcc -fPIC -shared saa.c -o saa.so sed "s#\./say\.so#./saa.so#" test >test3 ./test3 看看结果吧! 很令人惊奇是么,如果是setuid程序的话...其实这个也很难,因为这种程序我们一般是无法写的(给自己搞破坏不算).这也就明白了为什么长久以来对setuid程序的权限始终如此重视----因为太危险了. 惊奇过后你可能会想,对于未硬编码库地址的程序,我们直接把LD_LIBRARY_PATH改了不也行么?!指向我们的地址,用我们的库,然后...根本不用改什么文件了,要什么写权限了. 呵呵,要真那么容易我们可爱的Linux不也太脆弱了,这恐怕就玩大了,也是你我都不原意见到的.所以,ld-linux.so.2早以作出限止,setsid程序,LD_LIBRARY_PATH变量不起作用.不过文件中的还是有作用的. 最后,说一下ld,和ld-linux.so.2的区别,一个编译时用,一个运行时用,ld负责在它的搜索路径里找到要求的库,并查看是否有提供了需要的符号(如函数等),如果有,记录相关信息到程序中,由ld-linux.so.2在执行时查找到该库并,并根据相关信息进行需要符号的重定位等工作.注意这两者的搜索库的方式是不同的. LC_ALL=C ld --verbose|grep search -i 显示了它默认的查找地址.我们可以做个实验.一般它会有个类似i686-pc-linux-gnu/lib的路径,同时是不在ld-linux.so.2的搜索路径里的.其余的是我们编译是--with-lib-path和LIB_PATH变量指定的. mv ./say.so XXXX/i686-pc-linux-gnu/lib/libsay.so gcc -o test4 -lsay test.c ldd ./test4 结果肯定是libsay.so找不到的. |