so 介绍
基本介绍
为什么会用到 Shared Object(SO)
开发效率
快速移植
so 的版本
根据 CPU 平台有所不一样
加载方法
System.loadLibrary
如果加载的文件名是 xxx ,那么其实加载的是项目中 libs 目录下的 libxxx.so文件。
System.load
对应 lib 的绝对路径。
主要使用第一种方式,第二种方式主要用于在插件中加载 so 文件。
loadLibrary 加载流程
根据官方 API 介绍
The call
System.loadLibrary(name)is effectively equivalent to the call
可以看出该函数其实调用的是 Runtime.java( libcore/luni/src/main/java/java/lang/Runtime.java )中的函数 loadLibrary,继而会继续调用 loadLibrary 另一个重载函数,它包含两个参数
libame,我们传入的库名字
VMStack.getCallingClassLoader(),类加载器 ClassLoader,方便于去寻找相应的 library。
可以看出,程序主要的功能正如注释所说
Searches for a library, then loads and links it without security checks.
而其中所采用的加载函数是 doLoad 函数。在这里,我们先不继续分析,我们来看看 load 函数如何。
load 加载流程
根据官方 API 说明,如下
The call System.load(name) is effectively equivalent to the call:
其同样也是调用 Runtime.java 中的函数,如下
其同样也会调用load 的两个参数的重载函数,继而会调用doLoad函数。
无论是上面的哪一种加载方法,最后都会调用Runtime.java中的doLoad函数。
核心加载流程
doLoad
下面我们来分析一下 doLoad 函数,如下
虽然源代码很长,但是很多部分都是注释,也说明了为什么要使用这样的一个函数的原因,主要有以下原因
Android App 都是由 zygote fork 生成的,因此他们的 LD_LIBRARY_PATH 就是 zygote 的LD_LIBRARY_PATH,这也说明 apk 中的 so 文件不在这个路径下。
so 文件之间可能存在相互依赖,我们需要按照其按依赖关系的逆方向进行加载。
函数的基本思想就是找到库文件的路径,然后使用 synchronized 方式调用了 nativeLoad 函数。
nativeload
而 nativeload 函数其实就是一个原生层的函数
相应的文件路径为 dalvik/vm/native/java_lang_Runtime.cpp ,具体的 nativeLoad 函数如下
可以看出在 native 层对应的函数是 Dalvik_java_lang_Runtime_nativeLoad,如下
根据注释,我们可以确定关键的代码在
这一行执行后会告诉我们加载对应的 so 是否成功。
dvmLoadNativeCode
其基本的代码如下,我们可以根据注释来简单判断一下该函数的功能:
程序根据指定的绝对路径加载相应的 native code,但是如果该 library 已经加载了,那么就不会再次进行加载。
此外,正如 JNI 中所说,我们不能将一个库加载到多个 class loader 中,也就是说,一个 library 只会和一个 class loader 关联。
函数的基本执行流程如下
利用 findSharedLibEntry 判断是否已经加载了这个库,以及如果已经加载的话,是不是采用的是同一个class loader。
如果没有加载的话,就会利用 dlopen 打开该共享库。
其中的 dlopen 函数(bionic/linker/dlfcn.cpp)如下
其会调用 do_dlopen 函数(bionic/linker/linker.cpp),如下
在找到对应的库之后,会使用 si->CallConstructors(); 来构造相关信息,如下
可以看出,正如注释所写的,如说 .init 函数与 init_array 存在的话,程序会依次调用 .init 函数与.init_array 中对应位置的代码。相关说明如下
建立一个打开的共享库的 entry,并试图其加入到对应的 list 中,方便管理。如果加入失败的话,就会对其进行释放。
如果加载成功,就会利用 dlsym 来获取对应 so 文件中的 JNI_OnLoad 函数,如果存在该函数的话,就进行调用,否则,就会直接返回。
总结
这说明加载 .so 文件时,会按照执行如下顺序的函数(如果不存在的话,就会跳过)
.init 函数
.init_array 中的函数
JNI_OnLoad 函数
Last updated