安卓高版本c/c++调用java,通过JNI_CreateJavaVM/JNI_GetCreatedJavaVMs创建虚拟机实例

先看官方文档如何创建java vm  : The Invocation API

    #include <jni.h>       /* where everything is defined */
    ...
    JavaVM *jvm;       /* denotes a Java VM */
    JNIEnv *env;       /* pointer to native method interface */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=/usr/lib/java";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    delete options;
    /* invoke the Main.test method using the JNI */
    jclass cls = env->FindClass("Main");
    jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
    env->CallStaticVoidMethod(cls, mid, 100);
    /* We are done. */
    jvm->DestroyJavaVM();

比较简单就是通过JNI_CreateJavaVM 创建jni环境,对于一般系统需要依赖JDK中的libjvm.so,对于安卓系统5.0及以下Dalvik 虚拟机libdvm.so,5.1及以上版本切换到了Art 虚拟机libart.so。此外安卓runtime使用的libnativehelper.so封装了相关接口也可以用来创建jni环境(本文使用此方法)。

#include <jni.h>
#include "JniInvocation.h"
#include <dlfcn.h>
#include <gst/gst.h>

typedef JniInvocationImpl*(*JniInvocationCreate_t)();
typedef int (*JniInvocationInit_t)(struct JniInvocationImpl* , const char* );
typedef jint (*JNI_CreateJavaVM_t)(JavaVM** , JNIEnv** , void* );
typedef jint (*JNI_GetCreatedJavaVMs_t)(JavaVM** , jsize , jsize* );
typedef jint (*JNI_GetDefaultJavaVMInitArgs_t)(void*); 

extern "C" jint create_java_vm_wrapper(JavaVM **, JNIEnv ** , void *);
extern "C" jint get_java_vm_wrapper(JavaVM**, jsize , jsize* );

/*
 * 当前进程没有创建过JavaVM,通过JNI_CreateJavaVM创建JavaVM
 * 
 */
jint create_java_vm_wrapper(JavaVM ** p_vm, JNIEnv ** p_env, void *vm_args){
    void *handle = NULL;
    handle  = dlopen("libnativehelper.so", RTLD_NOLOAD);

    if( handle == NULL){
        handle = dlopen("libnativehelper.so", RTLD_GLOBAL| RTLD_LAZY);
    }else{
        GST_DEBUG ("libnativehelper already load");
    }
    if (handle == NULL) {   
        GST_DEBUG ("libnativehelper  dlopen error");   
        return JNI_ERR;
     }

    JniInvocationCreate_t JniInvocationCreate_func= (JniInvocationCreate_t)dlsym(handle, "JniInvocationCreate");

    if(JniInvocationCreate_func == NULL){
        GST_DEBUG ("JniInvocationCreate not found"); 
        dlclose(handle);  
        return JNI_ERR;
    }

    JniInvocationInit_t JniInvocationInit_func= (JniInvocationInit_t)dlsym(handle, "JniInvocationInit");

    if(JniInvocationInit_func == NULL){
        GST_DEBUG ("JniInvocationInit not found");   
        dlclose(handle); 
        return JNI_ERR;
    }

    JNI_GetDefaultJavaVMInitArgs_t JNI_GetDefaultJavaVMInitArgs_func= (JNI_GetDefaultJavaVMInitArgs_t)dlsym(handle, "JNI_GetDefaultJavaVMInitArgs");

    if(JNI_GetDefaultJavaVMInitArgs_func == NULL){
        GST_DEBUG ("JNI_GetDefaultJavaVMInitArgs not found");   
        dlclose(handle);
        return JNI_ERR;
    }

    JNI_CreateJavaVM_t JNI_CreateJavaVM_func= (JNI_CreateJavaVM_t)dlsym(handle, "JNI_CreateJavaVM");
    
    if(JNI_CreateJavaVM_func == NULL){
        GST_DEBUG ("JNI_CreateJavaVM not found");
        dlclose(handle);
        return JNI_ERR;
    }

    JniInvocationImpl *jvImp = JniInvocationCreate_func();
    if(JniInvocationInit_func(jvImp,NULL) != JNI_OK){
        dlclose(handle);
        return JNI_ERR;
    }
   // JNI_GetDefaultJavaVMInitArgs_func(vm_args);
    jint ret = JNI_CreateJavaVM_func(p_vm, p_env, vm_args);
    if(JNI_CreateJavaVM_func(p_vm, p_env, vm_args) != JNI_OK){
        dlclose(handle);
        return JNI_ERR;
    }
    dlclose(handle);
    return JNI_OK;
}
/*
 * 当前进程已经创建过JavaVM,通过JNI_GetCreatedJavaVMs获取JavaVM
 * 
 */
