linux上编译生成so动态链接库之后安装或者使用时使用ldd查看so文件链接其他so文件时显示not found的解决方案

这一段一直在整插件管理框架的事情,整个框架分成几层:

(1)开源的class_loader生成so文件,作为插件生成、加载等的基础;

(2)插件基类,包括标签生成器基类、传感器数据采集基类,依赖class_loader,目的是为具体的插件提供头文件;

(3)标签生成器插件,用户自己实现genLabel函数,通过用户逻辑来控制输出的标签的内容、长度等,这个插件依赖于class_loader和插件基类;

(4)传感器数据采集插件,也是以class_loader为基础,同时依赖标签生成器和插件基类;

(5)插件管理框架,将所有的插件管理起来,实际上直接依赖于class_loader和插件基类。

现在按照上述层次关系,依次编译安装。但在使用过程中,例如对于插件管理框架,用户想着我只是用你的插件管理框架,我引入该框架的.so文件即可,至于你依赖谁,我并不关心,我也不应该知道,我更不应该引入这些so文件。这是因为在默认情况下,库依赖项是传递的。当目标A 链接到目标B 时,链接到A 的库也会出现B 的链接线上。

上述几个工程都在同一个目录下,属于平级关系,如下图所示。

我们在编译完插件管理框架后,使用ldd查看链接关系,发现所有库都是链接上的。

对应的cmakelists文件如下:

cmake_minimum_required(VERSION 3.4.2)

project(pluginManager CXX)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic -pthread -lboost_filesystem)
endif()

include(../micros.cmake)
set(LINK_DIR ./lib ) # can not be obsolute path
set(CONSOLE_DIR /lib/x86_64-linux-gnu/)
set(POCO_DIR /usr/lib/x86_64-linux-gnu/)
set(CLASS_LOADER_LIB /opt/micros/1.0.1/third-party/class_loader/lib)
message("aaaaaaaaaa" ${CMAKE_INSTALL_PREFIX})
set(${PROJECT_NAME}_SRCS src/pluginManager.cpp)
set(${PROJECT_NAME}_HDRS include/micros_pluginmanager/pluginManager.h)

include_directories( ./include /usr/include)

include_directories(/opt/micros/1.0.1/include)
include_directories(${CMAKE_INSTALL_PREFIX}/third-party/class_loader/include)
link_directories(${CMAKE_INSTALL_PREFIX}/lib 
#${CMAKE_INSTALL_PREFIX}/third-party/class_loader/lib
#${CLASS_LOADER_LIB}
 ${CMAKE_TMP_OUTPUT_SO_PATH}
 )
SET(LIBRARY_OUTPUT_PATH ${CMAKE_TMP_OUTPUT_SO_PATH})

link_directories(${LINK_DIR}
                 ${CONSOLE_DIR} 
                 ${POCO_DIR} 
                /opt/micros/1.0.1/lib/plugins
                 ${CLASS_LOADER_LIB})

#插件基类对应的so文件
add_library(label_sensor.0.1.0 SHARED IMPORTED)
set_property(TARGET label_sensor.0.1.0 PROPERTY IMPORTED_LOCATION "/opt/micros/1.0.1/lib/plugins/liblabel_sensor.0.1.0.so")
#class_loader对应的so文件
add_library(libmicros_scene_classloader.0.1.0 SHARED IMPORTED)
set_property(TARGET libmicros_scene_classloader.0.1.0 PROPERTY IMPORTED_LOCATION "/opt/micros/1.0.1/third-party/class_loader/lib/libmicros_scene_classloader.0.1.0.so")


add_library(micros_scene_pluginmanager.0.1.0 SHARED ${${PROJECT_NAME}_SRCS} ${${PROJECT_NAME}_HDRS})

target_link_libraries(micros_scene_pluginmanager.0.1.0  optimized
  label_sensor.0.1.0
  micros_scene_classloader.0.1.0
  
) 
#安装so文件到/opt/xxx/lib/目录下
install(TARGETS micros_scene_pluginmanager.0.1.0 LIBRARY DESTINATION lib/)
install(DIRECTORY include/  DESTINATION include/) 

当我们安装后,也就是执行sudo make install之后,发现插件基类库和class_loader库并没有链接上去:

查了不少原因,尝试了不少方法还是不行。最后,我们思考一下,安装前后的区别是什么?是搜索路径:

问题

如果ldd命令没有找到对应的共享库文件和其具体位置?

可能是两种情况引起的:

1)共享库没有安装在该系统中;

2)共享库保存在/etc/ld.so.conf文件列出的搜索路径之外的位置。

通常情况下,许多开源代码的程序或函数库都会默认将在即安装到/usr/local目录下的相应位置(如:/usr/local/bin 或 /usr/local/lib)以便于系统自身的程序或函数库相区别。而许多linux系统的/ect/ld.so.conf 文件中默认又不包含 /usr/local/lib 。因此出现安装了共享库,但是却无法找到共享库的情况。

