先看官方文档如何创建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中的符号应该也是可以的。