【问题标题】:Android NDK CMake linking issuesAndroid NDK CMake 链接问题
【发布时间】:2016-10-15 18:47:53
【问题描述】:

我对 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 库中定义的本机方法。我在运行时收到UnsatisfiedLinkError。相反,如果我将方法移动(直接通过复制和粘贴)到 native.cpp,那么一切都会正常。

所以基本上:

  • Java -> native.cpp 中的方法有效
  • Java -> native.cpp 中的方法 -> foo 库中定义的方法 有效
  • Java -> foo 库中的方法 不起作用 (UnsatisfiedLinkError)

我尝试使用nm 检查导出的函数,看起来foo.a 正确导出了本机函数,我可以看到

00011060 T Java_com_mypackage_Controls_onTap

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

此外,从native.cpp 调用foo 库中的任何方法都按预期工作,因此库是有效静态链接的。

我无法理解这背后的原因,方法应该没问题,可见性应该是正确的,正如JNIEXPORT 宏指定的那样,所以我真的在黑暗中摸索(并且 Gradle 不提供任何输出编译阶段,所以我无法理解发生了什么,但 build.ninja 文件似乎是正确的)

【问题讨论】:

  • 您是否尝试添加set( CMAKE_VERBOSE_MAKEFILE on )?从您的解释中很难判断出了什么问题 - 要么是从 libnative.so 中删除了符号,要么完全跳过了链接 libfoo.a。
  • @AlexCohn 的 libfoo.a 链接不会被跳过,因为我能够从 libnative 调用 libfoo 的方法并且它可以正确链接和工作。似乎该符号已被剥离或没有从外部明显导出,但我试图强制 -fvisibility=default 没有任何成功。不幸的是,使用的生成器不是make,所以没有makefile。我可以看到调用命令,它似乎是正确的。 so 是通过喜欢 native.o 和 libfoo.a 生成的。我真的没有线索..

标签: android c++ android-ndk linker cmake


【解决方案1】:

这种行为,即使不愉快,也是正确的。链接器从使用的静态库中删除任何对象“文件”(在您的情况下,foo.o),除非它们被共享库中的对象之一“固定”(在您的情况下,本机.o)。解决问题的方法有以下三种:

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

  2. 参考Java_com_mypackage_Controls_onTap 或任何其他 foo.cpp 中的外部符号 native.cpp

  3. 使用SET(native -Wl,--whole-archive foo -Wl,--no-whole-archive)(见https://stackoverflow.com/a/17477559/192373

【讨论】:

  • 谢谢,通过搜索相关发布(不涉及 Android NDK),我得出了相同的结论。虽然使用--whole-archive 似乎是解决这个问题的灵丹妙药,但我想知道是否有办法将特定方法标记为“始终导出”,也许通过__attribute__ 只是为了保持理智,而不仅仅是盲目地导出任何符号任何目标文件(因为最终可能会使用 1000 个中的 10 个)?
  • 不,不存在链接器选项“保留符号 a、b 和 q,即使没有人需要它们”。但是(参见上面的2.)很容易模拟这样的选项。如果你从隐式 JNI 绑定切换到显式 RegisterNatives(),你会自动得到这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-14
  • 2010-12-20
  • 1970-01-01
  • 2021-02-06
  • 1970-01-01
  • 1970-01-01
  • 2012-05-22
相关资源
最近更新 更多