2016-10-15 106 views
1

我很新的NDK + Gradle + CMake集成,我試圖理解爲什麼鏈接不會按預期導出符號。Android NDK CMake鏈接問題

我有一個由CMakeLists.txt構建的靜態庫,它不是主要的CMakeLists.txt

腳本確實是這樣的:

# main CMakeLists.txt 
add_subdirectory(${LIBS}/foo libs} 

add_library(native SHARED native.cpp) 
# omitting standard android libraries 
target_link_libraries(native foo ${android-lib} ${log-lib}) 

CMakeLists.txt${libs}/foo如下:

# misc configuration of ${SRC} 
add_library(foo STATIC ${SRC}) 

該腳本能正常工作,它能夠鏈接libnative.so,我能夠找到生成libfoo.a。一切似乎都很好。

我再嘗試中所含的富庫foo.cpp定義一個本地方法:

extern "C" JNIEXPORT void JNICALL Java_com_mypackage_Controls_onTap(JNIEnv*, jobject, int x, int y) { 
    // log something 
} 

但我無法調用FOO庫中定義的本地方法。我在運行時得到一個UnsatisfiedLinkError。相反,如果我將(直接通過複製和粘貼)方法移動到native.cpp,那麼一切都很順利。

所以基本上:

  • 爪哇 - >在native.cpp方法工作
  • 爪哇 - 在native.cpp>方法 - >在富庫中定義的方法工作
  • 的Java - >在foo庫中的方法不起作用(UnsatisfiedLinkError

我試着用nm檢查導出的函數,它看起來像foo.a正確導出本機的功能,我可以看到

00011060 T Java_com_mypackage_Controls_onTap 

但該條目從libnative.so消失。相反,如果我直接在native.cpp中定義方法,那麼我也可以在libnative.so上使用nm正確地看到它。

另外調用foo庫中的任何方法從native.cpp起按預期工作,以便庫有效地靜態鏈接。

我無法理解背後的原因,該方法應該沒問題,應該是正確的,因爲它的宏應該是JNIEXPORT,所以我真的在黑暗中摸索(而且Gradle沒有提供任何彙編輸出階段,所以我不明白髮生了什麼,但build.ninja文件似乎是正確的)

+0

你試圖添加'設置(CMAKE_VERBOSE_MAKEFILE上)'?從你的解釋中很難判斷出了什麼問題 - 或者從libnative.so中刪除了符號,或者鏈接libfoo.a被完全忽略。 –

+0

@LlexCohn鏈接的libfoo.a不會被跳過,因爲我可以從libnative調用libfoo的方法,並且它正確地鏈接和工作。它看起來像符號被剝離或不從外部顯着導出,但我試圖強制-fvisibility =默認沒有任何成功。不幸的是,所使用的生成器不是make的,所以沒有makefile。我可以看到調用命令,它看起來是正確的。這是由喜歡native.o和libfoo.a生成的。我真的沒有線索.. – Jack

回答

2

這種行爲,即使不愉快,是正確的。鏈接器會從已使用的靜態庫中刪除任何對象「文件」(在您的情況下,爲foo.o),除非它們被共享庫中的一個對象「固定」(在您的情況下,native.o) 。有三種方法來解決這個問題:

  1. 編譯Foo.cpp中libnative.so代替 靜態庫的一部分。

  2. 參考Java_com_mypackage_Controls_onTapfoo.cppnative.cpp

  3. 使用SET(native -Wl,--whole-archive foo -Wl,--no-whole-archive)任何其他 外部符號(見https://stackoverflow.com/a/17477559/192373

+0

謝謝,通過保持搜索相關發佈(不涉及Android NDK)我得出了同樣的結論。雖然使用'--whole-archive'似乎是解決這個問題的靈丹妙藥,但我想知道是否有辦法將特定方法標記爲「始終導出」,也許通過'__attribute__'只是爲了保持事物的健全,而不僅僅是通過盲目地導出任何目標文件的任何符號(因爲最終可能會使用1000箇中的10個)? – Jack

+0

不,不存在鏈接器選項「即使沒有人需要它們,也保留符號a,b和q」。但是(參見上面的* 2 *)很容易模擬這種選項。如果你從隱式的JN​​I綁定切換到顯式的** RegisterNatives()**,你會自動得到這個。 –