首页
社区
课程
招聘
[分享]Art虚拟机JNIEnv相关分析(1)JavaVM &JNIEnv 的初始化流程
2021-4-14 10:52 7664

[分享]Art虚拟机JNIEnv相关分析(1)JavaVM &JNIEnv 的初始化流程

2021-4-14 10:52
7664

1.前言

     在安卓jni编程中,常常接触到JavaVMJNIEnv。比如jni动态注册中的JNI_OnLoad方法是我们需要实现的。使用的时候需要通过JavaVM获取JNIEnv。参考如下:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;    
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) 
    {        
        return -1;    
    }    
    LOGD("JNI Loaded");    
    return JNI_VERSION_1_6;
}

         除了动态注册的时候,在编写javanative方法在c层的实现时候,会使用到JNIEnv中提供的很多jni接口函数,比如FindClassCallObjectMethod等。

         以下将通过源码中追踪JavaVM&JNIEnv创建和初始化流程分析理解JNI层的一个大概实现机制。


    2.JavaVMJNIEnv创建流程分析

        安卓中zygote是所有Java层进程的鼻祖。zygote启动的时候会创建art虚拟机。JavaVMJNIEnv初始化的流程也会在zygote启动的时候完成。


    2.1  zygote启动流程


           (1).init进程解析init.rc

           安卓系统用户空间第一个进程init进程启动的时候会解析init.rc文件来确认使用那个一个init.zygotexx.rc文件来启动zygote进程。init.rc中相关内容如下:


      ...
      import /init.${ro.zygote}.rc
      ...


           由以上内容可知需要根据ro.zygote属性来确认加载具体的zygote启动配置的rc 文件来启动32位还是64位的zygote进程。在文件路径"system\core\rootdir"存在多个zygote启动的配置文件,如下所示:

            

      图片


         ro.zygote属性会有四种不同的取值:

      • zygote32:代表32位模式运行zygote进程

      • zygote32_64:代表32为主,64位为辅的方式启动zygote进程

      • zygote64:代表64位方式启动zygote进程

      • zygote64_32:代表64位为主,32位为辅的方式启动zygote进程

         

      以下是一加3手机在安卓10系统中的ro.zygote属性的值。如下所示:


        C:\Users\Qiang>adb shell getprop |findstr "ro.zygote"
        
        [ro.zygote]: [zygote64_32]


         根据以上ro.zygote值为zygote64_32,当前手机将会加载init.zygote64_32.rc文件。init.zygote64_32.rc内容如下:


          service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
              class main
              priority -20
              user root
              group root readproc reserved_disk
              ...
          
          service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
              class main
              priority -20
              user root
              ...


                该配置文件中配置启动了两个zygote进程,一个是执行app_process64,一个是执行app_process32。接下来分析app_process进程的相应逻辑代码。


            (2).app_process启动过程


               在安卓系统源码中,app_process64和app_process32使用的是同一份源代码。源码路径位于目录:


            frameworks\base\cmds\app_process


                 安卓系统在编译的时候根据Android.mk文件配置编译成了两个平台并重命名。如下是相应的Android.mk文件中配置情况。


              LOCAL_PATH:= $(call my-dir)
              ...
              include $(CLEAR_VARS)
              ...
              # 指定编译模块名称app_process
              LOCAL_MODULE:= app_process
              # 指定编译手机平台both表示32和64都要都要编译
              LOCAL_MULTILIB := both
              # 指定32位情况下编译名称变为app_process32
              LOCAL_MODULE_STEM_32 := app_process32
              # 指定64位情况下编译名称变为app_process64
              LOCAL_MODULE_STEM_64 := app_process64
              ...
              include $(BUILD_EXECUTABLE)
              ...


                    app_process模块中只有一个源文件app_main.cppapp_main.cpp文件中和zygote启动相关的逻辑代码如下:


                //AppRuntime继承AndroidRuntime类
                class AppRuntime : public AndroidRuntime
                {
                   ...
                }
                //zygote进程入口函数
                int main(int argc, char* const argv[])
                {
                    ...
                    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
                    ...
                    if (zygote) {
                        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
                    } 
                    ...
                }


                   以上代码中关键逻辑变成了AndroidRuntime->start,接下来分析一下AndroidRuntime中的相关逻辑。

                 

                 2.2  AndroidRuntime中的处理流程分析


                     AndroidRuntime类文件路径如下:


                  frameworks\base\core\jni\AndroidRuntime.cpp


                      在该类中的start方法如下


                    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
                    {
                        ...
                        /* start the virtual machine */
                        JniInvocation jni_invocation;
                        jni_invocation.Init(NULL);
                        JNIEnv* env;
                        if (startVm(&mJavaVM, &env, zygote) != 0) {
                            return;
                        }
                        onVmCreated(env);
                        ...
                    }


                          以上方法中调用了startVm方法创建JavaVM和JNIEnv。startVm中的关键逻辑如下:


                      int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
                      {
                        ...
                        if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
                             ALOGE("JNI_CreateJavaVM failed\n");
                             return -1;
                         }
                        ...
                      }


                           以上方法中执行JNI_CreateJavaVM方法来创建虚拟机。接下来分析JNI_CreateJavaVM中的处理逻辑。


                      2.3 JNI_CreateJavaVM中的处理流程分析


                           JNI_CreateJavaVM方法定义在"jni.h"文件中。具体实现在文件"libnativehelper\JniInvocation.cpp"中。该文件中JNI_CreateJavaVM实现如下:


                        MODULE_API jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
                           ...
                          return JniInvocationImpl::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
                        }


                        以上方法中使用了JniInvocationImpl类的JNI_CreateJavaVM方法。该类位于路径"libnativehelper\JniInvocation.cpp"中,他的JNI_CreateJavaVM方法实现如下:


                          jint JniInvocationImpl::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
                            return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
                          }


                                在以上实现中调用了JNI_CreateJavaVM_方法,该方法是一个函数指针,定义为如下:


                            jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*);


                                在JniInvocationImpl类中函数指针JNI_CreateJavaVM_赋值相关逻辑实现如下:


                              //该方法主要是根据打开的libart.so来查找函数符号
                              bool JniInvocationImpl::FindSymbol(FUNC_POINTER* pointer, const char* symbol) {
                                *pointer = GetSymbol(handle_, symbol);
                                ...
                                return true;
                              }
                              //
                              bool JniInvocationImpl::Init(const char* library) {
                                ...
                                //获取需要加载的so,此处为libart.so
                                library = GetLibrary(library, buffer);
                                //打开libart.so
                                handle_ = OpenLibrary(library);
                                ...
                                //此处表示获取libart.so中的JNI_CreateJavaVM函数地址并赋值给JNI_CreateJavaVM_
                                if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_CreateJavaVM_),
                                                "JNI_CreateJavaVM")) {
                                  return false;
                                }
                                ...
                              }


                                  以上分析可知最终调用的是libart.so中的JNI_CreateJavaVM方法来实现创建虚拟机。接下来分析art中的JNI_CreateJavaVM方法逻辑。


                              2.4 art中虚拟机创建分析

                                 在art源码中,JNI_CreateJavaVM方法实现源文件路径为:


                                art\runtime\jni\java_vm_ext.cc


                                   在该文件中JNI_CreateJavaVM的实现代码逻辑如下:


                                  extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
                                    ...
                                    //使用了Runtime::create创建虚拟机
                                    if (!Runtime::Create(options, ignore_unrecognized)) {
                                      return JNI_ERR;
                                    }
                                    ...
                                    Runtime* runtime = Runtime::Current();
                                    //启动虚拟机
                                    bool started = runtime->Start();
                                    ...
                                    //初始化JNIEnv
                                    *p_env = Thread::Current()->GetJniEnv();
                                    //初始化JavaVM
                                    *p_vm = runtime->GetJavaVM();
                                    return JNI_OK;
                                  }


                                       以上代码分析核心流程变成了Runtime::Create。接下来分析art中的Runtime类中的处理逻辑。


                                  2.5 Runtime中的处理逻辑

                                      

                                       Runtime类位于文件路径:


                                    art\runtime\runtime.cc


                                         在该类中Create调用的处理逻辑如下:


                                      //调用1
                                      bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {
                                        RuntimeArgumentMap runtime_options;
                                        return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&
                                            Create(std::move(runtime_options));
                                      }
                                      //调用2
                                      bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
                                        
                                        ...
                                        instance_ = new Runtime;
                                        ...
                                        //调用了Init方法初始化
                                        if (!instance_->Init(std::move(runtime_options))) {
                                          ...
                                        }
                                        return true;
                                      }


                                          以上调用中流程走到Init方法中,Init方法实现如下:


                                        bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
                                          ...
                                          std::string error_msg;
                                          //这个地方真正创建JavaVM
                                          java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg);
                                          if (java_vm_.get() == nullptr) {
                                            LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg;
                                            return false;
                                          }
                                          ...
                                          // 这个地方线程附加
                                          Thread* self = Thread::Attach("main", false, nullptr, false);
                                          ...
                                          }

                                           以上逻中完成JavaVM的初始化,接下来继续看一下JNIEnv的初始化流程。


                                        2.6  Thread类中的分析


                                           Thread类源码路径:


                                          art\runtime\runtime.cc


                                              在该类中Attach方法逻辑如下:


                                            //调用1
                                            Thread* Thread::Attach(const char* thread_name,
                                                                   bool as_daemon,
                                                                   jobject thread_group,
                                                                   bool create_peer) {
                                                  ...
                                                  //调用另一个Attach
                                             return Attach(thread_name, as_daemon, create_peer_action);
                                             }
                                             //调用2
                                             template <typename PeerAction>
                                            Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) {
                                              Runtime* runtime = Runtime::Current();
                                              ...
                                              Thread* self;
                                              {
                                                ...
                                                if (runtime->IsShuttingDownLocked()) {
                                                 ...
                                                } else {
                                                  Runtime::Current()->StartThreadBirth();
                                                  self = new Thread(as_daemon);
                                                  //初始化JNIEnv相关逻辑
                                                  bool init_success = self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
                                                  ...
                                                }
                                              }
                                             ...
                                            }


                                                以上代码中核心进入Thread->Init方法,Init方法实现如下:


                                              bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {
                                                ...
                                                if (jni_env_ext != nullptr) {
                                                 ...
                                                } else {
                                                  std::string error_msg;
                                                  //创建JNIEnv
                                                  tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm, &error_msg);
                                                  if (tlsPtr_.jni_env == nullptr) {
                                                    LOG(ERROR) << "Failed to create JNIEnvExt: " << error_msg;
                                                    return false;
                                                  }
                                                }
                                                ...
                                              }



                                              3.总结


                                                JavaVM创建关键:


                                                JavaVMExt::Create(this, runtime_options, &error_msg);


                                                  JNIEnv创建关键:


                                                  JNIEnvExt::Create(this, java_vm, &error_msg);

                                                  原文链接

                                                   



                                                  [CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

                                                  收藏
                                                  点赞1
                                                  打赏
                                                  分享
                                                  最新回复 (0)
                                                  游客
                                                  登录 | 注册 方可回帖
                                                  返回