Contents

解决 Linux 中的“无法打开共享库文件:没有这样的文件或目录”错误

1. 概述

在本教程中,我们将介绍Linux中“无法打开共享对象文件:没有此类文件或目录”错误的各种原因和修复方法。

2. 什么是共享库?

Linux 中的共享库 为程序提供了各种可重用的功能。 假设我们要编写一个将压缩文件作为输入的程序。为此,我们可以使用现有的库,例如zlib  ,而不是自己实现解压功能。

程序在启动时加载共享库。

此外,我们可以使用**ldd 命令列出程序使用的所有共享库:**

$ ldd /usr/bin/clang
/usr/bin/clang:
	linux-vdso.so.1 (0x00007ffd34a90000)
	libclang-cpp.so.13 => /usr/bin/../lib/libclang-cpp.so.13 (0x00007f24c5a94000)
	libLLVM-13.so => /usr/bin/../lib/libLLVM-13.so (0x00007f24c1548000)
	libstdc++.so.6 => /usr/bin/../lib/libstdc++.so.6 (0x00007f24c12d6000)
	libm.so.6 => /usr/bin/../lib/libm.so.6 (0x00007f24c11f8000)
	libgcc_s.so.1 => /usr/bin/../lib/libgcc_s.so.1 (0x00007f24c11dd000)
	libc.so.6 => /usr/bin/../lib/libc.so.6 (0x00007f24c0fc4000)
	/lib/ld-linux-x86-64.so.2 => /usr/lib/ld-linux-x86-64.so.2 (0x00007f24c95d4000)
	libz.so.1 => /usr/bin/../lib/../lib/libz.so.1 (0x00007f24c0fad000)

在这里,我们可以看到clang 编译器需要各种库(例如LLVM )才能运行。

正如我们所见,共享库以*.so*扩展名结尾。

有时,共享库可能不存在或可能位于非标准路径中。结果,我们在程序启动时得到“无法打开共享对象文件:没有这样的文件或目录” 。**

3. 缺少包

有时,我们可能只是缺少提供所需共享库的包。

例如,如果一个程序抱怨缺少libzstd.so,我们可以尝试在系统的包管理器中搜索它。

在基于apt 的系统上,我们可以使用apt search命令:

$ apt search zstd
Sorting... Done
Full Text Search... Done
zstd/stable 1.5.0 aarch64
  Zstandard compression.

现在,我们可以使用apt install安装缺少的软件包。但是,我们必须注意,这种方法不是很可靠,并且需要一些猜测才能找到包名。

在上面的示例中,缺少的库的名称是libzstd.so,但没有名为libzstd的包。结果,我们不得不猜测并只搜索zstd

4. LD_LIBRARY_PATH变量

我们可以在LD_LIBRARY_PATH环境变量中指定要搜索共享库的目录。

LD_LIBRARY_PATH是一个以冒号分隔的目录列表,就像PATH 变量一样。

默认搜索路径通常限于/usr/lib/usr/local/lib。**

假设我们有一个链接到libfoo.so的程序,位于*/home/blogdemo/libs/libfoo.so*,它位于默认搜索路径之外。然后,我们可以使用此变量附加到搜索路径并解决问题。

首先,让我们使用ldd命令确认程序链接到的确切库:

$ ./program
./program: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
$ ldd ./program
./program:
	linux-vdso.so.1 (0x00007ffce871b000)
	libfoo.so => not found
	libc.so.6 => /usr/lib/libc.so.6 (0x00007fdceadaf000)
	/lib/ld-linux-x86-64.so.2 => /usr/lib/ld-linux-x86-64.so.2 (0x00007fdceafd3000)

现在,让我们将目录添加到LD_LIBRARY_PATH并让我们的程序运行:

$ export LD_LIBRARY_PATH=/home/blogdemo/libs
$ ldd ./program
./program:
	linux-vdso.so.1 (0x00007ffe28dfd000)
	libfoo.so => /home/blogdemo/libs/libfoo.so (0x00007f8f0f7ba000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007f8f0f59d000)
	/lib/ld-linux-x86-64.so.2 => /usr/lib/ld-linux-x86-64.so.2 (0x00007f8f0f7c6000)

