android JNI (2) - load .so file with JNI

在尝试使用JNI加载libhello.so时遇到错误,系统提示无法找到库。通过strace工具跟踪系统调用发现库已被成功打开并读取。问题出在未解析的符号上,这些符号存在于libc.so中。通过在Android.mk中添加LOCAL_STATIC_LIBRARIES:=libc,静态链接libc解决了部分问题,尽管仍有少量符号未链接。最终,libhello.so得以成功加载运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

android JNI (1) 中,生成了一个libhello.so, 将libhello.so 使用adb push到模拟器的/system/lib中,修改文件权限为可读,之后运行Java上层应用。当运行到system.loadlibrary("hello")时,系统屏幕出现弹出窗口,并且结束了虚拟机的运行。使用adb logcat获取log, 错误部分的log为:

 

D/dalvikvm(  597): LOADING path /system/lib/libFnord.so 0x4006c4f8
I/dalvikvm(  597): Unable to dlopen(/system/lib/libFnord.so): Cannot find library

同时虚拟机还在java层抛出UnsatisfiedLinkError异常。

起初以为是libhello.so 位置放错了,或者libhello.so没有读的权限。但是确认后排除了这两种可能。

 

而后使用android strace来trace系统调用,strace 需要自行安装。具体使用方式在Java代码调用system.loadlibrary前使应用sleep, 或者用一个button click来触发加载。运行java应用,使用adb shell ps查询此应用的pid. 再将此pid attach到strace中, 使用 adb shell strace -p pid. 点击button, 触发system.loadlibrary加载Native动态库,可以得到strace 信息:

