使用ctypes加载具有依赖项的c共享库

27

在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 在这里没有效果。 有没有一种方法使这些依赖库“可加载”?

提前感谢您的帮助。


你用的是哪个操作系统?参见http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html -- 在HpUx中是SHLIB_PATH,在Aix中是LIBPATH,在Mac上是DYLD_lotsofthings...语义也略有不同。即使是Linux,请明确版本并适当标记您的问题,谢谢! - Alex Martelli
1
我正在使用Linux,因此我使用LD_LIBRARY_PATH,但ctypes似乎没有使用它。 - zoobert
6个回答

18

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


如果您能详细说明“something missing”,我将不胜感激。我在我的.so文件上运行了ldd命令,但我应该看到什么? - Bex
我想我的意思不太清楚。我的意思是ldd将显示libwav.so引用的库,应该检查这些库,看看是否有应该列出但未列出的库。 - Geoff Reedy
非常感谢您澄清这个四年前的答案。我发现ldd的输出有点令人困惑,但是您的回答帮助澄清了这一点。谢谢。 - Bex

8

我发现由于一个未被使用的未链接符号,我必须使用RTLD_LAZY。由于在我的ctypes中没有ctypes.RTLD_LAZY,所以我必须使用:

ctypes.CDLL(libidcwf_path, mode=1)

我通过检查/usr/include/bits/dlfcn.h找到了这个模式,它可能不是标准的。向ctypes邮件列表的这个2006年讨论串致敬。


1
在Python 3中,可以使用“from posix import RTLD_LAZY”或“os.RTLD_LAZY”,正如Rookie在另一个答案中提到的那样。我仍然没有在Python 2中找到这个常量。 - Qi Fan

8

您应该使用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)

8
我遇到了同样的问题。 为了解决它需要两个步骤:
1.像其他用户建议的那样,使用RTLD_GLOBAL。 2.你需要加载你的库使用的每个库。所以,如果ODBCGeneralQuery在例如libIDCodbc中定义,你需要首先运行这一行: ctypes.CDLL("libIDCodbc.so", mode = ctypes.RTLD_GLOBAL)

1
RTLD_GLOBAL 究竟是做什么的?它是否表示“如果您加载了一堆 .so 文件,则将所有符号放入一个巨大的命名空间中”? - Tommy
@Tommy 我晚了,但根据https://docs.python.org/3/library/ctypes.html#ctypes.PyDLL和https://manpages.debian.org/bookworm/manpages-dev/dlopen.3.en.html#RTLD_GLOBAL,`RTLD_GLOBAL`模式意味着使符号对随后加载的共享对象可用。 - undefined

6

当你编译共享库时,请确保将所有的-lsomething放在字符串命令的结尾。对我来说,这解决了问题。


2

根据Walter Nissen的最初的回答,您可以修改代码如下:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接