在android中如何用Java加载解析so

理论基础

so的加载是一种解析式装载,这与dex有一定区别,dex是先加载进行优化验证生成odex,再去解析odex文件,而so更像边解析边装载,在加载过程中主要解析是load段。
下面主要是以java层的so加载进行从源码上进行解析加载流程。

java层的so加载流程分析

System.loadLibrary入口点

在java层我们知道加载so文件是通过System.loadLibrary函数其实现的,下面就以其作为入口点进行分析它的调用关系和实现。
System.loadLibrary在的函数定义系统source\libcore\luni\src\main\java\java\lang\system.java的文件中。

下面是其函数定义实现。

//参数就是要加载的so文件名称
 public static void loadLibrary(String libName) {
         //通过调用Runtime下面的loadLibrary函数实现
         //函数有两个参数,参数1是加载的so文件名,参数2 类加载器。
        Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
    }

Runtime的loadLibray解析

通过上面的System.java的loadLibrary函数我们需要继续分析Runtime.java文件中的loadLibray函数的定义实现。
Runtime的loadLibrary函数在android系统中的位置是
source\libcore\luni\src\main\java\java\lang\Runtime.java文件。

下面是Runtime的 loadLibrary函数的定义实现源码。

    /*
     * Searches for and loads the given shared library using the given ClassLoader.
     */
    void loadLibrary(String libraryName, ClassLoader loader) {
        if (loader != null) {
            //通过加载器去查找要加载的so文件名
            String filename = loader.findLibrary(libraryName);
            //查找失败
            if (filename == null) {
                // It's not necessarily true that the ClassLoader used
                // System.mapLibraryName, but the default setup does, and it's
                // misleading to say we didn't find "libMyLibrary.so" when we
                // actually searched for "liblibMyLibrary.so.so".
                throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                               System.mapLibraryName(libraryName) + "\"");
            }
            //加载so文件名
            String error = doLoad(filename, loader);
            if (error != null) {
                throw new UnsatisfiedLinkError(error);
            }
            return;
        }

        String filename = System.mapLibraryName(libraryName);
        List<String> candidates = new ArrayList<String>();
        String lastError = null;
        //循环遍历文件路径
        for (String directory : mLibPaths) {
            //文件路径和文件名进行拼接
            String candidate = directory + filename;
            candidates.add(candidate);

            if (IoUtils.canOpenReadOnly(candidate)) {
                String error = doLoad(candidate, loader);
                if (error == null) {
                    return; // We successfully loaded the library. Job done.
                }
                lastError = error;
            }
        }

        if (lastError != null) {
            throw new UnsatisfiedLinkError(lastError);
        }
        throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
    }

Runtime的doLoad解析

通过上面的Runtime的loadLibrary函数,我们看到加载so的函数是走到doLoad函数,那么我们就需要继续分析Runtime下的doload函数的定义实现。
Rutime下的doload函数在系统中的
source\libcore\luni\src\main\java\java\lang\Runtime.java文件中。

下面的代码是Runtime的doload函数的定义实现。

 private String doLoad(String name, ClassLoader loader) {
        // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
        // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.

        // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
        // libraries with no dependencies just fine, but an app that has multiple libraries that
        // depend on each other needed to load them in most-dependent-first order.

        // We added API to Android's dynamic linker so we can update the library path used for
        // the currently-running process. We pull the desired path out of the ClassLoader here
        // and pass it to nativeLoad so that it can call the private dynamic linker API.

        // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
        // beginning because multiple apks can run in the same process and third party code can
        // use its own BaseDexClassLoader.

        // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
        // dlopen(3) calls made from a .so's JNI_OnLoad to work too.

        // So, find out what the native library search path is for the ClassLoader in question...
        String ldLibraryPath = null;
        if (loader != null && loader instanceof BaseDexClassLoader) {
            ldLibraryPath = ((BaseDexClassLoader) loader).getLdLibraryPath();
        }
        // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
        // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
        // internal natives.
        synchronized (this) {
            return nativeLoad(name, loader, ldLibraryPath);
        }
    }

总结

从以上的源码实现流程分析,我们可以看出Android在java层加载so的接口是System.loadLibrary(),通过层层递进关系从而实现java层的加载so。
下图是详细的java层加载so函数的调用关系。

到此这篇关于在android中如何用Java加载解析so的文章就介绍到这了,更多相关Android 加载so内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-10-07

Android so库的热更新问题

