JNI调用Cython生成库‘undefined symbol: PyInit_’问题
最近项目需要提升所有 Python 算法的执行时间,并给 Java 框架调用,根据 Python一键转Jar包,Java调用Python新姿势!的思路可以用 Cython 将 Python 代码转换为 C 代码再编译为动态连接库 (so / dll),提升 Python 代码执行速度。同时提供 Java Native 接口以供 Java 框架调用。
问题
但在根据刚刚所提博文进行复现时,出现了一个老大难问题,一直都没什么头绪
Exception in thread "main" java.lang.UnsatisfiedLinkError: /path/libTest.cpython-36m-x86_64-linux-gnu.so: /path/libTest.cpython-36m-x86_64-linux-gnu.so: undefined symbol: PyInit_Test
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
at java.lang.Runtime.load0(Runtime.java:809)
at java.lang.System.load(System.java:1086)
at Test.(Test.java:6)
at Demo.main(Demo.java:3)
直到看到这篇博客 解決 Linux 上 C/C++ 的 undefined symbol 或 undefined reference,可以通过 nm -D LIBRARY_FILE
来查看动态代码段的相关符号 (symbol),输出信息如下
0000000000004a10 T Java_Test_uninitModule
0000000000004a20 T Java_Test_upperFunction
......
U PyImport_ImportModule
U PyInit_Test
00000000000042e7 T PyInit_libTest
......
其中,T
表示全局/局部符号,U
表示为未定义的符号。
原因
也就是说,这个 PyInit_Test
函数未定义,但 PyInit_libTest
却定义了,明明在 main.c
中没有定义这个lib函数,唯一出现的地方就是在编译文件 setup.py
中
extensions = [Extension("libTest", sourcefiles,
include_dirs=['/usr/lib/jvm/java-8-oracle/include/',
'/usr/lib/jvm/java-8-oracle/include/linux/',
'/python/path/include/python3.6m/'],
library_dirs=['/python/path/lib/'],
libraries=['python3.6m'])]
第一个参数是 name
,表示的是这个 extension 的全称,而之后的调用就会依托这个名字,同时在 main.c
中 PyInit_
之后接的是该模块的名称。也就是说,当这两个名称相同的时候,Cython 才能成功编译这个模块。
解决方案
因此将参数 name
从 "libTest"
替换为 "Test"
,问题解决。
重新用 nm -D Test.so
来查看,可以看到 PyInit_Test
这个函数已定义,收工~
00000000000042b7 T PyInit_Test