Android原生库.so中JNI_OnLoad等符号缺失问题排查
问题原因分析
链接器构建动态库时,默认会丢弃未被直接引用的目标文件和符号。你的Lib1静态库中仅包含JNI_OnLoad和JNI_OnUnload,而动态库的入口文件native-lib.cpp没有直接引用这两个函数,链接器会判定这些符号属于“无用代码”,因此在生成.so时将它们剔除。
当你添加静态变量并在JNI_OnLoad中使用时,链接器会认为该目标文件存在被依赖的代码,从而保留整个目标文件的所有符号,这就是为什么此时符号会出现在.so中。
-E(--export-dynamic)参数无效的原因是:它的作用是将已被链接器包含的全局符号加入动态符号表,但如果链接器已经丢弃了包含JNI_OnLoad的目标文件,这个参数就起不到任何作用。
解决方案
1. 使用链接器-u参数强制保留符号
-u参数可以指定某个符号为“未定义”,迫使链接器将包含该符号的目标文件纳入链接流程,即使没有其他代码引用它。针对你的场景,需要强制保留JNI_OnLoad和JNI_OnUnload。
修改CMake脚本,给动态库目标添加链接选项:
add_library(${CMAKE_PROJECT_NAME} SHARED native-lib.cpp) # 强制链接器保留JNI_OnLoad和JNI_OnUnload符号 target_link_options(${CMAKE_PROJECT_NAME} PRIVATE -u JNI_OnLoad -u JNI_OnUnload ) target_link_libraries (${CMAKE_PROJECT_NAME} PUBLIC Lib1 Lib2 log android)
2. 备选方案:手动添加符号引用
在native-lib.cpp中添加对JNI_OnLoad的空引用,让链接器认为该符号被使用:
extern "C" jint JNI_OnLoad(JavaVM*, void*); // 强制编译器标记该符号为被使用 static void __attribute__((used)) dummy_ref() { (void)JNI_OnLoad; }
这种方式不如-u参数简洁,但同样能解决问题。
验证方法
编译完成后,使用nm命令检查.so文件的符号表:
nm -D libMyProject.so | grep JNI_
如果输出包含JNI_OnLoad和JNI_OnUnload,说明问题已解决。
内容的提问来源于stack exchange,提问作者Rohan Pande
相关产品推荐
相关产品推荐