jint get_java_vm_wrapper(JavaVM** vms, jsize size, jsize* vm_count){

    void *handle = NULL;
    handle  = dlopen("libnativehelper.so", RTLD_NOLOAD);

    if( handle == NULL){
        handle = dlopen("libnativehelper.so", RTLD_GLOBAL| RTLD_LAZY);
    }else{
        GST_DEBUG ("libnativehelper already load"); 
    }

    if (handle == NULL) {   
        GST_DEBUG ("libnativehelper dlopen error");   
        return JNI_ERR;
    }

    JNI_GetCreatedJavaVMs_t JNI_GetCreatedJavaVMs_func= (JNI_GetCreatedJavaVMs_t)dlsym(handle, "JNI_GetCreatedJavaVMs");

    if(JNI_GetCreatedJavaVMs_func == NULL){
        GST_DEBUG ("JNI_GetCreatedJavaVMs error"); 
        dlclose(handle);  
        return JNI_ERR;
    }

    jint ret = JNI_GetCreatedJavaVMs_func(vms,size,vm_count);

    if (ret != JNI_OK || vm_count == 0 || vms == NULL) {
        GST_DEBUG("Failed to get JVM instance");
        dlclose(handle);
        return JNI_ERR;
    }

    dlclose(handle);
    return JNI_OK;
}

创建jvm方式同:

    JNIEnv *env;
    JavaVMInitArgs vm_args;
    JavaVMOption options[6];

    options[0].optionString = "-verbose:jni";
    options[1].optionString = "-verbose:gc";
    options[2].optionString = "-Xcheck:jni";
    options[3].optionString = "-Xdebug"; 
    options[4].optionString = "-Djava.compiler=NONE";
    // JavaVMOption ="-Djava.ext.dirs=JAR
    //options[5].optionString = "-Djava.class.path=/system_ext/framework/media-framework.jar";  
   
    vm_args.version = JNI_VERSION_1_6;
    vm_args.options = options;
    vm_args.nOptions = 6;
    vm_args.ignoreUnrecognized = JNI_TRUE;
   if ((ret = create_java_vm_wra_t (&java_vm, &env, &vm_args)) != JNI_OK)
       goto create_failed;
 ......

问题:

安卓高版本中的问题:

