/* * 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 "nativehelper/JniInvocation.h" #ifdef _WIN32 #include <windows.h> #else #include <dlfcn.h> #endif #include <stdlib.h> #include <string.h> #include <cstddef> #define LOG_TAG "JniInvocation" #include <log/log.h> #ifdef __ANDROID__ #include <sys/system_properties.h> #endif #include "android-base/errors.h" #include "JniConstants.h" namespace { template <typename T> void UNUSED(const T&) {} bool IsDebuggable() { #ifdef __ANDROID__ char debuggable[PROP_VALUE_MAX] = {0}; __system_property_get("ro.debuggable", debuggable); return strcmp(debuggable, "1") == 0; #else return false; #endif } int GetLibrarySystemProperty(char* buffer) { #ifdef __ANDROID__ return __system_property_get("persist.sys.dalvik.vm.lib.2", buffer); #else UNUSED(buffer); return 0; #endif } #ifdef _WIN32 #define FUNC_POINTER FARPROC #else #define FUNC_POINTER void* #endif void* OpenLibrary(const char* filename) { #ifdef _WIN32 return LoadLibrary(filename); #else // 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; return dlopen(filename, kDlopenFlags); #endif } int CloseLibrary(void* handle) { #ifdef _WIN32 return FreeLibrary(static_cast<HMODULE>(handle)); #else return dlclose(handle); #endif } FUNC_POINTER GetSymbol(void* handle, const char* symbol) { #ifdef _WIN32 return GetProcAddress(static_cast<HMODULE>(handle), symbol); #else return dlsym(handle, symbol); #endif } std::string GetError() { #ifdef _WIN32 return android::base::SystemErrorCodeToString(GetLastError()); #else return std::string(dlerror()); #endif } } // namespace struct JniInvocationImpl final { public: JniInvocationImpl(); ~JniInvocationImpl(); bool Init(const char* library); // static const char* GetLibrary(const char* library, char* buffer); static const char* GetLibrary(const char* library, char* buffer, bool (*is_debuggable)() = IsDebuggable, int (*get_library_system_property)(char* buffer) = GetLibrarySystemProperty); static JniInvocationImpl& GetJniInvocation(); jint JNI_GetDefaultJavaVMInitArgs(void* vmargs); jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args); jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count); private: JniInvocationImpl(const JniInvocationImpl&) = delete; JniInvocationImpl& operator=(const JniInvocationImpl&) = delete; bool FindSymbol(FUNC_POINTER* pointer, const char* symbol); static JniInvocationImpl* jni_invocation_; // Handle to library opened with dlopen(). Library exports // JNI_GetDefaultJavaVMInitArgs, JNI_CreateJavaVM, JNI_GetCreatedJavaVMs. void* handle_; jint (*JNI_GetDefaultJavaVMInitArgs_)(void*); jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*); jint (*JNI_GetCreatedJavaVMs_)(JavaVM**, jsize, jsize*); friend class JNIInvocation_Debuggable_Test; friend class JNIInvocation_NonDebuggable_Test; }; // Check JniInvocationImpl size is same as fields, e.g. no vtable present. static_assert(sizeof(JniInvocationImpl) == 4 * sizeof(uintptr_t)); JniInvocationImpl* JniInvocationImpl::jni_invocation_ = NULL; JniInvocationImpl::JniInvocationImpl() : 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; } JniInvocationImpl::~JniInvocationImpl() { jni_invocation_ = NULL; if (handle_ != NULL) { CloseLibrary(handle_); } } static const char* kLibraryFallback = "libart.so"; const char* JniInvocationImpl::GetLibrary(const char* library, char* buffer, bool (*is_debuggable)(), int (*get_library_system_property)(char* buffer)) { #ifdef __ANDROID__ const char* default_library; if (!is_debuggable()) { // 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 (get_library_system_property(buffer) > 0) { default_library = buffer; } else { default_library = kLibraryFallback; } } else { // No buffer given, just use default fallback. default_library = kLibraryFallback; } } #else UNUSED(buffer); UNUSED(is_debuggable); UNUSED(get_library_system_property); const char* default_library = kLibraryFallback; #endif if (library == NULL) { library = default_library; } return library; } bool JniInvocationImpl::Init(const char* library) { #ifdef __ANDROID__ char buffer[PROP_VALUE_MAX]; #else char* buffer = NULL; #endif library = GetLibrary(library, buffer); handle_ = OpenLibrary(library); if (handle_ == NULL) { if (strcmp(library, kLibraryFallback) == 0) { // Nothing else to try. ALOGE("Failed to dlopen %s: %s", library, GetError().c_str()); 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, GetError().c_str()); library = kLibraryFallback; handle_ = OpenLibrary(library); if (handle_ == NULL) { ALOGE("Failed to dlopen %s: %s", library, GetError().c_str()); return false; } } if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetDefaultJavaVMInitArgs_), "JNI_GetDefaultJavaVMInitArgs")) { return false; } if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_CreateJavaVM_), "JNI_CreateJavaVM")) { return false; } if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetCreatedJavaVMs_), "JNI_GetCreatedJavaVMs")) { return false; } return true; } jint JniInvocationImpl::JNI_GetDefaultJavaVMInitArgs(void* vmargs) { return JNI_GetDefaultJavaVMInitArgs_(vmargs); } jint JniInvocationImpl::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { return JNI_CreateJavaVM_(p_vm, p_env, vm_args); } jint JniInvocationImpl::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) { return JNI_GetCreatedJavaVMs_(vms, size, vm_count); } bool JniInvocationImpl::FindSymbol(FUNC_POINTER* pointer, const char* symbol) { *pointer = GetSymbol(handle_, symbol); if (*pointer == NULL) { ALOGE("Failed to find symbol %s: %s\n", symbol, GetError().c_str()); CloseLibrary(handle_); handle_ = NULL; return false; } return true; } JniInvocationImpl& JniInvocationImpl::GetJniInvocation() { LOG_ALWAYS_FATAL_IF(jni_invocation_ == NULL, "Failed to create JniInvocation instance before using JNI invocation API"); return *jni_invocation_; } MODULE_API jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) { return JniInvocationImpl::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args); } MODULE_API jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { // Ensure any cached heap objects from previous VM instances are // invalidated. There is no notification here that a VM is destroyed. These // cached objects limit us to one VM instance per process. JniConstants::Uninitialize(); return JniInvocationImpl::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args); } MODULE_API jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) { return JniInvocationImpl::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count); } MODULE_API JniInvocationImpl* JniInvocationCreate() { return new JniInvocationImpl(); } MODULE_API void JniInvocationDestroy(JniInvocationImpl* instance) { delete instance; } MODULE_API int JniInvocationInit(JniInvocationImpl* instance, const char* library) { return instance->Init(library) ? 1 : 0; } MODULE_API const char* JniInvocationGetLibrary(const char* library, char* buffer) { return JniInvocationImpl::GetLibrary(library, buffer); } MODULE_API const char* JniInvocation::GetLibrary(const char* library, char* buffer, bool (*is_debuggable)(), int (*get_library_system_property)(char* buffer)) { return JniInvocationImpl::GetLibrary(library, buffer, is_debuggable, get_library_system_property); }