总结一下:
a. ldconfig 查看默认库路径和ld.so.conf包含的库路径,来建立运行时动态装载的库查找路径。
b. ldconfig不会影响gcc编译、链接时的库查找路径,gcc编译链接路径由其自己确定。
c. LD_LIBRARY_PATH既影响gcc编译链接时的库搜索路径,又影响运行时库查找路径。
d. gcc链接时查找库只是为了在目标文件中确定所需的动态库,并记录在dynamic字段中。
首先搞清楚编译链接时搜索路径和运行时搜索路径的区别
这里的一篇博文说的比较清楚:
严格的来说,头文件是在编译阶段搜索的。C和C++里面有两种include,一种是include “head.h”,一种是include
。在gcc中,唯一的区别在于,前者会试图在当前目录搜索头文件,而后,两者的搜索路径是一样的,按照如下的顺序a、由gcc选项-I指定的目录
b、查找gcc的环境变量C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH
c、GCC内定的目录搜索(这个貌似和编译生成GCC时的设置有关),一般是:
/usr/local/include
libdir/gcc/target/version/include
/usr/target/include
/usr/include
其中target指gcc的名字,version指gcc的版本
在gcc编译时,加上-v选项,会显示详细的头文件查找顺序。
参考资料:GCC官方文档之《Search Path》。
建议:如果引用第三方头文件时,一般在makefile文件中,在CXXFLAGS添加上-I就可以。
在链接产生共享对象或者可执行文件时,如果告诉了gcc需要链接静态库或共享对象,那么gcc会按照一定的顺序去查找这些库。查找的顺序是:
a、由gcc选项-L指定的目录
b、由环境变量LIBRARY_PATH和LD_LIBRARY_PATH指定的目录
c、由GCC内定的库搜索目录,一般是:
/lib
/usr/lib
/usr/local/lib
在gcc链接时,加上-v选项,可以看到gcc把所有要搜索的目录都通过-L选项加进来了。
这里很奇怪的是LD_LIBRARY_PATH也会影响链接时的库文件搜索,这个变量不推荐使用,有人专门写了一篇文章来指出为什么不要随意使用这个变量。《Why LD_LIBRARY_PATH is bad》
建议:一般需要链接库时,用-L指定库的搜索路径就可以了。
这个地方是最容易引起误会的,可能你会问,链接的时候不是已经指定了搜索路径吗?怎么还出来了一个运行时的搜索路径。特别是很多人会说,我不是再链接时用-L指定了搜索目录吗?这是因为共享库在编译链接过程和可执行文件装载过程都需要参与,所以要分别指定这两个过程共享库的查找目录。-L指定的是编译链接过程的查找目录,下文会讲,可以用Wl,-rpath 指定装载过程的查找目录。
一、先来想想,在编译链接一个可执行文件时,为什么还需要链接共享库?
假设a.cc需要调用b.so,用gcc –o a.out a.cc b.so命令生产可执行文件。
是啊,确实奇怪,在链接生产可执行文件a.out的时候,为什么还要链接共享库?共享库不是在可执行文件运行的时候动态链接的吗?其实,这个时候并不链接b.so,输入b.so到链接器主要完成两项工作:
1、让链接器看看a.cc里面引用了哪些b.so的符号,这些符号暂时不解析,标记为运行时动态解析。
2、在a.out的dynamic段里面,添加一个NEED项,一般这一项填写的是共享库的so-name。
好了,这就是在链接生产可执行文件过程做的全部工作了,显然此时指定的搜索路径只是为了在链接时找到共享库。
二、在可执行文件运行时,会由动态链接器去查看可执行文件的dynamic段,扫描Need项来得到此可执行文件依赖于哪些共享库,然后去查找这些共享库,再装载到内存里面来。问题来了,NEED项里面绝大多数情况存放的是共享库的so-name,如libc.so.6,这只是一个文件名,当然需要到相应的目录里面去查找这些文件。
上文说了这么多,就是为了阐述链接共享库时,链接时指定了目录,运行时还需要指定。在运行时,共享库的搜索目录顺序为:
a、通过gcc给ld传递-rpath选项,指定动态链接器查找共享库的目录。如gcc –Wl,-rpath ./ –o a.out a.cc b.so,则表示在运行时,先在当前目录查找b.so。
-rpath的实现是,把指定的目录写成可执行文件ELF中,具体的说,会在dynamic段添加(RPATH项),项的值就是指定的目录.
b、由环境变量LD_LIBRARY_PATH指定的路径。前面说了,这个变量不建议使用。一般只用在调试和测试共享库的时候。
c、由路径缓存文件/etc/ld.so.cache指定的路径,这个文件的结构经过特殊的设计,使得查找很快。这个文件是由ldconfig命名自动创建,更新和删除的。具体内容请看《ldconfig命令详解》
d、默认共享库目录,一般是/usr/lib,/lib
主要的查找目录在/etc/ld.so.cache里面指定,如果在安装第三方库后,没有使用ldconfig命令更新这个文件,将使得这个库无法使用。这是很多人碰到的问题。具体解决方法见《ldconfig命令详解》。
http://1.lz24.sinaapp.com/2013/05/14/linux-%E4%B8%8Bgcc-%E5%A4%B4%E6%96%87%E4%BB%B6%EF%BC%8C%E5%BA%93%E6%96%87%E4%BB%B6%E6%90%9C%E7%B4%A2%E8%B7%AF%E5%BE%84%E8%AF%A6%E8%A7%A3/然后是需要弄清楚ldconfig的工作原理:
ldconfig这个命令从名字上来看就可以判定它和动态链接器有关。如果提示ldconfig命令找不到,则用绝对路径/sbin/ldconfig。它主要做两件事:
1、为它管辖范围内所有的共享库管理so-name软连接,所谓的管理是指创建,删除和更新。比如,向/usr/local/lib添加一个共享库libtest.so.1.4,很显然它的so-name是libtest.so.1。运行ldconfig将产生一个软连接libtest.so.1指向libtest.so.1.4。如果再添加一个libtest.so.1.5,这运行ldconfig后,libtest.so.1这个软链接将指向libtest.so.1.5。如果把libtest.so.1.4,libtest.so.1.5都删除,那么libtest.so.1也将被删除。至于so-name对于共享库的意义,将在《Linux 共享库soname详解》介绍。
2、把它管辖范围内的所用共享库都放到路径缓存文件中/etc/ld.so.cache中,确保这些共享库在运行的时候,能被动态链接器找到。这个文件的存放格式是:
通过执行/sbin/ldconfig -p可以查看ld.so.cache文件中所有的共享库,以下是我在自己机子上运行此命令输出的一部分结果:
下文主要讲怎么利用ldconfig解决运行时无法找到共享库的问题。
在《Linux 下gcc 头文件,库文件搜索路径详解》中提到,/etc/ld.so.cache里面的路径对于运行时共享库的查找具有相当重要的作用,很多同学在使用第三方共享库或者自己生成的共享库时,往往会出现这样的问题,链接时一切正常(因为大家都知道用-L指定链接时的搜索目录~~~^_^~~~ ),但是运行时就是找不到。很可能的原因是:
1、这些共享库没有放到ldconfig的管辖范围。
2、这些共享库刚放到ldconfig的管辖范围,但是没有立刻调用sudo /sbin/ldconfig命令更新/etc/ld.so.cache文件,因而,还是找不到这些库。
我们已经多次提到ldconfig的管辖范围这个概念,其实管辖范围就是指一些目录。这些目录由/etc/ld.so.conf指定,默认情况下它的内容是
可见,ldconfig的管辖范围是/usr/local/lib以及ld.so.conf/文件夹下,其他.conf文件指定的目录。可以在ld.so.conf文件中添加目录来让ldconfig管理其他的共享库。
知道了ldconfig的原理,终于可以讲讲共享库的安装问题了。
很多人认为在linux下生产共享库很简单,确实很简单,只要在gcc命令上加上选项-shared 差不多就可以了。但是使用的时候问题多多,这是因为没有认识到共享库还有一个安装的问题。
所谓安装共享库,就是把创建的共享库添加到ldconfig管辖范围。确保运行时,动态链接器能找到这些库。正是因为忽略了这个问题,很多人运行使用共享库的程序时,会提示找不到共享库的错误。
安装的方法基本上有两种:
1、把共享库移动到ldconfig的管辖范围内,就是/etc/ld.so.conf指定的目录中,一般是/usr/local/lib
2、扩大ldconfig的管辖范围,要么就是往/etc/ld.so.conf添加目录,要么就是在ld.so.conf.d文件夹下创建一个.conf文件,文件内容为要共享库所在目录。
添加完目录以后,记得执行/sbin/ldconfig命令,更新路径缓存文件/etc/ld.so.cache。
注意:并不是所有的共享库都必须需要安装,如果想使用没有安装的共享库(就是那些不存在于/etc/ld.so.cache文件中的库),那么可以通过-Wl,-rpath 和指定 LD_LIBRARY_PATH 环境变量来确保共享库能在运行时被找到。
http://1.lz24.sinaapp.com/2013/05/14/ldconfig%E8%AF%A6%E8%A7%A3/
最后讨论下ldd的工作原理:
ldd
作用:用来查看程序运行所需的共享库,常用来解决程序因缺少某个库文件而不能运行的一些问题。
ldd命令原理(摘自网络)
1、首先ldd不是一个可执行程序,而只是一个shell脚本
2、ldd能够显示可执行模块的dependency,其原理是通过设置一系列的环境变量,如下:LD_TRACE_LOADED_OBJECTS、
LD_WARN、LD_BIND_NOW、LD_LIBRARY_VERSION、LD_VERBOSE等。当
LD_TRACE_LOADED_OBJECTS环境变量不为空时,任何可执行程序在运行时,它都会只显示模块的dependency,而程序并不真正执行。要不你可以在shell终端测试一下,如下:
(1) export LD_TRACE_LOADED_OBJECTS=1
(2) 再执行任何的程序,如ls等,看看程序的运行结果
3、ldd显示可执行模块的dependency的工作原理,其实质是通过ld-linux.so(elf动态库的装载器)来实现的。我们知道,ld-
linux.so模块会先于executable模块程序工作,并获得控制权,因此当上述的那些环境变量被设置时,ld-linux.so选择了显示可执
行模块的dependency。
4、实际上可以直接执行ld-linux.so模块,如:/lib/ld-linux.so.2 –list program(这相当于ldd program)
LD_LIBRARY_PATH (参考:http://blog.chinaunix.net/uid-25304914-id-3046279.html)
要指示动态装入器首先检查某个目录,请将 LD_LIBRARY_PATH 变量设置成您希望搜索的目录。多个路径之间用冒号分隔;例如:
# export LD_LIBRARY_PATH=”/usr/lib/old:/opt/lib”
导出 LD_LIBRARY_PATH 后,如有可能,所有从当前 shell 启动的可执行程序都将使用 /usr/lib/old 或 /opt/lib 中的库,如果仍不能满足一些共享库相关性要求,则转回到 /etc/ld.so.conf 中指定的库。