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.<clinit>(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.cPyInit_ 之后接的是該模塊的名稱。也就是說,當這兩個名稱相同的時候,Cython 才能成功編譯這個模塊。

解決方案

因此將參數 name"libTest" 替換為 "Test",問題解決。

重新用 nm -D Test.so 來查看,可以看到 PyInit_Test這個函數已定義,收工~

00000000000042b7 T PyInit_Test


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM