今天把一个apk给厂商签名后直接push到机器的/system/app,结果运行app时显示so找不到。报错:
java stacktrace:
java.lang.UnsatisfiedLinkError: Library mmkv not found; tried [/system/lib/libmmkv.so, /product/lib/libmmkv.so]
at java.lang.Runtime.loadLibrary0(Runtime.java:1101)
at java.lang.System.loadLibrary(System.java:1669)
at com.tencent.mmkv.MMKV.doInitialize(MMKV.java:226)
at com.tencent.mmkv.MMKV.initialize(MMKV.java:208)
at com.tencent.mmkv.MMKV.initialize(MMKV.java:94)
at com.wesley.CustomApplication.onCreate(CustomApplication.kt:14)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1154)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5871)
at android.app.ActivityThread.access 1100 ( A c t i v i t y T h r e a d . j a v a : 199 ) a t a n d r o i d . a p p . A c t i v i t y T h r e a d 1100(ActivityThread.java:199) at android.app.ActivityThread 1100(ActivityThread.java:199)atandroid.app.ActivityThreadH.handleMessage(ActivityThread.java:1650)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922)
日志(xcrash 自己有so异常捕获,所以直到加载 mmkv 才抛出异常)
12-27 14:58:04.840 9717 9717 E System : ##loadLibrary0## /system/app/demo/demo_V1.0.0(2412261921)-release-signed.apk!/lib/armeabi-v7a/libxcrash.so error: dlopen failed: library “/system/app/demo/demo_V1.0.0(2412261921)-release-signed.apk!/lib/armeabi-v7a/libxcrash.so” not found, and try to find so file from the lib path
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: xcrash: NativeHandler System.loadLibrary failed
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: java.lang.UnsatisfiedLinkError: Library xcrash not found; tried [/system/lib/libxcrash.so, /product/lib/libxcrash.so]
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at java.lang.Runtime.loadLibrary0(Runtime.java:1101)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at java.lang.System.loadLibrary(System.java:1669)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at xcrash.NativeHandler.initialize(NativeHandler.java:89)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at xcrash.XCrash.init(XCrash.java:189)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at com.wesley.base.apm.XCrashWrapper.(XCrashWrapper.kt:131)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at com.wesley.base.apm.XCrashWrapper.(XCrashWrapper.kt:17)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at com.wesley.CastApplication.attachBaseContext(CastApplication.kt:43)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.app.Application.attach(Application.java:212)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.app.Instrumentation.newApplication(Instrumentation.java:1121)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.app.LoadedApk.makeApplication(LoadedApk.java:1065)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5842)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.app.ActivityThread.access 1100 ( A c t i v i t y T h r e a d . j a v a : 199 ) 12 − 2714 : 58 : 04.84397179717 E d e m o − X C r a s h W r a p p e r : a t a n d r o i d . a p p . A c t i v i t y T h r e a d 1100(ActivityThread.java:199) 12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.app.ActivityThread 1100(ActivityThread.java:199)12−2714:58:04.84397179717Edemo−XCrashWrapper:atandroid.app.ActivityThreadH.handleMessage(ActivityThread.java:1650)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.os.Handler.dispatchMessage(Handler.java:106)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.os.Looper.loop(Looper.java:193)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at android.app.ActivityThread.main(ActivityThread.java:6669)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at java.lang.reflect.Method.invoke(Native Method)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
12-27 14:58:04.843 9717 9717 E demo-XCrashWrapper: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922)
因为安卓6开始支持直接加载apk内部so,如果编译后的 apk 内部 so 是不压缩的,那么就不需要释放到目录了。所以排除是因为不解压 so 到/system/app/demo/lib/arm 引起的问题,虽然这样也可以解决问题。
如果apk的minSdkVersion >= 23 并且 Android Gradle plugin >= 3.6.0情况下,打包时android:extractNativeLibs=false,apk的so默认是不压缩的。
在minSdkVersion < 23 或 Android Gradle plugin < 3.6.0情况下,打包时 android:extractNativeLibs=true,apk的so默认是压缩的。
然后,我尝试push没有给厂商签名前的版本 apk 到机器上是正常的,那么应该是厂商签名导致 apk 发生了变化。刚好前几天碰到安卓签名问题安卓15预置第三方apk时签名报错问题解决 - Wesley’s Blog。然后我就用命令看了一下:zipalign -c -v 4 demo_sign.apk
zipalign | Android Studio | Android Developers
显示:Verification FAILED
,那就是 4 字节没有对齐。
查看签名版本apksigner verify -v demo_sign.apk | grep Verified
只有v1签名
~/Project$ apksigner verify -v demo_sign.apk | grep Verified
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): false
Verified using v3 scheme (APK Signature Scheme v3): false
Verified using v3.1 scheme (APK Signature Scheme v3.1): false
Verified using v4 scheme (APK Signature Scheme v4): false
Verified for SourceStamp: false
因为是v1签名,执行zipalign -v -p 4 demo_sign.apk demo_sign_align.apk
对齐4 字节不会破坏厂商的签名信息,然后push进去果然好了。
然后我又试着安装没有对齐前的,结果直接报错了:
PS C:\Users\Wesley> adb install Y:\Project\demo_sign.apk
Performing Streamed Install
adb: failed to install Y:\Project\demo_sign.apk: Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]
但是系统扫描安装却是可以通过的,所以很难发现是字节不对齐引起的。
深究
之前直接搜索安卓java.lang.UnsatisfiedLinkError是很难找到针对这种问题的解决方法的。因为现在有了解决办法,所以换成UnsatisfiedLinkError zipalign进行谷歌搜索。结果就出来了:
android - java.lang.UnsatisfiedLinkError when installing as system app - Stack Overflow
zipflinger导致的UnsatisfiedLinkError分析 - 搜外SEO问答
zipflinger导致的UnsatisfiedLinkError分析_mb5ff2f24b42377的技术博客_51CTO博客
用 AI大模型总结一下:
这篇文章主要分析了在Android开发中,由于升级Android Gradle Plugin (AGP) 版本导致的UnsatisfiedLinkError问题,并提供了相应的解决方案。以下是文章的主要内容总结:
问题描述
- 问题现象:在Android 9.0环境下,将AGP从3.6.1升级到4.1.0后,预装在/system/priv-app下的APP出现了UnsatisfiedLinkError崩溃。
- 问题原因:升级AGP后,系统在加载so文件时失败,具体表现为so文件在APK中的对齐方式有问题。
问题分析
- so文件加载流程:系统使用“!/”分隔符来定位so文件路径,并在APK中查找对应的entry。问题出在zipalign处理上,导致so文件的对齐不正确。
- zipalign的作用:确保APK中所有未压缩数据在4字节边界上对齐,以便使用mmap()直接访问,减少RAM消耗。
- 系统编译处理:Android系统在编译时会对privileged app执行uncompress-dexs操作,将压缩存储的dex文件变为不压缩存储。之后还会进行align-package操作。
问题原因
- zipflinger工具:从AGP 4.1开始,默认在构建release版本时启用zipflinger工具进行打包。zipflinger改变了APK的打包方式,导致uncompress-dexs操作后zipalign出现问题。
解决方案
- 禁用zipflinger:在app工程的gradle.properties中加入配置以禁用zipflinger。
- 不解压 dex:DONT_UNCOMPRESS_PRIV_APPS_DEXS := true,可能会降低 dex 加载速度。
- 使用zip2zip工具:在最新的AOSP源码中,使用zip2zip工具来处理dex文件的解压缩,以适配zipflinger打包的APK。
- 其他方法:包括回退AGP版本、修改系统编译脚本等,但这些方法各有优缺点。
虽然引起问题的原因不一样,但都是因为字节不对齐引起的 so 找不到。
下面来分析一下具体原因:
so加载调用栈
ojluni/src/main/java/java/lang/System.java --> System.loadLibrary
ojluni/src/main/java/java/lang/Runtime.java --> Runtime.loadLibrary0 -> nativeLoad
ojluni/src/main/native/Runtime.c --> Runtime_nativeLoad
art/openjdkjvm/OpenjdkJvm.cc --> JVM_NativeLoad
art/runtime/java_vm_ext.cc --> JavaVMExt::LoadNativeLibrary
system/core/libnativeloader/native_loader.cpp --> OpenNativeLibrary
bionic/libdl/libdl.cpp --> android_dlopen_ext
bionic/linker/dlfcn.cpp --> __loader_android_dlopen_ext
bionic/linker/dlfcn.cpp --> dlopen_ext
bionic/linker/linker.cpp --> do_dlopen
bionic/linker/linker.cpp --> find_library
bionic/linker/linker.cpp --> find_libraries
bionic/linker/linker.cpp --> find_library_internal
bionic/linker/linker.cpp --> load_library
bionic/linker/linker.cpp --> open_library
bionic/linker/linker.cpp --> open_library_in_zipfile
重点看 bionic/linker/linker.cpp --> open_library_in_zipfile
这个函数,导致加载失败的是以下条件 entry.offset % PAGE_SIZE != 0
http://xrefandroid.com/android-9.0.0_r61/xref/bionic/linker/linker.cpp#992
if (entry.method != kCompressStored || (entry.offset % PAGE_SIZE) != 0) {close(fd);return -1;
}
总结来说,就是签名的时候破坏了4字节对齐,导致 so 加载失败。所以,以后碰到此类问题时,可以执行zipalign -c -v 4 xxx.apk
先看一下是否4字节对齐。