Android NDK CMake链接问题

Android NDK CMake链接问题,android,c++,android-ndk,linker,cmake,Android,C++,Android Ndk,Linker,Cmake,我是NDK+Gradle+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_libra

我是NDK+Gradle+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库中包含的
foo.cpp
中定义一个本机方法:

extern "C" JNIEXPORT void JNICALL Java_com_mypackage_Controls_onTap(JNIEnv*, jobject, int x, int y) {
  // log something
}
但我无法调用foo库中定义的本机方法。我在运行时遇到了一个
不满意的链接错误
。相反,如果我(直接通过复制和粘贴)将方法移动到native.cpp,那么一切都会正常进行

所以基本上:

  • Java->native.cpp中的方法有效
  • Java->native.cpp中的方法->在foo库中定义的方法
  • foo库中的Java->method不起作用(
    unsatifiedlinkerror
我试图用
nm
检查导出的函数,结果显示
foo.a
正确地导出了本机函数,如我所见

00011060 T Java_com_mypackage_Controls_onTap
但是这个条目会从
libnative.so
中消失。相反,如果我直接在native.cpp中定义方法,那么我也可以在
libnative.so
上使用nm正确地看到它

此外,从
native.cpp
调用
foo
库中的任何方法都可以正常工作,因此库可以有效地静态链接


我无法理解这背后的原因,方法应该是好的,可见性应该是正确的,正如
JNIEXPORT
宏指定的那样,所以我真的在暗中摸索(Gradle没有提供编译阶段的任何输出,所以我无法理解发生了什么,但是build.ninja文件似乎是正确的)这种行为,即使令人不快,也是正确的。链接器从使用过的静态库中删除任何对象“文件”(在您的示例中为foo.o),除非它们被共享库中的一个对象“固定”(在您的示例中为native.o)。有三种方法可以解决这个问题:

  • 将foo.cpp编译为libnative.so的一部分,而不是 静态库

  • 参考
    Java\u com\u mypackage\u控件\u onTap
    或任何其他 来自
    native.cpp
    foo.cpp
    的外部符号

  • 使用
    SET(native-Wl,--whole-archive foo-Wl,--no-whole archive)
    (请参阅)


  • 您是否尝试添加
    集(CMAKE\u VERBOSE\u MAKEFILE on)
    ?从您的解释中很难看出哪里出了问题-要么是从libnative.so中删除了符号,要么是完全跳过了链接libfoo.a。@不跳过libfoo.a的AlexCohn链接,因为我能够从libnative调用libfoo的方法,并且它正确链接并工作。看起来符号被剥离了,或者没有从外部明显导出,但我试图强制-fvisibility=default,但没有成功。不幸的是,使用的生成器不是make,因此没有makefile。我可以看到调用命令,它似乎是正确的。so是通过喜欢native.o和libfoo.a生成的。我真的没有任何线索。谢谢,通过对相关发布的搜索(不包括Android NDK),我得出了相同的结论。虽然使用
    --whole archive
    似乎是解决这个问题的灵丹妙药,但我想知道是否有一种方法可以将特定方法标记为“始终导出”,可能是通过
    \uuuuuu属性\uuuuuuuuuu
    来保持事情的正常,而不仅仅是盲目导出任何对象文件的任何符号(因为最终可能只会使用1000个符号中的10个)?否,不存在链接器选项“保留符号a、b和q,即使没有人需要它们”。但是(见上文2)很容易模拟这样的选项。如果您从隐式JNI绑定切换到显式RegisterNatives(),您将自动得到它。