[实践] Android5.1.1源码 - 让某个APP以解释执行模式运行

@(Android研究)[Android5.1.1|APP解释执行]

前言

本文的实践修改了Android5.1.1的源码。

本文只简单的讲了一下原理。在“实践”一节讲了具体做法。

本文的内容涉及Art模式下dex加载的知识,想要详细了解这部分知识可以去看老罗的文章: Android运行时ART简要介绍和学习计划 Android运行时ART加载OAT文件的过程分析 Android运行时ART加载类和方法的过程分析Android运行时ART执行类方法的过程分析

本文的内容涉及zygote,如果不知道zygote是什么,或者好奇zygote如何启动,可以去看老罗的文章: Android系统进程Zygote启动过程的源代码分析

老罗的文章分析的是Android2.3的源码,所以下面提到的与zygote有关的函数在老罗的文章里面可能没有,如果想要对下面提到的与zygote有关的函数有一个简单的了解可以看我的文章:Android5.1.1源码 - zygote fork出的子进程如何权限降级

原理简介

怎么才能让方法解释执行

在函数ClassLinker::LinkCode中会链接dex中的方法代码,这个函数的定义在文件"art/runtime/class_linker.cc"中,下面是它的源码(这里只列出了与本文有关的部分):

void ClassLinker::LinkCode(Handle<mirror::ArtMethod> method, const OatFile::OatClass* oat_class,const DexFile& dex_file, uint32_t dex_method_index,uint32_t method_index) {......bool enter_interpreter = NeedsInterpreter(...);......if (method->IsStatic() && !method->IsConstructor()) {......} else if (enter_interpreter) {if (!method->IsNative()) {// Set entry point from compiled code if there's no code or in interpreter only mode.method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());......} else {......}} else {......}......}

在这个函数中调用了NeedsInterpreter函数判断当前方法是否要解释执行,如果返回值为true,即局部变量enter_interpreter被赋值为true,那么调用ArtMethod类中的SetEntryPointFromQuickCompiledCode函数并将GetQuickToInterpreterBridge()的返回值传入,GetQuickToInterpreterBridge()函数返回用于解释执行的函数的入口地址,这个入口函数解释执行dex中的方法。

那么现在来看看NeedsInterpreter函数,这个函数的定义在文件"art/runtime/class_linker.cc"中,下面是它的源码:

// Returns true if the method must run with interpreter, false otherwise.
static bool NeedsInterpreter(......// If interpreter mode is enabled, every method (except native and proxy) must// be run with interpreter.return Runtime::Current()->GetInstrumentation()->InterpretOnly() &&!method->IsNative() && !method->IsProxyMethod();
}

当"Runtime::Current()->GetInstrumentation()->InterpretOnly()"返回true且不是本地方法和代理方法,那么这个函数就会返回true,否则返回false。

InterpretOnly函数是Instrumentation类的成员函数,它的函数定义在文件"art/runtime/instrumentation.h"中,下面是它的源码:

// Called by ArtMethod::Invoke to determine dispatch mechanism.
bool InterpretOnly() const {return interpret_only_;
}

interpret_only_是类Instrumentation的成员变量,是布尔类型。可以发现InterpretOnly函数仅仅是将"interpret_only_"返回,如果将interpret_only_设置为true,那么根据上文分析,所有“非本地且非代理”方法都将被解释执行。

那么如何将interpret_only_设置为true哪,在Instrumentation类中有一个ForceInterpretOnly函数,下面是这个函数的源码:

void ForceInterpretOnly() {interpret_only_ = true;forced_interpret_only_ = true;
}

这个函数是Instrumentation类的公有成员函数,所以直接调用这个函数即可将interpret_only_设置为true。

这里有一个问题,将interpret_only_设置为true,那么“非本地且非代理”方法在链接代码时都将被设置成解释执行,那么会不会影响到其他的APP进程?不会,因为ClassLinker::LinkCode函数对方法的链接是在APP进程的内存中进行的,所以这个操作并不会影响到其他进程。

这里进行一个小节,当执行"Runtime::Current()->GetInstrumentation()->ForceInterpretOnly()"语句时,会把Instrumentation对象的interpret_only_成员变量设置为true。那么当方法是“非本地且非代理”方法时,NeedsInterpreter函数将返回true,那么在ClassLinker::LinkCode函数中会将这个方法设置为解释执行。

如果要将APP中所有方法都设置为解释执行,那么就需要在链接APP的dex中的方法代码之前执行"Runtime::Current()->GetInstrumentation()->ForceInterpretOnly()"语句。

调用ForceInterpretOnly函数的时机

我的办法是在EnableDebugFeatures函数中调用ForceInterpretOnly函数,在这一节中会先说明Android如何执行到EnableDebugFeatures函数,然后会说明在EnableDebugFeatures函数中调用ForceInterpretOnly函数的好处。

所有的Android应用进程都是zygote fork出来的,fork APP进程时的函数调用路径:

|- forkAndSpecialize - java方法|- com_android_internal_os_Zygote_nativeForkAndSpecialize - native函数|- ForkAndSpecializeCommon - native函数

调用完ForkAndSpecializeCommon函数后APP进程就被fork出来了。

ForkAndSpecializeCommon函数定义在文件"frameworks/base/core/jni/com_android_internal_os_Zygote.cpp"中,下面它的源码:

static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids, jint debug_flags, jobjectArray javaRlimits, jlong permittedCapabilities, jlong effectiveCapabilities, jint mount_external, jstring java_se_info, jstring java_se_name, bool is_system_server, jintArray fdsToClose, jstring instructionSet, jstring dataDir) {......pid_t pid = fork();if (pid == 0) {......env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,is_system_server ? NULL : instructionSet);......} else if (pid > 0) {// the parent process}return pid;
}

gCallPostForkChildHooks是一个全局变量,它在com_android_internal_os_Zygote.cpp文件的register_com_android_internal_os_Zygote函数中被初始化。

env->CallStaticVoidMethod(...)语句调用了Java方法"Zygote.callPostForkChildHooks"。

下面是Zygote.callPostForkChildHooks方法的源码,这个方法在文件"frameworks/base/core/java/com/android/internal/os/Zygote.java"中:

private static void callPostForkChildHooks(int debugFlags, String instructionSet) {long startTime = SystemClock.elapsedRealtime();VM_HOOKS.postForkChild(debugFlags, instructionSet);checkTime(startTime, "Zygote.callPostForkChildHooks");
}

VM_HOOKS是Zygote类的成员变量,下面是它的定义:

private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();

VM_HOOKS.postForkChild调用的就是ZygoteHooks类中的成员方法postForkChild,这个方法在文件"libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java"中,下面是它的源码:

/** * Called by the zygote in the child process after every fork. The debug * flags from {@code debugFlags} are applied to the child process. The string * {@code instructionSet} determines whether to use a native bridge. */
public void postForkChild(int debugFlags, String instructionSet) {nativePostForkChild(token, debugFlags, instructionSet);
}

这个方法中调用了native函数nativePostForkChild,nativePostForkChild函数的C层代码在文件"/home/sowuy/android/system/art/runtime/native/dalvik_system_ZygoteHooks.cc"中,下面是它的源码:

static void ZygoteHooks_nativePostForkChild(JNIEnv* env, jclass, jlong token, jint debug_flags,jstring instruction_set) {......EnableDebugFeatures(debug_flags);......
}

我将在EnableDebugFeatures函数中调用ForceInterpretOnly函数,原因有三点:

  1. EnableDebugFeatures函数参数接收的是一个标志,我可以设置一个新的标志位用来表示是否需要调用ForceInterpretOnly函数。
  2. 每次APP启动的时候都会执行EnableDebugFeatures函数。
  3. EnableDebugFeatures函数被调用的时机好,它运行在fork出的APP进程中,并且在链接APP的dex中的方法前被调用。

实践

修改Zygote.java中的代码

Zygote.java文件的位置是:frameworks/base/core/java/com/android/internal/os/Zygote.java,在Zygote类中添加一个成员变量:

public static final int DEBUG_ENABLE_INTERPRET = 1 << 31;

在Zygote类forkAndSpecialize方法的开始部分添加下面的代码:

if (<APP包名>.equals(niceName)) {debugFlags |= DEBUG_ENABLE_INTERPRET;
}

修改dalvik_system_ZygoteHooks.cc中的代码

dalvik_system_ZygoteHooks.cc文件的位置是:art/runtime/native/dalvik_system_ZygoteHooks.cc,修改这个文件中的EnableDebugFeatures函数的代码。

向这个函数中添加下面的代码:

DEBUG_ENABLE_INTERPRET          = 1 << 31,
if ((debug_flags & DEBUG_ENABLE_INTERPRET) != 0) {Runtime::Current()->GetInstrumentation()->ForceInterpretOnly();debug_flags &= ~DEBUG_ENABLE_INTERPRET;
}

下面是对这个函数修改后的完整代码:

static void EnableDebugFeatures(uint32_t debug_flags) {// Must match values in dalvik.system.Zygote.enum {DEBUG_ENABLE_DEBUGGER           = 1,DEBUG_ENABLE_CHECKJNI           = 1 << 1,DEBUG_ENABLE_ASSERT             = 1 << 2,DEBUG_ENABLE_SAFEMODE           = 1 << 3,DEBUG_ENABLE_JNI_LOGGING        = 1 << 4,DEBUG_ENABLE_INTERPRET          = 1 << 31,};if ((debug_flags & DEBUG_ENABLE_CHECKJNI) != 0) {Runtime* runtime = Runtime::Current();JavaVMExt* vm = runtime->GetJavaVM();if (!vm->check_jni) {LOG(INFO) << "Late-enabling -Xcheck:jni";vm->SetCheckJniEnabled(true);// There's only one thread running at this point, so only one JNIEnv to fix up.Thread::Current()->GetJniEnv()->SetCheckJniEnabled(true);} else {LOG(INFO) << "Not late-enabling -Xcheck:jni (already on)";}debug_flags &= ~DEBUG_ENABLE_CHECKJNI;}if ((debug_flags & DEBUG_ENABLE_JNI_LOGGING) != 0) {gLogVerbosity.third_party_jni = true;debug_flags &= ~DEBUG_ENABLE_JNI_LOGGING;}Dbg::SetJdwpAllowed((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0);if ((debug_flags & DEBUG_ENABLE_DEBUGGER) != 0) {EnableDebugger();}debug_flags &= ~DEBUG_ENABLE_DEBUGGER;if ((debug_flags & DEBUG_ENABLE_SAFEMODE) != 0) {// Ensure that any (secondary) oat files will be interpreted.Runtime* runtime = Runtime::Current();runtime->AddCompilerOption("--compiler-filter=interpret-only");debug_flags &= ~DEBUG_ENABLE_SAFEMODE;}// This is for backwards compatibility with Dalvik.debug_flags &= ~DEBUG_ENABLE_ASSERT;if ((debug_flags & DEBUG_ENABLE_INTERPRET) != 0) {Runtime::Current()->GetInstrumentation()->ForceInterpretOnly();debug_flags &= ~DEBUG_ENABLE_INTERPRET;}if (debug_flags != 0) {LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags);}
}
原文地址: https://my.oschina.net/ibuwai/blog/528023

Android5.1.1源码 - 让某个APP以解释执行模式运行相关推荐

  1. [实践] Android5.1.1源码 - 让某个APP以解释执行模式运行

    [实践] Android5.1.1源码 - 让某个APP以解释执行模式运行 作者:寻禹@阿里聚安全 前言 本文的实践修改了Android5.1.1的源码. 本文只简单的讲了一下原理.在"实践 ...

  2. 【JVM源码解析】模板解释器解释执行Java字节码指令(上)

    本文由HeapDump性能社区首席讲师鸠摩(马智)授权整理发布 第17章-x86-64寄存器 不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Archit ...

  3. 金色新版萝卜影视系统源码+影视系统APP源码

    正文: 金色新版萝卜影视系统源码+影视系统APP源码,当前这个版本,可以说这是目前以来很牛的一款源码,无论是流畅度,还是原生稳定性都是非常稳定的. 环境:Android Studio,纯Java原生开 ...

  4. 360影视php视频系统源码,全新360影视2.0完整源码 双端APP+三级分销 附视频搭建教程...

    源码资源说明: 演示环境Linux+mysql+PHP5.6+伪静态支持这个程序基本环境要求不是很大,但有的地方还是要注意一下说一下需要的东西: 1.主机 2.域名 3.源码(会给大家打包,包含前后端 ...

  5. java社交婚恋交友app源码 安卓原生app+后台Spri ngMVC+Mybatis+mysql

    java社交婚恋交友app源码 安卓原生app+后台Spri ngMVC+Mybatis+mysql

  6. Ubuntu 16.04 下载、编译 友善之臂NanoPC-T2的Android5.1.1源码

    友善之臂官网: http://www.arm9.net 友善之臂NanoPC-T2维基使用手册链接: http://wiki.friendlyarm.com/wiki/index.php/NanoPC ...

  7. 送餐app+php,订餐APP源码Food Delivery App v2.1(客户端+服务端)

    android订餐APP源码Food Delivery App v2.1(客户端+服务端),包含android客户端源码.php+mysql服务端源码. Version 2.1 Food Delive ...

  8. Thinkphp高仿《拼多多拼团》商城系统源码 可封装APP 完美运营级

    介绍: [ThinkPHP内核]高仿<拼多多拼团>源码 可封装APP 多用户 完美运营级商城系统支持商家入驻 主要是基于ThinkPHP框架后台管理系统制作,是目前来说最新微信拼团系统.完 ...

  9. TinkPHP内核仿每推推51领啦试客源码_PC源码+WAP端+APP原生代码_自带5套精美模板

    TinkPHP内核仿每推推51领啦试客源码_PC源码+WAP端+APP原生代码_自带5套精美模板 源码说明:TinkPHP内核上制作而成,是全国领先的免费试用网站!程序全开源无加密!带有wap手机端, ...

最新文章

  1. 详解GPU的内存带宽与CPU的不同
  2. 开发日记-20190602 关键词 读书笔记《鸟哥的Linux私房菜-基础学习篇》
  3. python之tkinter图形界面
  4. 【亚马逊AWS】入门级别实践
  5. 调用函数,计算分段函数的值
  6. pandas-数据分析
  7. AI:你们是不是在等一顶红帽子?
  8. Flask项目中应用七牛云存储
  9. linux的可执行文件通常放在哪个目录中?写出该目录的路径.,实验2 Linux的基本操作与 使用vi编辑器 2010 (1)...
  10. c语言 static变量和函数
  11. vgremore 删除卷组
  12. itunes卸载工具_iTunes卸载麻烦
  13. Cisco交换机链路聚合配置
  14. 第二课:更换国内下载源(阿里源为例)
  15. 论文章的标题与页面的距离不一样(已解决)章标题设置相同的段前段后间距,但各章段前距不一致
  16. python智能推荐系统教程_最新Python黑马头条推荐系统项目视频教程(精讲)
  17. Linux内存:内存管理的实质 (转CU上frank_seng 的总结,感谢frank_seng )
  18. 玩转Dokuwiki
  19. wifi联网神器 android,WiFi连网神器
  20. 能够自动感知背景主色调的按钮

热门文章

  1. Linux系统安全工具tcpdump用法
  2. PHP5.4.3,有些插件不是你想用就能用的
  3. [最新答案V0.4版]微软等数据结构+算法面试100题[第41-60题答案]
  4. 基于节拍谱的语音音乐分类模型
  5. 学习Matlab强大的符号计算(解方程)
  6. 目标检测,目标识别的SAR数据集构建和标注
  7. [云炬创业基础笔记]第一章创业环境测试8
  8. 云炬Android开发笔记 11主界面-商品分类开发(多布局Section RecyclerView)
  9. 山西职称计算机考试报名时间 2014,2014山西省6月份职称计算机考试报名入口
  10. python如何注释掉一段代码_爬取出来的网页代码都被注释掉怎么解决?