问题现象
同事突然找我,说新升级的一个c++的so库不能加载,同事A描述,在实体机上可以使用,在容器上不能执行。同事B描述,在容器上不能执行,但通过同事C给的一个命令执行后,在容器中可以执行。同事A给了一张截图,截图显示的现象是有两个文件通过ldd命令显示的是not found。
结合多个同事的描述,同时拉了一个腾讯会议,同时让同事A把新版本的c++的so库发我本地看一下。
最终问题现象为: 同事C在实体机上使用的是export LD_LIBRARY_PATH命令把so库目录加载到连接目录中取了,同事B在容器中使用的ld.so.conf文件进行的配置,同时,使用命令ldconfig命令进行加载,同时,使用ldd进行验证,也使用一个python脚本进行执行验证,得到的结果是缺少相应的两个库的依赖,ldd的时候,命令很明显的显示为not found,库文件名称为 SD_DBIO.tx 及另一个SD_DBEntity.tx文件。但使用了export LD_LIBRARY_PATH命令后,都能正常使用,无论ldd命令还是python脚本都可以正常执行。
问题初验证
根据上述同事描述,非常经典的依赖库找不到的问题,但问题又非常奇异,使用export LD_LIBRARY_PATH居然可以,而使用ldconfig加载配置文件居然找不到相应的依赖。于是,个人给出的初步解决方案就是,在容器中,启动的时候,增加一个环境变量export LD_LIBRRARY_PATH的命令执行。初步判断是ldconfig并没有很好的加载到相应的文件,可能是容器初始化的时候,当前的文件并没有加载到容器的目录下,或者是/etc/ld.so.conf文件修改并没有成功,或者容器挂载目录不成功。
根据会议的讨论,我本地也做了下验证,在我本机上使用ldconfig命令可以加载,此时,执行ldd的文件路径为当前文件所在目录,(系统应该会默认加载当前目录下的所有文件为连接目录),问题并没有出现not found的现象(此时还没有意识到要到别的目录下从其他目录进行ldd库文件的现象);
同时,联系了同事B,让同事演示了下他的具体的环境现象,确实是ldd后出现SD_DBIO.tx not found的现象,不仅仅是在容器中出现这个问题,同时,在实体机上使用ldconfig命令加载后,都会出现当前现象。
虽然有了解决方案,但问题的排查似乎陷入了僵局,没有排查到问题的真相,仅仅是解决了一个问题而已,下次有可能还会出现类似的问题,又要如何解决这样的问题啊?
曙光出现
问题就摆在面前,不能追根溯源的话,这个坑就会一直存在,说不定哪天就会有另一个相似的坑等着我们跳进去。从表象上看就是ldconfig加载当前文件失败。
百思不得其解。
突然,搜索ldconfig源码的时候,发现网络上有一篇文章写到,ldconfig文件有一个参数,可以看到add cache后的数据,同时,ldconfig -v 可以看到加载动态库的时候详细信息,于是,连接到服务器上,使用ldconfig -v命令进行查看,发现,确实是没能加载到SD_DBIO.tx文件和另一个文件,使用ldconfig -p 查看缓存中的文件,也是没有相应的缓存数据。
突然想到,在我本地系统的服务器上,并未出现not found的问题,是不是LD_LIBRARY_PATH默认加载了当前目录,于是,切换到前一个目录,再次执行ldd 命令,此时,出现了not found 。那么,问题已经非常明确了,ldconfig文件在执行的时候,并没有把当前目录下所有的文件加载到缓存中,而且,ldd命令执行时,只是从缓存中查找有没有相应的动态库。但export命令只是告知LD_LIBRARY_PATH环境变量中,你可以到当前目录去寻找相应的动态库,只要相应的文件名在这个目录下,连接程序就可以加载到当前文件。
问题出现了曙光,可以十分明确的确认ldconfig确实没有加载到相应的文件。问题定位到了,但为什么文件不能加载啊,问题快到了终点。
既然是文件没有加载到,那么,翻阅一下ldconfig的源码就可以定位到问题的所在。于是,查看代码,在linux系统中翻了翻,没有找到相应的代码,只好求助万能的网络,发现ldconfig在glibc的库中,下载glibc代码,搜索ldconfig文件,在elf目录找到了相应的文件:
看了看整体的代码,发现search_dir方法是对目录下的文件进行检索,向下翻阅,找到如下代码
if (((strncmp (direntry->d_name, lib, 3) != 0
&& strncmp (direntry->d_name, ld-, 3) != 0)
|| strstr (direntry->d_name, .so) == NULL)
&& (
#ifdef _DIRENT_HAVE_D_TYPE
direntry->d_type == DT_REG ||
#endif
!is_hwcap_platform (direntry->d_name)))
continue;
从上面的代码可以看到,文件不是以lib开头或不是ld-开头且没有.so字符的,都直接忽略,而我们的文件则正好符合这个规则。
附1:全文就附了一段代码,而这段代码好像还有点拗口,类似中文中双重否定就是肯定,这里,符合ldconfig加载规则的是: 以lib开头,或者是以ld-开头的文件,且文件名中有.so字符串的,也符合规则。文件中只有.so字符的,也符合规则。但只有lib/ld-开头的文件名,但不包含.so字符串不符合规则。
附2:问题也算完美解决,有了一个比较好的结局。不过,最后发现了一个比较有趣的现象,修改文件名后,使用ld.so.conf文件增加环境变量,再用ldconfig文件,发现,对应的SD_DBIO.tx文件又出现了,使用ls -l命令查看,此时的文件是一个软连接,指向的是修改过名称后的文件。之前在排查问题时,曾使用readelf命令查看过当前文件的头(readelf -d 命令),文件的动态连接名称(library soname )是SD_DBIO.tx,使用ldconfig后,会增加相应的软连接。方法见create_links。在cache中,显示的也是SD_DBIO.tx的库名,并指向对应的文件目录及文件名。