在使用动态库开发部署时,遇到最多的问题可能就是 undefined symbol 了,导致这个出现这个问题的原因有多种多样,快速找到原因,采用对应的方法解决是本文写作的目的。

可能的原因

  1. 依赖库未找到

    这是最常见的原因,一般是没有指定查找目录,或者没有安装到系统查找目录里
  2. 链接的依赖库不一致

    编译的时候使用了高版本,然后不同机器使用时链接的却是低版本,低版本可能缺失某些 api
  3. 符号被隐藏

    如果动态库编译时被默认隐藏,外部代码使用了某个被隐藏的符号。
  4. c++ abi 版本不一致

    最典型的例子就是 gcc 4.x 到 gcc 5.x 版本之间的问题,在 4.x 编辑的动态库,不能在 5.x 中链接使用。

解决方法

  1. 依赖库未找到
  • 使用 ldd -r , 确定系统库中是否存在所依赖的库
  • 执行 ldconfig 命令更新 ld 缓存
  • 执行 ldconfig -p | grep {SO_NAME} 查看是否能找到对应的库
  • 检查 LD_LIBRATY_PATH 是否设置了有效的路径
  1. 链接的库版本不一致

    如果系统中之前有安装过相同的库,或者存在多个库,就需要确定链接的具体是哪个库

有一个特殊场景需要注意下,.so 文件中有个默认 rpath 路径,用于搜索被依赖的库,这个路径优先于系统目录和LD_LIBRARY_PATH。假如 rpath 存在相同名字的 .so 文件,会优先加载这个路径的文件。

在遇到 undefined symbol 问题时,使用 readelf -d | grep rpath 查看:

$ readelf -d libSXVideoEngineJni.so | grep rpath
0x000000000000000f (RPATH) Library rpath:
[/home/slayer/workspace/SXVideoEngine-Core/Render/cmake-build-
debug:/home/slayer/workspace/SXVideoEngine-Core/Render/../../SXVideoEngine-Core-Lib/blend2d/linux/lib]

如果存在的路径中有相应的库,可以先重命名文件再测试确认。

关于连接时的顺序可以查看文档: http://man7.org/linux/man-pages/man8/ld.so.8.html

    If a shared object dependency does not contain a slash, then it is
searched for in the following order: o Using the directories specified in the DT_RPATH dynamic section
attribute of the binary if present and DT_RUNPATH attribute does
not exist. Use of DT_RPATH is deprecated. o Using the environment variable LD_LIBRARY_PATH, unless the
executable is being run in secure-execution mode (see below), in
which case this variable is ignored. o Using the directories specified in the DT_RUNPATH dynamic section
attribute of the binary if present. Such directories are searched
only to find those objects required by DT_NEEDED (direct
dependencies) entries and do not apply to those objects' children,
which must themselves have their own DT_RUNPATH entries. This is
unlike DT_RPATH, which is applied to searches for all children in
the dependency tree. o From the cache file /etc/ld.so.cache, which contains a compiled
list of candidate shared objects previously found in the augmented
library path. If, however, the binary was linked with the -z
nodeflib linker option, shared objects in the default paths are
skipped. Shared objects installed in hardware capability
directories (see below) are preferred to other shared objects. o In the default path /lib, and then /usr/lib. (On some 64-bit
architectures, the default paths for 64-bit shared objects are
/lib64, and then /usr/lib64.) If the binary was linked with the
-z nodeflib linker option, this step is skipped.

符号被隐藏

第三方已经编译好的库,在引入了对应的头文件,使用了其中的某个方法,最终链接的时候出现 undefined symbol,这种情况有可能是库的开发者并没有导出这个方法的符号。

# 使用 nm 命令查看导出的函数符号, 这里查看 License 相关的函数
$ nm -gDC libSXVideoEngineJni.so | grep -i license
0000000000008110 T __ZN13SXVideoEngine6Public7License10SetLicenseEPKc
0000000000008130 T __ZN13SXVideoEngine6Public7License13LicenseStatusEv
0000000000008190 T __ZN13SXVideoEngine6Public7License19IsVideoCutSupportedEv
0000000000008170 T __ZN13SXVideoEngine6Public7License26IsDynamicTemplateSupportedEv
0000000000008150 T __ZN13SXVideoEngine6Public7License26IsStadardTemplateSupportedEv # nm 返回的并不是原始函数名,通过 c++filt 获取原始名称
$ c++filt __ZN13SXVideoEngine6Public7License10SetLicenseEPKc
SXVideoEngine::Public::License::SetLicense(char const*)

