关于linux:Linux-动态库-undefined-symbol-原因定位与解决方法

12次阅读

共计 4970 个字符,预计需要花费 13 分钟才能阅读完成。

在应用动静库开发部署时,遇到最多的问题可能就是 undefined symbol 了,导致这个呈现这个问题的起因有多种多样,疾速找到起因,采纳对应的办法解决是本文写作的目标。

可能的起因

  1. 依赖库未找到
    这是最常见的起因,个别是没有指定查找目录,或者没有装置到零碎查找目录里
  2. 链接的依赖库不统一
    编译的时候应用了高版本,而后不同机器应用时链接的却是低版本,低版本可能缺失某些 api
  3. 符号被暗藏
    如果动静库编译时被默认暗藏,内部代码应用了某个被暗藏的符号。
  4. c++ abi 版本不统一
    最典型的例子就是 gcc 4.x 到 gcc 5.x 版本之间的问题,在 4.x 编辑的动静库,不能在 5.x 中链接应用。

解决办法

1. 依赖库未找到

  • 应用 ldd -r <lib-file-name>, 确定零碎库中是否存在所依赖的库
  • 执行 ldconfig 命令更新 ld 缓存
  • 执行 ldconfig -p | grep {SO_NAME} 查看是否能找到对应的库
  • 查看 LD_LIBRATY_PATH 是否设置了无效的门路

2. 链接的库版本不统一
如果零碎中之前有装置过雷同的库,或者存在多个库,就须要确定链接的具体是哪个库

有一个非凡场景须要留神下,.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-pag…

   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)
         ...
    
  2. 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*)
正文完
 0