工作时遇到了一个奇的问题。手动创建某个so符号链接似乎不能被ldconfig识别。

事情的起因为是这样的,我有一个程序P,通过ldd检查,它明确依赖libcrypto.so.1.0.0,我的系统中只有这几个so

libcrypto.so
libcrypto.so.1.1
libcrypto.so.1.1.1k

所以最开始的想着建立一个符号链接就可以了,因为我在系统中只有libcrypto.so.1.0.2k的时候成功过。所以我在/lib64目录中创建了一个符号链接:

[root@localhost certus]# ll /lib64/  | grep libcrypto
lrwxrwxrwx.  1 root root       19 Dec 18  2023 libcrypto.so -> libcrypto.so.1.1.1k
lrwxrwxrwx.  1 root root       26 Sep 23 05:17 libcrypto.so.1.0.0 -> /lib64/libcrypto.so.1.1.1k
lrwxrwxrwx.  1 root root       19 Dec 18  2023 libcrypto.so.1.1 -> libcrypto.so.1.1.1k
-rwxr-xr-x.  1 root root  3087888 Dec 18  2023 libcrypto.so.1.1.1k

然后这样并没有达到预期效果,我的程序P无法运行。我知道这个应该是版本太高的原因。

所以我从其它机器上复了较低版本libcrypt.so.1.0.2k,然后给它创建了符号链接libcrypt.1.0.0,最后运行一下程序,发现可以正常运行。本来事情到这里就可以结束了。但是我觉得直接在系统的/lib64目录中新文件的方式不太好,不如单独指定一个目录,然后通过ldconfig来更新依赖库。于是我就遇到了一个困扰我一天的问题。

操作流程如下:

  1. 新建一个目录/usr/local/lib64/foo
  2. libcrpyto.so.1.0.2k复制到/usr/local/lib64/foo目录中
  3. libcrypto.so.1.0.0符号链接复制到/usr/local/lib64/foo目录中
  4. 新建一个ldconfig配置文件/etc/ld.so.conf.d/foo.conf,内容为:/usr/local/lib64/foo
  5. 执行ldconfig 命令。

操作完上述流程后,我再次运行我的程序P,发现依然找不到libcrypto.so.1.0.0。那么为什么/usr/local/lib64/foo目录中的libcrypto.so.1.0.0这个符号没有被找到呢?通过ldconfig -v查看,发现ldconfig根据libcrypto.so.1.0.2k生成了libcrpyto.so.10这个符号,而这个符号是可以被识别的。而我自己手动创建的符号libcrypto.so.1.0.0却不能工作。我猜测肯定是有什么原因导致ldconfig没有识别我的符号链接。于是我又偿试不用符号链接,直接把libcrypt.so.1.0.2k重命名成libcrypto.so.1.0.0,但是这样依然不能生效。

通过调试,发现search cache=/etc/ld.so.cache里面就没有libcrypto.so.1.0.0

 LD_DEBUG=libs /path/to/progP

后来我在/lib64目录中创建符号链接libcrypto.so.1.0.0,这里的符号是可以被程序P找到的。

搜索了很多资料,也没有发现有什么能解释这个问题的。问了chatgpt,它跟我扯了一堆没用的,什么lib命名问题,权限的问题,优先级问题等等,都没有解决问题。

后来我想了一下,应该是和ldconfig的机制有关,想去看看ldconfig的源码,发现一时半会儿也理不清,就放弃了。但是我注意到ldconfig会根据libcrypto.so.1.0.2k自动生成符号链接libcrypto.so.10,为什么ldconfig自己生成的符号有效,而我手动创建的符号无效?我猜测ldconfig在搜索目录时,可能只会处理目录中有效的SONAME,我重新编译了一个 libcrypto.so.1.0.0,然后放入/usr/local/lib64/foo目录中,这次运行ldconfig就能识别这个库了。

经过上述偿试,我最终猜测ldconfg处理lib库的机制是:

  1. ldconfig遍历指定的目录,只搜索不是符号链接的lib*.so*文件。
  2. 从对应的lib*.so*文件提取SONAME如果有SONAME,那么就根据SONAME创建对应的符号链接,如果SONAME与文件名同名,则不用创建。把对应的SONAME加入ld.so.cache。如果没有SONAME,那么就把该库本身加入ld.so.cache

这样就可以解释两点:

  1. 直接在/usr/local/lib64/foo中手动创建符号链接不生效:因为ldconfig不处理符号链接。
  2. libcrypto.so.1.0.2k改名成libcrypto.so.1.0.0不生效:因为ldconfig会根据 libcrypto.so.1.0.0 中的 SONAME 生成对应的符号链接 libcrypt.so.10 所以只会把 libcrypto.so.10 加入 ld.so.cache

至于为何直接在 /lib64 中手动创建符号可以生效,我猜测这个和 ldconfig 没有关系,因为 /lib64 是进程默认的 so 加载路径。所以可以直接找。通过 LD_LIBRARY_PATH 也可以指定程序搜索对应目录的 so 文件,也同样不需要 ldconfig 的参于。