这是一个完全正常的用例(如果我正确理解了这个问题)Nix,并且应该运行没有问题。
在处理相关问题时ctypes ([Python 3]:ctypes - Python 的外部函数库 https://docs.python.org/3/library/ctypes.html#module-ctypes),解决这些问题的最佳(通用)方法是:
- 写一个(小)C完成所需工作的应用程序(当然,有效)
- 然后才移动到ctypes(基本上这是翻译上面的应用程序)
我准备了一个小(和虚拟)示例:
-
定义.h:
#pragma once
#include <stdio.h>
#define PRINT_MSG_0() printf("From C: [%s] (%d) - [%s]\n", __FILE__, __LINE__, __FUNCTION__)
-
libC:
-
libC.h:
#pragma once
size_t funcC();
-
libC.c:
#include "defines.h"
#include "libC.h"
#include "libA.h"
size_t funcC() {
PRINT_MSG_0();
for (size_t i = 0; i < ARRAY_DIM; i++)
{
printf("%zu - %c\n", i, charArray[i]);
}
printf("\n");
return ARRAY_DIM;
}
-
libB:
-
libA:
-
libA.h:
#pragma once
#define ARRAY_DIM 3
extern char charArray[ARRAY_DIM];
size_t funcA();
-
libA.c:
#include "defines.h"
#include "libA.h"
#include "libB.h"
char charArray[ARRAY_DIM] = {'A', 'B', 'C'};
size_t funcA() {
PRINT_MSG_0();
return funcB();
}
-
code.py:
#!/usr/bin/env python3
import sys
from ctypes import CDLL, \
c_size_t
DLL = "./libA.so"
def main():
lib_a = CDLL(DLL)
func_a = lib_a.funcA
func_a.restype = c_size_t
ret = func_a()
print("{:s} returned {:d}".format(func_a.__name__, ret))
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
Output:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> ls
code.py defines.h libA.c libA.h libB.c libB.h libC.c libC.h
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> gcc -fPIC -shared -o libC.so libC.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> gcc -fPIC -shared -o libB.so libB.c -L. -lC
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> gcc -fPIC -shared -o libA.so libA.c -L. -lB
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> ls
code.py defines.h libA.c libA.h libA.so libB.c libB.h libB.so libC.c libC.h libC.so
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> LD_LIBRARY_PATH=. ldd libC.so
linux-vdso.so.1 => (0x00007ffdfb1f4000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f56dcf23000)
/lib64/ld-linux-x86-64.so.2 (0x00007f56dd4ef000)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> LD_LIBRARY_PATH=. ldd libB.so
linux-vdso.so.1 => (0x00007ffc2e7fd000)
libC.so => ./libC.so (0x00007fdc90a9a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdc906d0000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdc90e9e000)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> LD_LIBRARY_PATH=. ldd libA.so
linux-vdso.so.1 => (0x00007ffd20d53000)
libB.so => ./libB.so (0x00007fdbee95a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fdbee590000)
libC.so => ./libC.so (0x00007fdbee38e000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdbeed5e000)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> nm -S libC.so | grep charArray
U charArray
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> nm -S libA.so | grep charArray
0000000000201030 0000000000000003 D charArray
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> LD_LIBRARY_PATH=. python3 code.py
Python 3.5.2 (default, Nov 12 2018, 13:43:14)
[GCC 5.4.0 20160609] on linux
From C: [libA.c] (9) - [funcA]
From C: [libB.c] (7) - [funcB]
From C: [libC.c] (7) - [funcC]
0 - A
1 - B
2 - C
funcA returned 3
但是如果你的数组被声明为static ([CPPReference]:C 关键字:static https://en.cppreference.com/w/c/keyword/static)(因此,结果不能是extern如示例中所示),那么您就有点高兴了。
@EDIT0:扩展示例,使其更适合描述。
Since ldd不显示之间的依赖关系.sos,我假设每个都是动态加载的。
-
utils.h:
#pragma once
#include <dlfcn.h>
void *loadLib(char id);
-
utils.c:
#include "defines.h"
#include "utils.h"
void *loadLib(char id) {
PRINT_MSG_0();
char libNameFormat[] = "lib%c.so";
char libName[8];
sprintf(libName, libNameFormat, id);
int load_flags = RTLD_LAZY | RTLD_GLOBAL; // !!! @TODO - @CristiFati: Note RTLD_LAZY: if RTLD_NOW would be here instead, there would be nothing left to do. Same thing if RTLD_GLOBAL wouldn't be specified. !!!
void *ret = dlopen(libName, load_flags);
if (ret == NULL) {
char *err = dlerror();
printf("Error loading lib (%s): %s\n", libName, (err != NULL) ? err : "(null)");
}
return ret;
}
下面是修改后的版本libB.c。注意相同的模式也应该应用到原来的libA.c.
Output:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> ls
code.py defines.h libA.c libA.h libB.c libB.h libC.c libC.h utils.c utils.h
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> gcc -fPIC -shared -o libC.so libC.c utils.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> gcc -fPIC -shared -o libB.so libB.c utils.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> gcc -fPIC -shared -o libA.so libA.c utils.c
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> ls
code.py defines.h libA.c libA.h libA.so libB.c libB.h libB.so libC.c libC.h libC.so utils.c utils.h
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> ldd libA.so
linux-vdso.so.1 => (0x00007ffe5748c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4d9e3f6000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4d9e9c2000)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> ldd libB.so
linux-vdso.so.1 => (0x00007ffe22fe3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe93ce8a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe93d456000)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> ldd libC.so
linux-vdso.so.1 => (0x00007fffe85c3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2d47453000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2d47a1f000)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> nm -S libC.so | grep charArray
U charArray
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> nm -S libA.so | grep charArray
0000000000201060 0000000000000003 D charArray
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> LD_LIBRARY_PATH=. python3 code.py
Python 3.5.2 (default, Nov 12 2018, 13:43:14)
[GCC 5.4.0 20160609] on linux
Traceback (most recent call last):
File "code.py", line 22, in <module>
main()
File "code.py", line 12, in main
lib_a = CDLL(DLL)
File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
self._handle = _dlopen(self._name, mode)
OSError: ./libA.so: undefined symbol: funcB
I believe that this reproduces the problem. Now, if you modify (the 1st part of) code.py to:
#!/usr/bin/env python3
import sys
from ctypes import CDLL, \
RTLD_GLOBAL, \
c_size_t
RTLD_LAZY = 0x0001
DLL = "./libA.so"
def main():
lib_a = CDLL(DLL, RTLD_LAZY | RTLD_GLOBAL)
func_a = lib_a.funcA
func_a.restype = c_size_t
ret = func_a()
print("{:s} returned {:d}".format(func_a.__name__, ret))
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
你会得到以下结果output:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q053327620]> LD_LIBRARY_PATH=. python3 code.py
Python 3.5.2 (default, Nov 12 2018, 13:43:14)
[GCC 5.4.0 20160609] on linux
From C: [libA.c] (11) - [funcA]
From C: [utils.c] (6) - [loadLib]
From C: [libB.c] (8) - [funcB]
From C: [utils.c] (6) - [loadLib]
From C: [libC.c] (7) - [funcC]
0 - A
1 - B
2 - C
funcA returned 3
Notes:
- It's very important that in C
RTLD_LAZY | RTLD_GLOBAL
are there. if RTLD_LAZY is replaced by RTLD_NOW, it won't work
- 另外,如果RTLD_GLOBAL没有指定,它也不会工作。我没有检查是否还有其他RTLD_可以指定的标志而不是RTLD_GLOBAL为了让事情仍然有效
- 创建处理所有库加载和初始化的包装器库将是一件好事(解决方法),特别是如果您计划从多个地方使用它们(这样,整个过程只会在一个地方发生)。但是,上一个子弹仍然适用
- 因为某些原因,ctypes不暴露RTLD_LAZY(事实上,还有许多其他相关标志)。将其定义在code.py,是一种解决方法,并且在不同的(Nix)平台(口味),其价值可能会有所不同