ctypes 加载具有依赖关系的 c 共享库

Posted

技术标签:

【中文标题】ctypes 加载具有依赖关系的 c 共享库【英文标题】:ctypes loading a c shared library that has dependencies 【发布时间】:2011-01-20 14:31:02 【问题描述】:

在 Linux 上,我有一个依赖于其他库的 c 共享库。 LD_LIBRARY_PATH 已正确设置以允许链接器加载所有库。当我这样做时:

libgidcwf    = ctypes.cdll.LoadLibrary(libidcwf_path)

我收到以下错误:

Traceback (most recent call last):
  File "libwfm_test.py", line 12, in <module>
    libgidcwf    = ctypes.cdll.LoadLibrary(libidcwf_path)
  File "/usr/lib/python2.5/ctypes/__init__.py", line 431, in LoadLibrary
    return self._dlltype(name)
  File "/usr/lib/python2.5/ctypes/__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: path-to-my-lib/libwav.so: undefined symbol: ODBCGeneralQuery

似乎 LD_LIBRARY_PATH 在这里不起作用。 有没有办法让这些依赖库“可加载”?

提前感谢您的帮助。

【问题讨论】:

您使用的是什么操作系统?请参阅tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html——在 HpUx 中是 SHLIB_PATH,在 Aix 中是 LIBPATH,在 Mac 上是 DYLD_lotsofthings……语义也有细微的不同。即使是 Linux,请澄清版本并适当标记您的 Q,谢谢! 我在Linux上,所以我使用LD_LIBRARY_PATH,但ctypes似乎没有使用它 【参考方案1】:

似乎 libwav.so 没有声明它依赖于定义 ODBCGeneralQuery 的库。尝试运行ldd path-to-my-lib/libwav.so 并查看是否缺少某些内容。如果这是您正在构建的共享库,您应该将-llibname 添加到库代码使用的每个库的链接命令(类似于gcc -shared -o libwav.so a.o b.o c.o)中。以这种方式被原始共享库引用的任何其他库也应自动加载。

【讨论】:

如果您能详细说明“缺少的东西”,我将不胜感激。我在我的 .so 上运行 ldd - 但我应该看到什么? 我想我的意思不清楚。我的意思是 ldd 将显示 libwav.so 引用的库,并且应该检查是否存在应该列出但没有列出的库。 非常感谢您真正澄清这个四年前的答案。我发现ldd 的输出有点令人困惑,但您的回答有助于澄清它。谢谢。【参考方案2】:

您应该使用RTLD_GLOBAL。我有一个混合平台系统,所以我的代码如下所示:

import numpy, ctypes
try:
  if "Linux" in esmfos:
    _ESMF = ctypes.CDLL(libsdir+'/libesmf.so',mode=ctypes.RTLD_GLOBAL)
  else:
    _ESMF = numpy.ctypeslib.load_library('libesmf',libsdir)
except:
  traceback.print_exc(file=sys.stdout)
  sys.exit(ESMP_ERROR_SHAREDLIB)

【讨论】:

【参考方案3】:

我发现我必须使用 RTLD_LAZY,因为未定义的符号未链接,因为它没有被使用。由于我的 ctypes 中没有 ctypes.RTLD_LAZY,我不得不使用:

ctypes.CDLL(libidcwf_path, mode=1)

我通过检查/usr/include/bits/dlfcn.h 发现了这种模式,这可能不是标准的。向 ctypes 邮件列表上的这个 2006 thread 致敬。

【讨论】:

在 Python 3 中使用 from posix import RTLD_LAZYos.RTLD_LAZY,正如 Rookie 在另一个答案中提到的那样。我还没有在 Python 2 中找到这个常量。【参考方案4】:

编译共享对象的时候,一定要把所有的-lsomething放在字符串命令的最后。对我来说,它解决了问题。

【讨论】:

【参考方案5】:

我遇到了同样的问题。 为了解决这个问题,需要做两件事:

    如其他用户所说,使用RTLD_GLOBAL 您需要加载您的库使用的每个库。所以如果ODBCGeneralQuery 定义在libIDCodbc 中,你需要先运行这一行:

ctypes.CDLL("libIDCodbc.so", mode = ctypes.RTLD_GLOBAL)

【讨论】:

RTLD_GLOBAL 到底是做什么的?是不是说“如果你加载一堆 .so 文件,把所有的符号放到一个巨大的命名空间中”?【参考方案6】:

基于上面Walter Nissen的answer,可以修改代码为:

import os
ctypes.CDLL(libidcwf_path, mode=os.RTLD_LAZY)

【讨论】:

以上是关于ctypes 加载具有依赖关系的 c 共享库的主要内容,如果未能解决你的问题,请参考以下文章

使用 ctypes/cffi 解决循环共享对象依赖关系

Ctypes找不到我加载的dll的dll依赖

JAVA加载一个目录下有依赖关系本地库的通用代码

将具有外部依赖项的共享库集成到 MATLAB |即犰狳、LAPACK、BLAS

Python ctypes加载错误:未定义的符号

使用 ctypes 在 Python 中解码 C const char*