06-26 21:15:19.413   999   999 F DEBUG   : Revision: '1234'
06-26 21:15:19.414   999   999 F DEBUG   : ABI: 'arm'
06-26 21:15:19.414   999   999 F DEBUG   : Timestamp: 2023-06-26 21:15:19+0800
06-26 21:15:19.415   999   999 F DEBUG   : pid: 664, tid: 798, name: mediaserver  >>> /system/bin/mediaserver <<<
06-26 21:15:19.418   999   999 F DEBUG   : uid: 1013
06-26 21:15:19.419   999   999 F DEBUG   : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
06-26 21:15:19.419   999   999 F DEBUG   :     r0  00000000  r1  0000031e  r2  00000006  r3  ef4027a8
06-26 21:15:19.419   999   999 F DEBUG   :     r4  ef4027bc  r5  ef4027a0  r6  00000298  r7  0000016b
06-26 21:15:19.420   999   999 F DEBUG   :     r8  ef4027a8  r9  ef4027b8  r10 ef4027d8  r11 ef4027c8
06-26 21:15:19.421   999   999 F DEBUG   :     ip  0000031e  sp  ef402778  lr  f5202b0d  pc  f5202b20
06-26 21:15:19.486   999   999 F DEBUG   : Abort message: ''
06-26 21:15:19.486   999   999 F DEBUG   : backtrace:
06-26 21:15:19.486   999   999 F DEBUG   :       #00 pc 00062b20  /apex/com.android.runtime/lib/bionic/libc.so (abort+172) (BuildId: aa529401e59439e0c6bc7c514840a24d)
06-26 21:15:19.486   999   999 F DEBUG   :       #01 pc 00001a2d  /system/lib/libsigchain.so (AddSpecialSignalHandlerFn+16) (BuildId: 414697320c5451e5b4b39987dcd31dfc)
06-26 21:15:19.486   999   999 F DEBUG   :       #02 pc 00196a1b  /apex/com.android.art/lib/libart.so (art::FaultManager::Init()+94) (BuildId: 1b7b44258a42f1839a8a61313331cb4a)
06-26 21:15:19.486   999   999 F DEBUG   :       #03 pc 004024a1  /apex/com.android.art/lib/libart.so (art::Runtime::Init(art::RuntimeArgumentMap&&)+9808) (BuildId: 1b7b44258a42f1839a8a61313331cb4a)
06-26 21:15:19.486   999   999 F DEBUG   :       #04 pc 00404ac9  /apex/com.android.art/lib/libart.so (art::Runtime::Create(std::__1::vector<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void const*>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void const*> > > const&, bool)+68) (BuildId: 1b7b44258a42f1839a8a61313331cb4a)
06-26 21:15:19.487   999   999 F DEBUG   :       #05 pc 00298bef  /apex/com.android.art/lib/libart.so (JNI_CreateJavaVM+582) (BuildId: 1b7b44258a42f1839a8a61313331cb4a)
06-26 21:15:19.487   999   999 F DEBUG   :       #06 pc 000018c3  /system_ext/lib/libnativehelperwrapper.so (create_java_vm_wra+218)
06-26 21:15:19.487   999   999 F DEBUG   :       #07 pc 00021617  /system_ext/lib/gstreamer-1.0/libgstandroidmedia.so (gst_amc_jni_initialize_internal+286)
06-26 21:15:19.487   999   999 F DEBUG   :       #08 pc 0004e6d3  /system_ext/lib/libglib-2.0.so (g_once_impl+62)
06-26 21:15:19.487   999   999 F DEBUG   :       #09 pc 000214d3  /system_ext/lib/gstreamer-1.0/libgstandroidmedia.so (gst_amc_jni_initialize+38)
06-26 21:15:19.487   999   999 F DEBUG   :       #10 pc 0000b581  /system_ext/lib/gstreamer-1.0/libgstandroidmedia.so (plugin_init+52)
06-26 21:15:19.487   999   999 F DEBUG   :       #11 pc 0006e6a9  /system_ext/lib/libgstreamer-1.0.so (gst_plugin_register_func+436)
06-26 21:15:19.487   999   999 F DEBUG   :       #12 pc 0006f38d  /system_ext/lib/libgstreamer-1.0.so (_priv_gst_plugin_load_file_for_registry+1232)
06-26 21:15:19.487   999   999 F DEBUG   :       #13 pc 0007c1dd  /system_ext/lib/libgstreamer-1.0.so (gst_registry_scan_plugin_file+152)
06-26 21:15:19.487   999   999 F DEBUG   :       #14 pc 0007bd4f  /system_ext/lib/libgstreamer-1.0.so (gst_registry_scan_path_level+526)
06-26 21:15:19.487   999   999 F DEBUG   :       #15 pc 0007aa4d  /system_ext/lib/libgstreamer-1.0.so (gst_registry_scan_path_internal+36)
06-26 21:15:19.487   999   999 F DEBUG   :       #16 pc 0007af31  /system_ext/lib/libgstreamer-1.0.so (gst_update_registry+548)
06-26 21:15:19.487   999   999 F DEBUG   :       #17 pc 0002d895  /system_ext/lib/libgstreamer-1.0.so (init_post+872)
06-26 21:15:19.487   999   999 F DEBUG   :       #18 pc 0003e447  /system_ext/lib/libglib-2.0.so (g_option_context_parse+1366)
06-26 21:15:19.487   999   999 F DEBUG   :       #19 pc 0002da5d  /system_ext/lib/libgstreamer-1.0.so (gst_init_check+92)

通过backtrace可以看到JNI_CreateJavaVM时挂在libart.so->libsigchain.so->libc.so(abort函数)

通过addr2line可以反编译源码位置,下面我们看下大体流程。

Android11/art$ ./runtime/jni/java_vm_ext.cc:

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  ScopedTrace trace(__FUNCTION__);
  const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);
  if (JavaVMExt::IsBadJniVersion(args->version)) {
    LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
    return JNI_EVERSION;
  }
  RuntimeOptions options;
  for (int i = 0; i < args->nOptions; ++i) {
    JavaVMOption* option = &args->options[i];
    options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
  }
  bool ignore_unrecognized = args->ignoreUnrecognized;
  if (!Runtime::Create(options, ignore_unrecognized)) {
    return JNI_ERR;
  }

  // Initialize native loader. This step makes sure we have
  // everything set up before we start using JNI.
  android::InitializeNativeLoader();

  Runtime* runtime = Runtime::Current();
  bool started = runtime->Start();
  if (!started) {
    delete Thread::Current()->GetJniEnv();
    delete runtime->GetJavaVM();
    LOG(WARNING) << "CreateJavaVM failed";
    return JNI_ERR;
  }

  *p_env = Thread::Current()->GetJniEnv();
  *p_vm = runtime->GetJavaVM();
  return JNI_OK;
}

