运行编译后的程序报错 error while loading shared libraries: lib*.so: cannot open shared object file: No such file or directory

1、错误原因

错误的原因大概是运行程序的时候,没有找到程序所需要的lib*.so动态库。动态库和静态库的最大区别就是,静态库是静态连接,也就是在产生可执行文件的时候就把静态库中的实现嵌入到程序中了,一旦编译成功,静态库也就有存在的价值了,即便静态库不存在了,可执行程序也是可以跑起来的。但是动态库就不一样了,它是遵循动态链接,也就是说编译的时候需要去指定路径找该so文件链接编译,运行的时候也需要指定相应的路径去找。如果在运行的时候,可执行程序会先去默认的系统lib目录下寻找该so,如果找不到了就报错error while loading shared libraries。(可以通过该文章了解一下动态库的动态加载机制http://blog.csdn.net/dbzhang800/article/details/6918413)


2、动态库的搜索路径搜索的先后顺序

  • 编译目标代码时指定的动态库搜索路径
  • 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
  • 配置文件/etc/ld.so.conf中指定的动态库搜索路径
  • 默认的动态库搜索路径/lib/usr/lib

可参考这篇文章,它里面提及了gcc编译动态库运行链接动态库的知识。http://www.cnblogs.com/zhengmeifu/archive/2010/03/02/linux-gcc_compile_header_file_and_lib_path.html

回到可执行程序error while loading shared libraries错的问题上,我们可以针对上述的几个加载顺序选择一种解决办法。比如我们可以使用第二个办法LD_LIBRARY_PATH指定的动态库搜索路径,即通过设置LD_LIBRARY_PATH,把当前程序使用的so的路径添加到LD_LIBRARY_PATH中去,这样程序跑起来的时候,去LD_LIBRARY_PATH找肯定就可以找到该so了,当然之前我们也已经验证了此方法的准确性。

1038604-20161030005620718-795671863

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 再例如
# ImportError: libmkl_intel_lp64.so: cannot open shared object file: No such file or directory

# 解决方案
# 在 /opt/conda 路径下查找对应文件,一般就在安装的conda路径下
find /opt/conda -name libmkl_intel_lp64.so

# 假如find的结果显示如下,那么这就是所在路径了
/opt/conda/lib/libmkl_intel_lp64.so

# 将刚才搜索出来的路径添加到环境变量中
export LD_LIBRARY_PATH=/opt/conda/lib:$LD_LIBRARY_PATH

# 更新环境变量文件
source ~/.bashrc

# 确认一下是否正确添加了路径
echo $LD_LIBRARY_PATH

我们可以不用设置LD_LIBRARY_PATH,也一样可以把程序跑起来。主要可以从编译该程序开始。这里就要先介绍下gcc的编译选项了,但这里不具体细说,可以看这篇文章gcc使用中常用的参数及命令http://www.cnblogs.com/Recan/p/6012248.html

这里我们需要使用的编译选项是-Wl-rpath;这里是字母l而不是1-rpath选项就是告诉gcc在编译链接的时候,把该程序运行时查找so的路径写入到ELF文件中。使用方法就是gcc -o OutApp *.c -lzint -Wl,-rpath="/usr/local/lib"或者gcc -o OutApp *.c -lzint -Wl,-rpath -Wl,"/usr/local/lib"两者在功能上是等价的。

1038604-20161030005640000-2077874288

从图中我们可以看到,确实编译成功后直接运行程序就不会再报error while loading shared libraries了,而且这种方法最大的好处就是在编译成功后,不用再去做任何设置就可以把程序跑起来了,当然是编译时传递的-rpath参数得是正确的。这个方法尤其是在交叉编译嵌入式设备的可执行程序时十分有用。


3、关于安装共享库后要注意的共享库路径设置问题

【1】如果共享库文件安装到了/lib/usr/lib目录下,那么需要执行一下ldconfig命令

ldconfig命令的用途主要是在默认搜索目录(/lib/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件,缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表。


【2】如果共享库文件安装到了/usr/local/lib(很多开源的共享库都会安装到该目录下)或其他非/lib或/usr/lib目录下,那么在执行ldconfig命令前还要把新共享库目录加入到共享库配置文件/etc/ld.so.conf
1
2
3
4
# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
# echo "/usr/local/lib" >> /etc/ld.so.conf
# ldconfig

【3】如果共享库文件安装到了其它非/lib或/usr/lib目录下,但是又不想在/etc/ld.so.conf中添加路径(或者是没有权限添加路径),那可以export一个全局变量LD_LIBRARY_PATH,然后运行程序的时候就会去这个目录中找共享库

LD_LIBRARY_PATH的意思是告诉loader在哪些目录中可以找到共享库,可以设置多个搜索目录,这些目录之间用冒号分隔开。

比如安装了一个mysql/usr/local/mysql目录下,其中有一大堆库文件在/usr/local/mysql/lib下面,则可以在.bashrc.bash_profile(在~/目录下,没有的话自己创建)或shell里加入以下语句即可:

1
2
# 一般来讲这只是一种临时的解决方案,在没有权限或临时需要的时候使用
export LD_LIBRARY_PATH=/usr/local/mysql/lib:$LD_LIBRARY_PATH

【4】如果程序需要的库文件比系统目前存在的库文件版本低,可以做一个连接
1
2
3
4
5
6
$ ls /usr/lib/libncu*
/usr/lib/libncurses.a /usr/lib/libncurses.so.5
/usr/lib/libncurses.so /usr/lib/libncurses.so.5.3

# 可见虽然没有libncurses.so.4,但有libncurses.so.5,是可以向下兼容的建一个链接就好了
ln -s /usr/lib/libncurses.so.5.3 /usr/lib/libncurses.so.4