You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
5.8 KiB
186 lines
5.8 KiB
/* |
|
* Copyright (C) 2013 The Android Open Source Project |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
#include "JniInvocation.h" |
|
|
|
#include <dlfcn.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
#include <cstddef> |
|
|
|
#define LOG_TAG "JniInvocation" |
|
#include "cutils/log.h" |
|
|
|
#ifdef __ANDROID__ |
|
#include <sys/system_properties.h> |
|
#endif |
|
|
|
JniInvocation* JniInvocation::jni_invocation_ = NULL; |
|
|
|
JniInvocation::JniInvocation() : |
|
handle_(NULL), |
|
JNI_GetDefaultJavaVMInitArgs_(NULL), |
|
JNI_CreateJavaVM_(NULL), |
|
JNI_GetCreatedJavaVMs_(NULL) { |
|
|
|
LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized"); |
|
jni_invocation_ = this; |
|
} |
|
|
|
JniInvocation::~JniInvocation() { |
|
jni_invocation_ = NULL; |
|
if (handle_ != NULL) { |
|
dlclose(handle_); |
|
} |
|
} |
|
|
|
#ifdef __ANDROID__ |
|
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2"; |
|
static const char* kDebuggableSystemProperty = "ro.debuggable"; |
|
#endif |
|
static const char* kLibraryFallback = "libart.so"; |
|
|
|
template<typename T> void UNUSED(const T&) {} |
|
|
|
const char* JniInvocation::GetLibrary(const char* library, char* buffer) { |
|
#ifdef __ANDROID__ |
|
const char* default_library; |
|
|
|
char debuggable[PROP_VALUE_MAX]; |
|
__system_property_get(kDebuggableSystemProperty, debuggable); |
|
|
|
if (strcmp(debuggable, "1") != 0) { |
|
// Not a debuggable build. |
|
// Do not allow arbitrary library. Ignore the library parameter. This |
|
// will also ignore the default library, but initialize to fallback |
|
// for cleanliness. |
|
library = kLibraryFallback; |
|
default_library = kLibraryFallback; |
|
} else { |
|
// Debuggable build. |
|
// Accept the library parameter. For the case it is NULL, load the default |
|
// library from the system property. |
|
if (buffer != NULL) { |
|
if (__system_property_get(kLibrarySystemProperty, buffer) > 0) { |
|
default_library = buffer; |
|
} else { |
|
default_library = kLibraryFallback; |
|
} |
|
} else { |
|
// No buffer given, just use default fallback. |
|
default_library = kLibraryFallback; |
|
} |
|
} |
|
#else |
|
UNUSED(buffer); |
|
const char* default_library = kLibraryFallback; |
|
#endif |
|
if (library == NULL) { |
|
library = default_library; |
|
} |
|
|
|
return library; |
|
} |
|
|
|
bool JniInvocation::Init(const char* library) { |
|
#ifdef __ANDROID__ |
|
char buffer[PROP_VALUE_MAX]; |
|
#else |
|
char* buffer = NULL; |
|
#endif |
|
library = GetLibrary(library, buffer); |
|
// Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed. |
|
// This is due to the fact that it is possible that some threads might have yet to finish |
|
// exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is |
|
// unloaded. |
|
const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE; |
|
handle_ = dlopen(library, kDlopenFlags); |
|
if (handle_ == NULL) { |
|
if (strcmp(library, kLibraryFallback) == 0) { |
|
// Nothing else to try. |
|
ALOGE("Failed to dlopen %s: %s", library, dlerror()); |
|
return false; |
|
} |
|
// Note that this is enough to get something like the zygote |
|
// running, we can't property_set here to fix this for the future |
|
// because we are root and not the system user. See |
|
// RuntimeInit.commonInit for where we fix up the property to |
|
// avoid future fallbacks. http://b/11463182 |
|
ALOGW("Falling back from %s to %s after dlopen error: %s", |
|
library, kLibraryFallback, dlerror()); |
|
library = kLibraryFallback; |
|
handle_ = dlopen(library, kDlopenFlags); |
|
if (handle_ == NULL) { |
|
ALOGE("Failed to dlopen %s: %s", library, dlerror()); |
|
return false; |
|
} |
|
} |
|
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_), |
|
"JNI_GetDefaultJavaVMInitArgs")) { |
|
return false; |
|
} |
|
if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_), |
|
"JNI_CreateJavaVM")) { |
|
return false; |
|
} |
|
if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_), |
|
"JNI_GetCreatedJavaVMs")) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
jint JniInvocation::JNI_GetDefaultJavaVMInitArgs(void* vmargs) { |
|
return JNI_GetDefaultJavaVMInitArgs_(vmargs); |
|
} |
|
|
|
jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { |
|
return JNI_CreateJavaVM_(p_vm, p_env, vm_args); |
|
} |
|
|
|
jint JniInvocation::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) { |
|
return JNI_GetCreatedJavaVMs_(vms, size, vm_count); |
|
} |
|
|
|
bool JniInvocation::FindSymbol(void** pointer, const char* symbol) { |
|
*pointer = dlsym(handle_, symbol); |
|
if (*pointer == NULL) { |
|
ALOGE("Failed to find symbol %s: %s\n", symbol, dlerror()); |
|
dlclose(handle_); |
|
handle_ = NULL; |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
JniInvocation& JniInvocation::GetJniInvocation() { |
|
LOG_ALWAYS_FATAL_IF(jni_invocation_ == NULL, |
|
"Failed to create JniInvocation instance before using JNI invocation API"); |
|
return *jni_invocation_; |
|
} |
|
|
|
extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) { |
|
return JniInvocation::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args); |
|
} |
|
|
|
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { |
|
return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args); |
|
} |
|
|
|
extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) { |
|
return JniInvocation::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count); |
|
}
|
|
|