用python脚本查找.so库文件中的符号


最近在写Makefile的时候总是报找不到符号的错误,比如:

/usr/bin/ld: /tmp/ccnwoS4i.o: undefined reference to symbol '_ZN4Ogre8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE'
/usr/bin/ld: /usr/lib/libOgreRTShaderSystem.so.1.12.6: error adding symbols: DSO missing from command line

遇到长这样的错误一般就是在链接的时候少链接了库文件,只要链接对应的库就好,比如这个错误就可以通过编译时加上-lOgreRTShaderSystem修复。

因此,在出现这类错误时有必要查找到底少链接了哪一个库文件。

预备知识

Linux动态链接库的命名规则及存放/寻找路径

Linux系统中动态链接库是.so(Shared Object)文件,其默认存放位置为/usr/lib//lib/

其命名规则一般是lib+库名+.so,例如库OgreRTShaderSystem所对应的库文件为libOgreRTShaderSystem.so。而在gcc编译的时候传入的是库名而不是库文件名,例如,在编译时要链接库OgreRTShaderSystem,则在gcc中要以以下方式调用:

g++ test.cpp -o test -lOgreRTShaderSystem

gcc将会在以以下顺序寻找libOgreRTShaderSystem.so这个库文件:

  1. 编译目标代码时指定的动态库搜索路径;
  2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
  3. 配置文件/etc/ld.so.conf.d/*中指定的动态库搜索路径;
  4. 默认的动态库搜索路径/lib;
  5. 默认的动态库搜索路径/usr/lib。

Linux中搜索文件

在Linux中可以用find命令来搜索文件,这里以Manjaro系统为例。

Linux find 命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时,不设置任何参数,则 find 命令将在当前目录下查找子目录与文件。并且将查找到的子目录和文件全部进行显示。

一般语法为:

find   path   -option

其中常用参数有:

  • -amin n : 在过去 n 分钟内被读取过
  • -atime n : 在过去n天内被读取过的文件
  • -ipath p, -path p : 路径名称符合 p 的文件,ipath 会忽略大小写
  • -name name, -iname name : 文件名称符合 name 的文件。iname 会忽略大小写
  • -size n : 文件大小 是 n 单位,b 代表 512 位元组的区块,c 表示字元数,k 表示 kilo bytes,w 是二个位元组
  • -type c : 文件类型是 c 的文件

这里我们用到-name参数,我们用以下命令来查找以libOgre开头的.so库文件:

find /usr/lib/ -name 'libOgre*.so'

Linux浏览.so库文件中的符号列表

在Linux中可以用nm命令来列出指定库文件中的符号列表。

nm是names的缩写, nm命令主要是用来列出某些文件中的符号(说白了就是一些函数和全局变量等)。

这里我们会用到-D(–dynamic)参数,其用于显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。

例如查找/usr/lib/libOgreBites.so中的符号:

nm -D /usr/lib/libOgreBites.so

其结果类似下面这样:

0000000000046020 B __bss_start
                 U commandWidgetClass
                 U __cxa_allocate_exception
                 U __cxa_atexit
                 U __cxa_begin_catch
                 U __cxa_call_unexpected
                 U __cxa_end_catch
                 w __cxa_finalize
                 U __cxa_free_exception
                 U __cxa_guard_abort
                 U __cxa_guard_acquire
                 U __cxa_guard_release
                 U __cxa_rethrow
                 U __cxa_throw
                 U __dynamic_cast
0000000000046020 D _edata
       ...
       ...
0000000000039160 u _ZZNSt8__detail18__to_chars_10_implIjEEvPcjT_E8__digits
00000000000380e0 u _ZZNSt8__detail18__to_chars_10_implImEEvPcjT_E8__digits

可以用grep在结果中查找含有指定符号(_ZN4Ogre8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE)的行:

nm -D /usr/lib/libOgreBites.so | grep _ZN4Ogre8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE

其结果如下:

e8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE
                 U _ZN4Ogre8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE

用Python调用shell命令

在Python中可以通过subprocess模块实现调用shell命令。其方法是直接调用Popen()函数:

import subprocess
child = subprocess.Popen('ls ~ -a', shell=True)

在前后进程有顺序要求的情况下需要加上wait()这一过程来确保后一过程在前一过程完成后开始:

child.wait()

而在subprocess中运行shell命令返回的结果如果要存到变量中,则需要指定Popen()stdout参数为subprocess.PIPE。并且要用communicate()来从subprocess.PIPE中读取结果:

child = subprocess.Popen('ls ~ -a', shell=True, stdout=subprocess.PIPE)
child.wait()
result_binary = child.communicate()

这里的result是一个元组,其第一个元素为保存了运行结果的二进制串,因此需要对其进行解码:

result = result_binary[0].decode("utf-8")

完整程序

import subprocess

sub = subprocess.Popen('find /usr/lib/ -name \'libOgre*.so\'', shell=True, stdout=subprocess.PIPE)
sub.wait()
out = sub.communicate()
res = out[0].decode("utf-8").split('\n')
for item in res:
    if len(item):
        tmp_sub = subprocess.Popen('nm -D {} | grep _ZN4Ogre8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE'.format(item), shell=True, stdout=subprocess.PIPE)
        tmp_sub.wait()
        tmp = tmp_sub.communicate()[0]
        if tmp:
            print('file: {}'.format(item))
            print('\t{}'.format(tmp))

运行结果:

find: ‘/usr/lib/firmware/b43legacy’: Permission denied
find: ‘/usr/lib/firmware/b43’: Permission denied
file: /usr/lib/libOgreRTShaderSystem.so
        b'00000000000c6fa0 T _ZN4Ogre8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE\n'
file: /usr/lib/libOgreBites.so
        b'                 U _ZN4Ogre8RTShader15ShaderGenerator15addSceneManagerEPNS_12SceneManagerE\n'

观察结果发现应该是没有链接libOgreRTShaderSystem.so

参考链接:
gcc从哪里寻找头文件和库文件
Linux find命令
linux中的nm命令简介

猜你喜欢

转载自blog.csdn.net/willian113/article/details/106963940