c++ Abi 版本不一致

Gcc 对 c++ 的新特性是一步一步的增加的,如果实现了新的特性,就可能会修改 c++ 的 abi,并且会升级 glibc 的版本。

Abi 链接最常见的错误是 std::string 和 std::list 的在gcc 4.x 和 gcc 5.x 的不同实现引起的。在gcc 4.x 时,gcc 对标准 string 的实现就放在 std 命名空间下,编译时展开为 std::basic_string 。但是 gcc 5.x 开始,对 string 的实现就放在了 std::__cxx11空间里,编译后展开为 std::__cxx11::basic_string 。这就会导致在 gcc 4.x 编译的动态库,假如有的函数使用了 string 作为参数或者返回值,这时导出的函数参数为 std::basic_string 类型。 无法在 gcc 5.x 下编译连接使用。

错误类似:

undefined symbol:  "std::__cxx11 ***"

这种情况有一个折中办法就是在gcc 5.x 或以上 编译时,增加 -D_GLIBCXX_USE_CXX11_ABI=0 禁用 c++11 abi。

当然最好的做法就是保证编译器大版本基本一致。在新开发的程序如果用到了 c++ 的新特性,升级 gcc 版本和 glibc 是十分必要的。

实用命令总结

  1. ldd 命令,用于查找某个动态库所依赖的库是否存在
# ldd -r <lib/excutable file>
# 找不到的库会出现 not found
$ ldd -r libSXVideoEngine.so
linux-vdso.so.1 => (0x00007ffc337d2000)
libz.so.1 => /lib64/libz.so.1 (0x00007f061cf41000)
libX11.so.6 => /lib64/libX11.so.6 (0x00007f061cc03000)
libEGL.so.1 => /lib64/libEGL.so.1 (0x00007f061c9ef000)
libGLESv2.so.2 => /lib64/libGLESv2.so.2 (0x00007f061c7dd000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f061c5c1000)
libblend2d.so => /home/seeshion/workspace/SXVideoEngine-Core/Render/../../SXVideoEngine-Core-Lib/blend2d/linux/lib/libblend2d.so (0x00007f061c187000)
libfreeimage.so.3 => /lib/libfreeimage.so.3 (0x00007f061b8ac000)
libavcodec.so.58 => /lib/libavcodec.so.58 (0x00007f06198b6000)
libavformat.so.58 => /lib/libavformat.so.58 (0x00007f06193e1000)
libavutil.so.56 => /lib/libavutil.so.56 (0x00007f06190bd000)
...
  1. nm 命令,用于读取库被导出的符号
$ nm -gDC libSXVideoEngineJni.so | grep -i license
0000000000008110 T __ZN13SXVideoEngine6Public7License10SetLicenseEPKc
0000000000008130 T __ZN13SXVideoEngine6Public7License13LicenseStatusEv
0000000000008190 T __ZN13SXVideoEngine6Public7License19IsVideoCutSupportedEv
0000000000008170 T __ZN13SXVideoEngine6Public7License26IsDynamicTemplateSupportedEv
0000000000008150 T __ZN13SXVideoEngine6Public7License26IsStadardTemplateSupportedEv
  1. readelf 用于读取 elf 文件的相关信息
$ readelf -d libSXVideoEngineJni.so | grep rpath
0x000000000000000f (RPATH) Library rpath:
[/home/slayer/workspace/SXVideoEngine-Core/Render/cmake-build-
debug:/home/slayer/workspace/SXVideoEngine-Core/Render/../../SXVideoEngine-Core-Lib/blend2d/linux/lib]
  1. c++filt 用于获取符号的原始名
$ c++filt __ZN13SXVideoEngine6Public7License10SetLicenseEPKc
SXVideoEngine::Public::License::SetLicense(char const*)

