欢迎光临
专注android技术,聚焦行业精粹,我们一直在努力

Android SO 加载流程

1、背景

实际工作中经常遇到so相关的错误,主要包括:

常见问题一:

java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/com.xxx.xxx-1/base.apk”],nativeLibraryDirectories=[/data/app/com.xxx.xxx-1/lib/arm64, /vendor/lib64, /system/lib64]]] couldn’t find “xxx.so”,

常见问题二:

java.lang.UnsatisfiedLinkError: dlopen failed: “/data/app/com.xxx.xxx-2/lib/arm64/xxx.so” is 32-bit instead of 64-bit

想要从根本上了解导致上述问题的原因,我们需要了解android系统的so加载流程

2、动态链接库的加载流程

首先从宏观流程上来看,对于 load 过程我们分为 find&load,首先是要找到 so 所在的位置,然后才是 load 加载进内存,先贴一张图看看 so 加载的大概流程。

这个图里面有4个关键点:

  1. SO查找和加载:System.loadLibrary只会在固定的几个目录(nativeLibraryDirectories)中找so文件是否存在,如果找到则用该so的绝对路径去查询是否加载过,没有则会真正去加载so
  2. SO查找的路径(nativeLibraryDirectories)来源:主要来自3个地方:data/app-lib/ , data/app/<apkname>.apk!/lib/ ,  system property(“/vendor/lib, /system/lib”)
  3. primaryAbi 获取:请参考下面2.1节的介绍
  4. SO拷贝:通过拼接 nativeLibraryDirectories 和 primaryAbi 得到最终so的完整路径(mLibDir ),并将apk的对应的so拷贝到该路径下面。

 

2.1、获取PrimaryAbi流程

primaryAbi获取流程可以简单概括以下2个步骤:

  1. 获取手机系统支持的abilist
  2. 遍历 apk(其实就是一个压缩文件)中的所有文件,如果文件全路径中包含 abilist 中的某个 abi 字符串,则记录该 abi 字符串的索引,最终返回所有记录索引中最靠前的,即排在 abilist 中最前面的索引

 

2.1.1、获取手机系统支持的abilist

查询手机支持的ABI 列表的两种方式:

a).通过命令行

具体操作如下,华为mate8手机支持的abi列表为:arm64-v8a, armeabi-v7a, armeabi

 

b). 在代码中获取

得到的结果如下:

 

2.1.2、获取primaryAbi

获取primaryAbi的逻辑在NativeLibraryHelper 中的 findSupportedAbi 方法中,它的核心代码主要如下,基本就是我们前文说的主要逻辑,遍历 apk(其实就是一个压缩文件)中的所有文件,如果文件全路径中包含 abilist 中的某个 abi 字符串,则记录该 abi 字符串的索引,最终返回所有记录索引中最靠前的,即排在 abilist 中最前面的索引。

findSupportedAbi核心代码如下:

举个例子,假如我们的 app 中的 so 地址中有包含 arm64-v8a 的字符串,同时 abilist 是 arm64-v8a,armeabi-v7a,armeab,那么这里就会返回 arm64-v8a。

 

2.2、SO拷贝

主要的策略就是,遍历 apk 中文件,当遍历到有主 Abi 目录的 so 时,拷贝并设置标记 hasPrimaryAbi 为真,以后遍历则只拷贝主 Abi 目录下的 so。这个主 Abi 就是我们前面 findSupportedAbi 的时候找到的那个 abi 的值,大家可以去回顾下。

当标记为假的时候,如果遍历的 so 的 entry 名包含其他abi字符串,则拷贝该 so,拷贝 so 到我们上文说到 mLibDir 这个目录下。

这里有一个很重要的策略是:ZipFileRO 的遍历顺序,他是根据文件对应 ZipFileR0 中的 hash 值而定,而对于已经 hasPrimaryAbi 的情况下,非 PrimaryAbi 是直接跳过 copy 操作的,所以这里可能会出现很多拷贝 so 失败的情况。

举个例子:假设存在这样的 apk, lib 目录下存在 armeabi/libx.so , armeabi/liby.so , armeabi-v7a/libx.so 这三个 so 文件,且 hash 的顺序为 armeabi-v7a/libx.so 在 armeabi/liby.so 之前,则 apk 安装的时候 liby.so 根本不会被拷贝,因为按照拷贝策略, armeabi-v7a/libx.so 会优先遍历到,由于它是主 abi 目录的 so 文件,所以标记被设置了,当遍历到 armeabi/liby.so 时,由于标记被设置为真, liby.so 的拷贝就被忽略了,从而在加载 liby.so 的时候会报异常。

 

3、问题回顾

问题1

java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/com.xxx.xxx-1/base.apk”],nativeLibraryDirectories=[/data/app/com.xxx.xxx-1/lib/arm64, /vendor/lib64, /system/lib64]]] couldn’t find “xxx.so”,

分析:

通过上面的分析我们不难了解到System.loadLibrary首先会去特定的几个目录查找so是否存在,如果不存在则会报这个错误。有两种原因:

  1. app没有这个so文件。需要加回来
  2. app 中对应到系统的primaryAbi目录下面的so不全,需要确认app primaryAbi目录下面是否有该so文件

 

问题2:

java.lang.UnsatisfiedLinkError: dlopen failed: “/data/app/com.xxx.xxx-2/lib/arm64/xxx.so” is 32-bit instead of 64-bit

分析:

System.loadLibrary 方法在查找到so所在的绝对路径后会去加载该so,但是如果该so不兼容64位就会报错。解决办法很简单,重新打一个支持64位的so放在app对应的abi目录下面就可以了。

拓展学习:

Android 在5.0以后其实已经支持64位了,而对于很多时候大家在运行so的时候也会遇到这样的错误:dlopen failed: “xx.so” is 32-bit instead of 64-bit,这种情况其实是因为进程由 64zygote 进程 fork 出来,在64位的进程上必须要64位的动态链接库。

Art 上支持64位程序的主要策略就是区分了 zygote32 和 zygote64,对于32位的程序通过 zygote32 去 fork 而64位的自然是通过 zygote64去 fork。

问题总结:

所以当你的 app 中有64位的 abi,那么就必须所有的 so 文件都有64位的,不能出现一部分64位的一部分32位的,当你的 app 发现 primaryAbi 是64位的时候,他就会通过 zygote64 fork 在64位下,那么其他的32位 so 在 dlopen 的时候就会失败报错。

 

4、拓展学习

4.1、各种CPU架构的兼容关系

目前android支持如下7中CPU架构:

  • armeabi 第5代 ARM v5TE,使用软件浮点运算,兼容所有ARM设备,通用性强,速度慢(只支持armeabi)
  • armeabi-v7a 第7代 ARM v7,使用硬件浮点运算,具有高级扩展功能(支持 armeabi 和 armeabi-v7a,目前大部分手机都是这个架构
  • arm64-v8a 第8代,64位,包含AArch32、AArch64两个执行状态对应32、64bit(支持 armeabi-v7a、armeabi 和 arm64-v8a)
  • *x86 intel 32位,一般用于平板(支持 armeabi(性能有所损耗) 和 x86)
  • x86_64 intel 64位,一般用于平板(支持 x86 和 x86_64)
  • mips 基本没见过(支持 mips)
  • mips64 基本没见过(支持 mips 和 mips_64)

对应的映射关系表如下:

 

5、参考文档

Android 动态链接库加载原理及 HotFix 方案介绍

 

 

赞(1) 打赏
未经允许不得转载:花花鞋 » Android SO 加载流程
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

国内精品Android技术社区

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