本来想写资源的热修复的,虽然方案差不多已经完成了,但是考虑到一些敏感问题,资源修复就不写了.那就来写写so的热修复,其原理和class的修复是一样的,但是so的热修复的需求并不高,就当做学习吧. 首先来总结一下Android的ClassLoader方式的热更新,这种方式类的查找过程是通过BaseDexClassLoader来完成的,最终会通过成员变量DexPathList对象中的findClass方法来查找类,代码如下: public Class findClass(String name, L

非常详细的android so库逆向调试教程

目录 前言 应用环境准备 创建默认的native application 修改stringFromJNI方法,便于调试 修改androidManifest文件 修改CMakeLists.txt 编译运行,获取so hook环境准备 使用ida pro进行hook adb与手机的准备 ida pro的工作准备 使用ida pro进行调试 进行调试 结束 前言 好久没有写博客了,最近的精力全放在逆向上面.目前也只是略懂皮毛. android java层的逆向比较简单,主要就是脱壳 .反编译源码,通过

Android Studio工程引用第三方so文件的方法

应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库.在Android 系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64- v8a,mips64,x86_64. jar包存放到工程的libs目录下. 在main下建个文件叫jniLibs android { compileSdkVersion 26 buil

深入分析Android加载so文件源码

Android系统中使用ndk进行编程,有很多的好处(Java的跨平台特性导致其本地交互的能力不够强大,一些和操作系统相关的特性Java无法完成:代码的保护:由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大:可以方便地使用C/C++开源库:便于移植,用C/C++写的库可以方便在其他平台上再次使用:提供程序在某些特定情形下的执行效率,但是并不能明显提升Android程序的性能). 要使用ndk进行编程,在Java层就必须要对so进行加载.Java层加载so的函数有两个: Sys

Android.mk引入第三方jar包和so库文件的方法

以SystemUI为例,如果需要在SystemUI中引入第三方jar包以及so库,可作如下处理: 首先,在frameworks\base\packages\SystemUI下新建libs目录: 将需要引入的jar包放置到libs目录下.然后修改Android.mk文件: 也就说引入jar包主要依赖的是LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES和LOCAL_STATIC_JAVA_LIBRARIES.下面吧完整的Android.mk贴出来,方便拷贝. LOCAL_PA

IntelliJ IDEA引入第三方jar包或查看Java源码的时候报decompiled.class file bytecode version:52.0(java 8)错误的解决办法

引入第三方jar包或者查看Java源码的时候,经常遇到问题如下: decompiled.class file bytecode version:52.0(java 8) ,open source file. 翻译一下上面的意思:(反编译 .class文件中的字节码 到Java 8 版本) 打开源文件 其实这个时候,你就得注意一下,你看到文件是 .class文件,还是 .java文件. 然后我就详细的总结了一下,二者的差别: 1.先是理论知识: .java为Java的源文件后缀,编写的代码需要写在

vscode快速引入第三方jar包发QQ邮件

本文详细的介绍了vscode快速引入第三方jar包发QQ邮件,分享给大家,具体如下: 安装 jdk 直接安装下一步即是 安装maven 若想多版本切换,删除环境变量中的javapath即可 安装插件 vscode插件装一个pack就行了,后续想装哪个就哪个 使用第三方jar包 简单尝鲜某个jar包,使用maven或gradle太重.而配置一下.setting.json,然后将jar包直接放入该文件夹下即可,不需要手动添加到classpath. 当然前提是jar包先手动下载,大规模多依赖,还是需要

Java实现从jar包中读取指定文件的方法

本文实例讲述了Java实现从jar包中读取指定文件的方法.分享给大家供大家参考,具体如下: 以下的Java代码实现了从一个jar包中读取指定文件的功能: /** * This class implements the funcationality of reading and writing files in jar files. */ package com.leo.util; import java.io.InputStream; import java.io.FileOutputStrea

dom4j从jar包中读取xml文件的方法

进行封装的时候,我们常常需要用xml来定义一些规范,在单独运行读取的时候当然不会有问题,但这些xml是往往是放在jar包里的,这样一来,这些东西就找不出来了.文中用到的xml定义如下: 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?><ACCESOS> <item>  <SOCIO name="adsf">   <NUMERO>00045

android导入第三方jar包报错 如何正确导入jar包

在android上导入zxing.jar包,总是报错: Could not find class 'com.google.zxing.MultiFormatWriter', referenced from method com.changyang.app.util.Encode2dUtil.creat2DCode 后来找到了解决方法: 在adt17 的版本之前,导入第三方jar包时要建立一个lib目录,并 add to buiild path. 在adt17的版本之后,导入第三方jar包,要建立

Android Studio 在项目中引用第三方jar包的方法

在Android Studio项目中引用第三方jar包的方法: 步骤: 1.在build.gradle文件中添加如下代码: 备注:要添加在Android作用域下 sourceSets { main { jniLibs.srcDirs = ['libs'] } } 点击[Sync Now],会生成jniLibs文件夹 找到jniLibs文件夹对应的实体目录,把需要用到的jar包放到该目录下 在build.gradle文件中,在dependencies模块,添加以下代码: compile files

spring boot加载第三方jar包的配置文件的方法

前言 今天收到一封邮件,大概内容如下:spring boot鼓励去配置化,那么怎么将第三方jar包中的xml去配置化了? 其实,这个问题,在前面的文章中也有提到,http://www.jb51.net/article/125700.htm 下面,我们就以Quartz定时任务为例,单独对这个问题来进行说明,如何实现去配置化. 如果不使用spring boot,我们配置一个简单的定时任务时,需要引入以下配置文件: <!-- 配置需要定时执行的任务类以及方法 --> <bean id=&quo

Android在高jar包版本的工程中修改方法

android的应用程序安装包APK如果是在高版本的android jar上开发的是无法在低版本的android SDK上跑的,所以我们需要将我们已经开发好的应用程序换上低版本的android jar包重新编译一个APK,那么如何简单的在高版本的工程中直接修改呢: 1. eclipse中点击工程->Properties-> Android 在右侧的界面中选中Android 2.2即可(前提是你的android sdk中同时包含这两个版本的android jar包) 或者 打开default.p

Android Studio 生成自定义jar包的步骤详解

想要将一个项目导出为jar包,供其它项目使用,在eclipse中可以直接导出该项目为jar包,而 在AS中可以通过修改gradle才处理. 接下来就介绍下具体的步骤: 1.新建一个项目,项目名随意,eg:MakeJarApplication,在项目中新建一个module类型为android-library ,命名为testLibrary.如图: 项目结构图 2.让app依赖这个库,在app下的build.gradle文件中添加compile project(':testlibrary') dep