主要调用 Runtime::Create和Start(),Create中继续调用了init函数

bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
  // TODO: acquire a static mutex on Runtime to avoid racing.
  if (Runtime::instance_ != nullptr) {
    return false;
  }
  instance_ = new Runtime;
  Locks::SetClientCallback(IsSafeToCallAbort);
  if (!instance_->Init(std::move(runtime_options))) {
    // TODO: Currently deleting the instance will abort the runtime on destruction. Now This will
    // leak memory, instead. Fix the destructor. b/19100793.
    // delete instance_;
    instance_ = nullptr;
    return false;
  }
  return true;
}
BlockSignals();
  InitPlatformSignalHandlers();

  // Change the implicit checks flags based on runtime architecture.
  switch (kRuntimeISA) {
    case InstructionSet::kArm:
    case InstructionSet::kThumb2:
    case InstructionSet::kX86:
    case InstructionSet::kArm64:
    case InstructionSet::kX86_64:
      implicit_null_checks_ = true;
      // Historical note: Installing stack protection was not playing well with Valgrind.
      implicit_so_checks_ = true;
      break;
    default:
      // Keep the defaults.
      break;
  }


  if (!no_sig_chain_) {
    // Dex2Oat's Runtime does not need the signal chain or the fault handler.
    if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {

      fault_manager.Init(); // 根据backtrace是这里

Android11/art$ ./runtime/fault_handler.cc:

  CHECK(!initialized_);
  sigset_t mask;
  sigfillset(&mask);
  sigdelset(&mask, SIGABRT);
  sigdelset(&mask, SIGBUS);
  sigdelset(&mask, SIGFPE);
  sigdelset(&mask, SIGILL);
  sigdelset(&mask, SIGSEGV);

  SigchainAction sa = {
    .sc_sigaction = art_fault_handler,
    .sc_mask = mask,
    .sc_flags = 0UL,
  };

  AddSpecialSignalHandlerFn(SIGSEGV, &sa); //继续走到这
  initialized_ = true;
}

Android11/art/sigchainlib/ 定位到最终挂的位置,搜索可以看到有两个文件定义AddSpecialSignalHandlerFn函数,这也是导致问题的关键!!!

grep AddSpecialSignalHandlerFn ./ -nir
./version-script64.txt:4:  AddSpecialSignalHandlerFn;
./sigchain.cc:488:extern "C" void AddSpecialSignalHandlerFn(int signal, SigchainAction* sa) {
./sigchain_test.cc:59:    art::AddSpecialSignalHandlerFn(SIGSEGV, &action);
./sigchain.h:34:extern "C" void AddSpecialSignalHandlerFn(int signal, SigchainAction* sa);
./sigchain_dummy.cc:37:extern "C" void AddSpecialSignalHandlerFn(int signal ATTRIBUTE_UNUSED,
./version-script32.txt:4:  AddSpecialSignalHandlerFn;

通过反编译我们是知道实际是挂在./sigchain_dummy.cc中的AddSpecialSignalHandlerFn

调用到sigchain_dummy这里是必然会挂掉的,这就奇怪了[--
Android11/frameworks/base/core/jni/AndroidRuntime.cpp :AndroidRuntime::startVm 启动虚拟机也是同样的流程为什么不会调用到这里呢?

[---------------------------------------------------------------------------------------------------------------------------

  art虚拟机启动过程分析_boot.art_Carlos_0419的博客-CSDN博客  

Android系统的启动流程_mb5fe55a9dbe9dd的技术博客_51CTO博客

------------------------------------------------------------------------------------------------------------------------------]

extern "C" void EnsureFrontOfChain(int signal ATTRIBUTE_UNUSED) {
  log("EnsureFrontOfChain is not exported by the main executable.");
  abort();
}

extern "C" void AddSpecialSignalHandlerFn(int signal ATTRIBUTE_UNUSED,
                                          SigchainAction* sa ATTRIBUTE_UNUSED) {
  log("zhaodebug dummy SetSpecialSignalHandlerFn is not exported by the main executable.");
  abort();
}

extern "C" void RemoveSpecialSignalHandlerFn(int signal ATTRIBUTE_UNUSED,
                                             bool (*fn)(int, siginfo_t*, void*) ATTRIBUTE_UNUSED) {
  log("SetSpecialSignalHandlerFn is not exported by the main executable.");
  abort();
}

extern "C" void SkipAddSignalHandler(bool value ATTRIBUTE_UNUSED) {
  log("SkipAddSignalHandler is not exported by the main executable.");
  abort();
}

我们看下libsigchain的编译bp:

cc_library {
    name: "libsigchain",
    defaults: ["art_defaults"],
    visibility: [
        // TODO(b/133140750): Clean this up.
        "//frameworks/base/cmds/app_process",
    ],

    host_supported: true,
    target: {
        linux: {
            shared: {
                srcs: ["sigchain.cc"],
            },
            static: {
                srcs: ["sigchain.cc"],
            },
        },

        darwin: {
            srcs: ["sigchain_dummy.cc"],
        },

        android: {
            whole_static_libs: ["libasync_safe"],
        },
    },

    export_include_dirs: ["."],
    apex_available: [
        "com.android.art.release",
        "com.android.art.debug",
        // TODO(b/142944931) Clean this up. This is due to the dependency from
        // app_process
        "//apex_available:platform",
    ],
}

发现libsigchain.so是使用sigchain_dummy.cc,而libsigchain.a是使用sigchain.cc,

通过 readelf -d libart.so 看到art同样连接的也是动态库,但是却没有走到这里?而是走的静态库的。

readelf -d libart.so

Dynamic section at offset 0x4f74f4 contains 41 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libartpalette.so]
 0x00000001 (NEEDED)                     Shared library: [libnativebridge.so]
 0x00000001 (NEEDED)                     Shared library: [libnativeloader.so]
 0x00000001 (NEEDED)                     Shared library: [libbacktrace.so]
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libbase.so]
 0x00000001 (NEEDED)                     Shared library: [libunwindstack.so]
 0x00000001 (NEEDED)                     Shared library: [libsigchain.so]
 0x00000001 (NEEDED)                     Shared library: [libartbase.so]
 0x00000001 (NEEDED)                     Shared library: [libdexfile.so]

实际是在这里连接到进程的二进制程序中:

readelf -s  app_process32  |grep -i AddSpecialSignalHandlerFn
    87: 00004581   228 FUNC    GLOBAL PROTECTED   15 AddSpecialSignalHandlerFn
   400: 00004581   228 FUNC    GLOBAL PROTECTED   15 AddSpecialSignalHandlerFn
 

Android11/frameworks/base/cmds/app_process:

cc_binary {
    name: "app_process",

    srcs: ["app_main.cpp"],

    multilib: {
        lib32: {
            version_script: ":art_sigchain_version_script32.txt",
            suffix: "32",
        },
        lib64: {
            version_script: ":art_sigchain_version_script64.txt",
            suffix: "64",
        },
    },

    ldflags: ["-Wl,--export-dynamic"],

    shared_libs: [
        "libandroid_runtime",
        "libbinder",
        "libcutils",
        "libdl",
        "libhidlbase",
        "liblog",
        "libnativeloader",
        "libutils",

        // This is a list of libraries that need to be included in order to avoid
        // bad apps. This prevents a library from having a mismatch when resolving
        // new/delete from an app shared library.
        // See b/21032018 for more details.
        "libwilhelm",
    ],

    whole_static_libs: ["libsigchain"], //在这里链接来静态库

    compile_multilib: "both",

app_process中的导入的libsigchain.a符号会覆盖libsigchain.so中符号,也就是运行时候实际使用的是libsigchain.a中符号,所以不会挂掉。

所以到这我们就清楚了为何我们自己的程序挂到libsigchain,而AndroidRuntime启动虚拟机确实可以的。

综上,总结使用方式:

所以对我们自己的程序需要使用JNI_CreateJavaVM创建jvm实例的场景,需要主动连接静态libsigchain.a到我们的程序(这个libsigchain.a需要安卓SDK源码工程的),如果我们不主动去链接libsigchain.a,当我们使用JNI_CreateJavaVM 时候libart就会链接libsigchain.so从而导致问题。

如果我们没有办法依赖安卓SDK源码工程编译,无法链接libsigchain.a,可以自己实现libsigchain中的符号覆盖libsigchain.so中的符号应该也是可以的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值