此外,如果我们不知道库在哪里,我们可以使用find 命令在常见路径中找到它,例如*/home/usr*:

$ find /home -type f -name libfoo.so
/home/blogdemo/libs/libfoo.so

5. 永久配置库路径

我们可以使用*/etc/ld.so.conf*文件来永久设置库搜索路径。在这个文件中,我们指定了一个以换行符分隔的目录列表。

让我们再次修复libfoo.so错误:

$ ./program
./program: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
$ echo "/home/blogdemo/libs" | sudo tee /etc/ld.so.conf
/home/blogdemo/libs
$ sudo ldconfig
$ ldd ./program
./program:
	linux-vdso.so.1 (0x00007ffefc3db000)
	libfoo.so => /home/blogdemo/libs/libfoo.so (0x00007f021e88d000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007f021e674000)
	/lib/ld-linux-x86-64.so.2 => /usr/lib/ld-linux-x86-64.so.2 (0x00007f021e89e000)

我们必须运行一次ldconfig命令以使系统知道新路径。

6. ldconfig命令

我们可能最近安装了新的共享库或修改了共享库搜索路径。因此,我们需要运行ldconfig 命令。

它更新链接器的缓存以使其了解新的共享库。

链接器,称为ld.so 加载程序的共享库。

我们可以使用*-p标志调用ldconfig*来检查当前缓存:

$ ldconfig -p
347 libs found in cache `/etc/ld.so.cache'
	libzstd.so.1 (libc6,x86-64) => /usr/lib/libzstd.so.1
	libzstd.so (libc6,x86-64) => /usr/lib/libzstd.so
	libz.so.1 (libc6,x86-64) => /usr/lib/libz.so.1
	libz.so (libc6,x86-64) => /usr/lib/libz.so
	libx265.so.199 (libc6,x86-64) => /usr/lib/libx265.so.199
	libx265.so (libc6,x86-64) => /usr/lib/libx265.so
	libx264.so.157 (libc6,x86-64) => /usr/lib/libx264.so.157
	libx264.so (libc6,x86-64) => /usr/lib/libx264.so
...

从这个输出中,我们可以确定各种共享库的确切位置。

7. 在编译时设置库路径

如果我们可以访问程序的源代码,我们可以在链接过程中 使用特殊标志对其进行编译, 以便可以找到共享库。

我们可以在运行时使用ld 的*-rpath*标志将库路径传递给动态链接器。

让我们编译一个基本程序并将其链接到我们的库:

$ pwd
/home/blogdemo/libs
$ ls
libfoo.so
$ echo "int main() {}" > program.c # Dummy program
$ cc program.c libfoo.so # Link to our library
$ ./a.out 
./a.out: error while loading shared libraries: libfoo.so: cannot open shared object file: No such file or directory
$ ldd ./a.out 
./a.out:
	linux-vdso.so.1 (0x00007ffcbc1f8000)
	libfoo.so => not found
	libc.so.6 => /usr/lib/libc.so.6 (0x00007feb0ee04000)
	/lib/ld-linux-x86-64.so.2 => /usr/lib/ld-linux-x86-64.so.2 (0x00007feb0f029000)

我们可以看到程序无法加载我们的共享库。

现在,让我们尝试使用 -rpath 标志编译它,将* /home/blogdemo/libs*添加到库搜索路径:

$ gcc program.c libfoo.so -Wl,-rpath=/home/blogdemo/libs
$ ldd ./a.out 
./a.out:
	linux-vdso.so.1 (0x00007ffd0fbad000)
	libfoo.so => /home/blogdemo/libs/libfoo.so (0x00007fba09d99000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007fba09b7b000)
	/lib/ld-linux-x86-64.so.2 => /usr/lib/ld-linux-x86-64.so.2 (0x00007fba09da5000)

在这里,我们使用gcc 的*-Wl标志将参数传递给ld*。