Linux 动态库 undefined symbol 原因定位与解决方法的更多相关文章

  1. Window下通过CuteFTP与Linux虚拟机连接失败的原因总结及解决方法

    环境:虚拟机类型Redhat Enterprise 6 (1) 虚拟机是否安装了ftp服务器? 若未安装,则yum install vsftpd,并启动ftp服务 (2) 虚拟机防火墙是否已关闭? 若 ...

  2. c编译动态库可以编译但是无法导入解决方法

    $(CC) $(CFLAGS) -Wl,--whole-archive ${libusb} -Wl,--no-whole-archive $(lib_objs) $(LFLAGS) -o $(lib) ...

  3. .netcore在linux下使用P/invoke方式调用linux动态库

    http://www.mamicode.com/info-detail-2358309.html   .netcore下已经实现了通过p/invoke方式调用linux的动态链接库(*.so)文件 1 ...

  4. linux动态库加载RPATH, RUNPATH

    摘自http://gotowqj.iteye.com/blog/1926771 linux动态库加载RPATH, RUNPATH 链接动态库 如何程序在连接时使用了共享库,就必须在运行的时候能够找到共 ...

  5. linux 动态库 静态库 函数覆盖

    本文讨论了linux动态库  静态库中函数的覆盖问题. 测试目的: 同名函数,分别打成动态库libdync_lib.so与静态库libstatic_lib.a,并把libstatic_lib.a打到另 ...

  6. Linux动态库(.so)搜索路径

    主要内容: 1.Linux动态库.so搜索路径 编译目标代码时指定的动态库搜索路径: 环境变量LD_LIBRARY_PATH指定的动态库搜索路径: 配置文件/etc/ld.so.conf中指定的动态库 ...

  7. Linux动态库的导出控制

    在实际工作中,许多软件模块是以动态库的方式提供的.做为模块开发人员,我们不仅要掌握如何编写和构建动态库,还要了解如何控制动态库的导出接口,这样,我们可以向模块的用户仅导出必要的接口,而另一些内部接口, ...

  8. linux动态库编译和使用详细剖析 - 后续

    引言 - 也许是修行 很久以前写过关于动态库科普文章, 废话反正是说了好多. 核心就是在 linux 上面玩了一下 dlopen : ) linux动态库编译和使用详细剖析 - https://www ...

  9. Linux动态库搜索路径的技巧

    众所周知,Linux动态库的默认搜索路径是/lib和/usr/lib.动态库被创建后,一般都复制到这两个目录中.当程序执行时需要某动态库,并且该动态库还未加载到内存中,则系统会自动到这两个默认搜索路径 ...

随机推荐

  1. LTP--linux稳定性测试 linux性能测试 ltp压力测试 ltp-pan

    LTP--linux稳定性测试 linux性能测试 ltp压力测试 zhangzj1030关注14人评论33710人阅读2011-12-09 12:07:45   说明:在写这篇文章之前,本人也不曾了 ...

  2. 转载 https://www.cnblogs.com/bobo-pcb/p/11708459.html

    https://www.cnblogs.com/bobo-pcb/p/11708459.html #### 1 用VMware 15.0+win10企业版 1次安装成功 20200124 2 不要用v ...

  3. Bash shell的特性

    bash的特性 命令补全 Tab键 ​ 命令补全 ​ 路径补全 ​ 选项补全 ​ yum install -y bash-completion ​ -长格式 ​ --all ​ -短格式 ​ -a 快 ...

  4. linux进阶之yum管理

    一.部署私有repo源 1.官网下载需要的仓库: rsync -avrt --delete rsync://mirrors.ustc.edu.cn/centos/7/cloud/x86_64/open ...

  5. gpgj-12 ROIC估值法总结

    1.公司甲乙丙 ·公司的分类: 1.ROIC  <    10%的公司        不赚钱的公司,价值摧毁者,避免投资这种 2.ROIC 约等于 10%的公司        勉强能够糊口,EV ...

  6. KEIL-C下数组指针与指针数组实验

    http://blog.csdn.net/men_wen/article/details/52694069 第一个: 数组指针的小实验 用指针传递参数 结果:  第二个: 数组指针实验 定义一个指针 ...

  7. vue+element-ui, el-upload组件 文件上传之前return false,会自动调用文件移除回调问题

    日常搬砖的时候,项目中在使用element-ui的上传组件,但是当我在文件上传文件之前的回调里面做了些文件格式的二次校验和文件大小的校验的时 然后 return false 会发现调用 文件移除的回调 ...

  8. MindSpore模型精度调优实践

    MindSpore模型精度调优实践 引论:在模型的开发过程中,精度达不到预期常常让人头疼.为了帮助用户解决模型调试调优的问题,为MindSpore量身定做了可视化调试调优组件:MindInsight. ...

  9. springMVC源码解读笔记

    1: DispatcherServlet 的初始化流程(调用的init方法) a) 初始化spring高级容器,WebApplicationContext(容器初始化12个步骤) Servlet类的i ...

  10. 单点突破:MySQL之索引

    前言 开发环境:MySQL5.7.31 什么是索引 在MySQL中,索引(Index)是帮助高效获取数据的数据结构. 我们可以将数据库理解为一本书,数据库中的各个数据列(column)就是目录中的章节 ...