解决办法:
检查/etc/ld.so.conf文件,如果其中缺少自己编译的被依赖的库的目录,就添加进去;
注意:在修改了/etc/ld.so.conf 文件或者在系统中安装了新的函数库之后,需要运行命令 ldconfig ,该命令用来刷新系统的共享库缓存,即 /etc/ld.so.cache 文件。为了减少共享库系统的库搜索时间,共享库系统维护了一个共享库so名称的缓存文件 /etc/ld.so.cache 。 因此,在安装新的共享库之后,一定要运行 ldconfig刷新该缓存。

我们添加插件基类库和class_loader库文件所在路径到/etc/ld.so.conf文件中,如下图所示:

然后执行sudo /sbin/ldconfig使上述修改生效。

重新安装插件管理框架库文件,然后ldd查看:

至此,链接库找不到的问题解决。

虽然上面的问题解决了,但是给我留下一个更大的疑惑

疑惑:

为什么在生成so文件的地方使用ldd查看依赖关系的时候,这些库是能找到的呢?如果说当前路径是默认添加到搜索路径的,我可以理解,但是,pluginmanager.so依赖于label_sensor.so和class_loader.so,但它们俩并不在当前目录下!!!

1. 测试将make生成so文件移动位置到其他地方,看能否链接成功

我可以将so文件移动到home/ok/下的任意位置,ldd查看都是能链接上的!

移动到/home/ok/code/下

移动到/home/ok/下,该文件仍然属于ok用户。

将so文件移动到/home/下

将so文件移动到/var下:

移动到/opt/micros/1.0.1/lib/下:

链接没问题,这就意味着make出来的so文件,是记录了它所依赖的其他so文件的路径信息的。

2. 测试将make install后的so文件移动到其他地方(已经将搜索路径从/etc/ld.so.conf文件中删除)

我们将它移动到/var目录下:

我们把安装后的so文件复制到/home/ok/下,然后查看(用户是root):

我们发现,将安装后的so文件移动到任何一个位置,都无法找到依赖包。

上述两个文件的区别仅仅是文件的属主,一个是ok,一个是root。

那么,使用make生成so文件时,依赖库的路径信息到底有么有被放到目标so中呢?如果放进去了,这个好理解,该文件可以随便移动,但是存在弊端:如果你依赖的库文件移动位置了,那就肯定找不到了。我们验证一下,把label_sensor.so从lib/plugins下移动到third-party/class_loader/lib下。(这俩路径目前都不在系统的/etc/ld.so.conf文件中)

再挪回来:

我们发现,labe_sensor.so虽然更换了路径,但是仍然能找到,这说明make生成so文件时,依赖库的路径并没有被放到so文件中。

那么我们思考一下,是不是每一个用户都有一个搜索路径呢?

如果每个用户都有一个搜索路径,那我把label_sensor.so移动到一个其他位置,是否还有效呢?

我们将label_sensor.so移动到/opt/micros/下,然后查看:

我们发现,找不到label_sensor.so,这也就说明(1)so文件中不记录所依赖的其他so文件的路径,只记录so文件名;(2)有可能,每个用户都有一个搜索路径,并且在执行CMakelists.txt对应的make的时候,会把link_directories和add_library添加的依赖路径添加到执行make命令的用户的搜索路径下;(3)有可能每一个so文件里面都包含了一个搜索路径,就是cmakelists.txt中的link_directories和add_library以及target_link_libraries引入的路径,当使用so或者ldd该so文件的时候,会根据so文件里面包含的路径去查找,但这样的话,即使切换用户,应该也能找到!所以这个成立的可能性很小!!!

3. 使用sudo make来编译so文件

生成so文件:

然后安装该文件:

发现不行,仍然找不到路径。

我们使用sudo cmake ..; sudo make 再来一遍:

sudo make install:

仍然不行,现在我们将sudo cmake ..和sudo make得到的so文件复制到/opt/micros/1.0.1/lib/下:

发现可以了,这说明是sudo make install破坏了原来的依赖关系!!!

这说明前面的第二条推论也可能是错误的!!!也就是并不是在cmake ..和make的时候,会将相应的路径添加到用户的搜索路径中?但是如果这样的话,怎么解释make出来的so文件随便移动,其都能找到依赖的so文件,并且其依赖的so文件移动到某些路径下(生成目标so时的link_dictories和add_library以及target_link_libraries指定的路径下)仍然能找到呢?

是不是生成目标so文件时,附带了对应该so的cmakelists中的link_dictories和add_library以及target_link_libraries的路径呢?也就是前面的推论(3)可能是成立的!但是sudo make install破坏了这种关系。

我们查看以下make和make install后的文件大小的区别:

上面的是复制过来的,下面的是安装过来的,大小一致,唯一的区别是make得到的有可执行权限。

为什么make出来的so文件的库依赖是没有问题的,而make install出来的库文件需要添加依赖库的路径到/etc/ld.so.conf文件中呢?

限于篇幅问题,我在下一个blog中继续探索!

有一点需要说明,添加到/etc/ld.so.conf文件中的搜索路径必须是so文件的直接父路径,不能是祖先路径。貌似ldd不会便利某路径下的子目录。

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值