stat64("/system/lib/libhello.so", {st_mode=S_IFREG|0666, st_size=816132, ...}) = 0
open("/system/lib/libhello.so", O_RDONLY|O_LARGEFILE) = 31
lseek(31, 0, SEEK_SET)                  = 0
read(31, "/177ELF/1/1/1/0/0/0/0/0/0/0/0/0/3/0(/0/1/0/0/0/234/267"..., 4096) = 4096
lseek(31, -8, SEEK_END)                 = 816124
read(31, "/1/0/0/0/0/0/0/0", 8)         = 8
mmap2(0x80400000, 815104, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x80400000
mmap2(0x80400000, 806756, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 31, 0) = 0x80400000
mprotect(0x80400000, 806912, PROT_READ|PROT_WRITE|PROT_EXEC) = 0
mmap2(0x804c5000, 4684, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 31, 0xc5) = 0x804c5000
close(31)                               = 0
munmap(0x80400000, 815104)              = 0
writev(5, [{"/4", 1}, {"dalvikvm/0", 9}, {"Unable to dlopen(/system/lib/lib"..., 64}], 3) = 74
由此也可以看到libhello.so已经被系统被成功打开,而且数据也被成功读取。

 

Cannot find library是使用dlerror输出的的错误信息,通过分析dlerror代码,发现不只是文件找不到会导致Cannot find library 错误, 在加载过程中其他任何的错误都会输出同样的问题。

 

Android Developers 邮件列表的一个帖子http://groups.google.com/group/android-developers/browse_thread/thread/636d11723aae3117/bd9f19e197e82a24?lnk=gst&q=unresolved+symbols+%26+jni#bd9f19e197e82a24,发现了一个突破口。

 

libhello.so的Unresolved symbols可能会导致加载器加载失败,由此说来,这似乎是android JNI加载器在加载多各互相依赖的动态库的bug。 使用Toolchain的objdump工具

arm-eabi-objdump -T libhello.so | grep -e UND. 发现果然在libhello.so有不少未定义的符号。这些符号存在于其他的动态库中,比如c标准库libc.so中。因此, 将工程的Android.mk添加一个变量定义:

LOCAL_STATIC_LIBRARIES:=libc

将libc静态的链接到libhello.so中。 值得注意的是, 使用android build系统的链接器链接会出现部分符号不链接的情况,也就是说,虽然采用静态的连接libc, libhello.so仍然有少量的符号为UNDefined属性的,而这些符号确实在libc.a中存在,这部分符号依然需要在运行时由libc.so中加载运行。原因未知,可能这是链接器的bug吧。 幸运的是,少量的这类符号没有使加载器报告 Cannot find library的错误。libhello.so被成功加载运行。

 

在这里,我只能够描述这些现象。具体原因目前不清楚,还需要进一步调查。

07-22 18:02:18.502 11831 11831 I Typeface: Preloading /system/fonts/Roboto-Regular.ttf 07-22 18:02:18.542 11831 11831 V GraphicsEnvironment: ANGLE Developer option for 'com.taixin.txallremote' set to: 'default' 07-22 18:02:18.542 11831 11831 V GraphicsEnvironment: ANGLE GameManagerService for com.taixin.txallremote: false 07-22 18:02:18.543 11831 11831 V GraphicsEnvironment: Neither updatable production driver nor prerelease driver is supported. 07-22 18:02:18.544 11831 11831 D NetworkSecurityConfig: No Network Security Config specified, using platform default 07-22 18:02:18.545 11831 11831 D NetworkSecurityConfig: No Network Security Config specified, using platform default 07-22 18:02:18.545 11831 11831 I : application init 07-22 18:02:18.552 11831 11853 D libEGL : loaded /system/lib/egl/libEGL_emulation.so 07-22 18:02:18.553 11831 11853 I nemucooker: cook file1: /etc/mumu-configs/renderer.config, last modify time: Thu Aug 29 11:22:45 2024 07-22 18:02:18.553 11831 11853 I nemucooker: cook file2: /data/system/etc/mumu-configs/renderer.config, last modify time: Thu Jul 17 21:45:34 2025 07-22 18:02:18.553 11831 11853 I nemucooker: cook select file: /data/system/etc/mumu-configs/renderer.config 07-22 18:02:18.566 11831 11853 D libEGL : loaded /system/lib/egl/libGLESv1_CM_emulation.so 07-22 18:02:18.568 11831 11853 D libEGL : loaded /system/lib/egl/libGLESv2_emulation.so 07-22 18:02:18.590 11831 11853 E GraphicsEnv: Failed to get gpu service 07-22 18:02:18.599 11831 11831 I txallremote-H6RemoteIO: load library: libtxporting-i2c.so 07-22 18:02:18.600 11831 11831 E ziparchive: Zip: lseek on fd -2 failed: Bad file descriptor 07-22 18:02:18.625 11831 11831 I txallremote-H6RemoteIO: txallremote init 07-22 18:02:18.626 11831 11831 E xin.txallremot: No implementation found for void com.taixin.txallremote.jni.H6RemoteIO.TxAllRemote_Init() (tried Java_com_taixin_txallremote_jni_H6RemoteIO_TxAllRemote_1Init and Java_com_taixin_txallremote_jni_H6RemoteIO_TxAllRemote_1Init__) 07-22 18:02:18.626 11831 11831 D AndroidRuntime: Shutting down VM 07-22 18:02:18.627 11831 11831 E AndroidRuntime: FATAL EXCEPTION: main 07-22 18:02:18.627 11831 11831 E AndroidRuntime: Process: com.taixin.txallremote, PID: 11831 07-22 18:02:18.627 11831 11831 E AndroidRuntime: java.lang.UnsatisfiedLinkError: No implementation found for void com.taixin.txallremote.jni.H6RemoteIO.TxAllRemote_Init() (tried Java_com_taixin_txallremote_jni_H6RemoteIO_TxAllRemote_1Init and Java_com_taixin_txallremote_jni_H6RemoteIO_TxAllRemote_1Init__) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at com.taixin.txallremote.jni.H6RemoteIO.TxAllRemote_Init(Native Method) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at com.taixin.txallremote.jni.H6RemoteIO.remoteOpen(H6RemoteIO.java:17) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at com.taixin.txallremote.dataway.RemoteDirectIo.getInstance(RemoteDirectIo.java:27) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at com.taixin.txallremote.activity.TempHumiControlManagerActivity.onCreate(TempHumiControlManagerActivity.java:81) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8054) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8034) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1347) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3736) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3931) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2294) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:201) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.os.Looper.loop(Looper.java:288) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8060) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) 07-22 18:02:18.627 11831 11831 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1091) 07-22 18:02:18.651 11831 11831 I Process : Sending signal. PID: 11831 SIG: 9 07-22 18:02:18.673 1251 2855 I ActivityManager: Process com.taixin.txallremote (pid 11831) has died: fg TOP 07-22 18:02:18.673 1055 1055 I Zygote : Process 11831 exited due to signal 9 (Killed) 07-22 18:02:18.673 1251 1291 I libprocessgroup: Successfully killed process cgroup uid 10109 pid 11831 in 0ms 07-22 18:02:24.472 1091 1091 D opengl-gc: Check pid 11831: running 0
最新发布
07-23
<think>我们正在处理一个Android应用崩溃问题,具体错误是java.lang.UnsatisfiedLinkError,并且提示Native Method not found。这通常发生在JNIJava Native Interface)调用中,当Java代码尝试加载一个本地方法(Native Method)时,在本地库(.so文件)中找不到对应的实现。 根据引用[1]和引用[2],我们可以知道这个错误通常与本地库(.so文件)的加载有关。可能的原因包括: 1. 本地库文件(.so文件)没有正确打包到APK中,或者没有放在正确的ABI目录下(如armeabi-v7a, arm64-v8a, x86等)。 2. 本地库依赖的其他库不存在或版本不匹配。 3. 在加载库时,库的初始化过程中发生了错误。 4. 设备CPU架构与APK中提供的ABI不匹配。 5.Android 7.0及以上版本,如果应用使用了复制的.so文件而不是直接使用APK中的,可能会因为文件权限问题导致加载失败。 解决方案: 1. 检查APK中是否包含了对应ABI的.so文件。可以使用解压工具查看APK的lib目录下是否有对应的ABI文件夹,并且文件夹中有需要的.so文件。 2. 确保在Java代码中加载库的顺序正确。如果某个库依赖另一个库,需要先加载被依赖的库。 3. 检查本地方法的名称是否符合JNI规范(包名+类名+方法名),避免使用ProGuard混淆了本地方法所在的类名或方法名。 4. 如果是动态下载.so文件,需要确保下载的文件完整,并且放置的目录有可执行权限(在Android 7.0以上,建议使用Android的NDK库加载机制,避免直接复制到应用目录)。 5. 检查本地库的依赖关系,使用`readelf -d <your_library.so>`(Linux)或`objdump -x <your_library.so>`(Windows)查看依赖的库是否都存在。 针对引用[3],它提到了一个IntPtr的构造函数,这通常是在Xamarin.Android开发中遇到的。如果你使用的是Xamarin.Android,那么需要确保本地库的加载方式正确,并且与Xamarin的绑定库正确关联。 具体到你的错误信息:`com.taixin.txallremote`崩溃,原因是`java.lang.UnsatisfiedLinkError`和`Native Method not found`。我们可以按照以下步骤排查: 步骤1:检查APK中的.so文件 - 确认APK中包含了目标设备ABI对应的.so文件。例如,如果设备是arm64-v8a,那么APK中应该有`lib/<arm64-v8a>/libxxx.so`文件。 步骤2:检查加载库的代码 -Java代码中,通常使用`System.loadLibrary("xxx")`来加载库,其中xxx是去掉前缀`lib`和后缀`.so`的名字。确保加载的库名正确,并且没有拼写错误。 - 如果使用了多个库,注意加载顺序(先加载依赖的库)。 步骤3:检查JNI方法名 - 在C/C++代码中,确保方法名按照JNI规范命名,例如:`Java_com_taixin_txallremote_ClassName_methodName`。注意包名中的点(.)要替换为下划线(_),并且类名和方法名要完全匹配。 步骤4:检查构建配置 - 在build.gradle中,确保ndk配置了正确的ABI过滤,例如: ```groovy android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } } ``` 注意:从Android插件3.0.0开始,默认情况下会构建所有支持的ABI,但如果你只指定了部分ABI,那么其他ABI的设备将无法加载库。 步骤5:检查设备兼容性 - 如果设备是x86架构,而APK中只有armeabi-v7a的库,那么需要确保在build.gradle中包含了x86的ABI,或者使用兼容模式(但最好提供对应的ABI库)。 步骤6:检查日志 - 查看logcat日志,在加载库的时候可能会输出更详细的错误信息,例如缺少依赖库等。 步骤7:测试不同的设备 - 如果可能,在不同的设备上测试,以确定是否特定设备或系统版本的问题。 通过以上步骤,通常可以解决UnsatisfiedLinkError的问题。如果问题仍然存在,请提供更详细的错误日志,以便进一步分析。 相关问题: 1. Android中如何正确配置和使用JNI2. 如何查看APK中包含的ABI架构? 3. Android 7.0以上版本对.so文件的加载有什么特殊要求? 4. 如何解决本地库的依赖问题? 5.Android Studio中如何调试JNI代码?</think>### Android应用崩溃原因分析:`java.lang.UnsatisfiedLinkError` `java.lang.UnsatisfiedLinkError` 是Android开发中常见的JNIJava Native Interface)错误,表示Java虚拟机在尝试加载本地库(.so文件)时找不到对应的原生方法实现。具体到您的报错信息 `com.taixin.txallremote crash due to java.lang.UnsatisfiedLinkError and Native Method not found`,核心原因如下: --- #### **根本原因分析** 1. **本地库加载失败** - **`.so`文件缺失**:APK中未包含目标设备CPU架构(如arm64-v8a, armeabi-v7a)对应的本地库文件。 - **路径错误**:Java代码通过`System.loadLibrary()`加载库时,库名与实际文件名不匹配(如`libxxx.so`应简写为`xxx`)。 - **多ABI冲突**:设备支持的ABI与APK中提供的ABI不兼容(如x86设备缺少x86架构的.so文件)[^1]。 2. **JNI函数签名不匹配** - 本地方法在C/C++中的函数名未遵循JNI命名规范(如`Java_com_taixin_txallremote_ClassName_methodName`)。 - 方法参数类型或返回值类型在Java声明与C/C++实现中存在不一致。 3. **依赖链断裂** - 目标`.so`文件依赖的其他动态库(如第三方.so)未正确打包到APK中。 - Android 7.0+设备上,若.so文件从网络下载后手动加载,可能因文件权限问题导致加载失败[^1]。 --- #### **解决方案** ##### **1. 检查APK中的.so文件** - 解压APK,确认`lib/`目录下存在目标ABI文件夹(如`arm64-v8a`)且包含所需.so文件。 - 使用Android Studio的 **Build > Analyze APK** 直接查看APK结构。 - **修复措施**:在`build.gradle`中明确指定支持的ABI: ```groovy android { defaultConfig { ndk { abiFilters 'arm64-v8a', 'armeabi-v7a' } // 按需添加 } } ``` ##### **2. 验证JNI函数签名** - 检查Java中`native`方法的声明: ```java public native int nativeMethodName(String param); // Java声明 ``` - 对应的C++函数名应为: ```cpp Java_com_taixin_txallremote_ClassName_nativeMethodName(JNIEnv* env, jobject obj, jstring param) ``` - **工具验证**:使用`javac -h`生成JNI头文件,对比函数签名。 ##### **3. 确保正确的库加载顺序** - 若存在库依赖(如A.so依赖B.so),需先加载被依赖库: ```java static { System.loadLibrary("B"); // 先加载依赖库 System.loadLibrary("A"); // 再加载主库 } ``` ##### **4. 处理Android 7.0+的权限问题** -.so文件为动态下载,需设置文件可执行权限: ```java File soFile = new File(path); soFile.setExecutable(true); // 添加执行权限 System.load(soFile.getAbsolutePath()); // 使用绝对路径加载 ``` ##### **5. 排查ProGuard混淆问题** - 若使用ProGuard,需在`proguard-rules.pro`中保留JNI类和方法: ```proguard -keepclasseswithmembernames class * { native <methods>; } ``` --- #### **调试工具推荐** 1. **`adb logcat`**:过滤日志`adb logcat | grep UnsatisfiedLinkError`,查看详细缺失方法名。 2. **`nm`命令**:检查.so文件是否包含目标符号: ```bash nm -D libxxx.so | grep Java_com_taixin_txallremote ``` 3. **`readelf`命令**:分析.so文件的依赖库: ```bash readelf -d libxxx.so | grep NEEDED ``` --- ### 相关问题 1. 如何为不同CPU架构生成多版本.so文件? 2. Android Studio中如何调试JNI代码? 3. 如何解决Android 10以上版本的外部存储访问限制导致的.so加载问题? 4. 使用第三方SDK(如百度地图)时遇到`UnsatisfiedLinkError`,如何定位是SDK问题还是集成错误?[^2] 5. Xamarin.Android开发中如何处理`Java.Lang.UnsatisfiedLinkError`?[^3] [^1]: Android系统安装APK时解压.so的逻辑问题及加载失败解决方案 [^2]: 百度地图SDK的`UnsatisfiedLinkError`常见原因及修复方法 [^3]: Xamarin.AndroidJNI构造函数的处理方式
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值