| /* |
| * 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" |
| |
| #include <string> |
| |
| #ifdef _WIN32 |
| #include <windows.h> |
| #else |
| #include <dlfcn.h> |
| #endif |
| |
| #define LOG_TAG "JniInvocation" |
| #include <log/log.h> |
| |
| #if defined(__ANDROID__) |
| #include <sys/system_properties.h> |
| #elif defined(_WIN32) |
| #include <android-base/errors.h> |
| #endif |
| |
| #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, |
| bool (*is_debuggable)() = IsDebuggable, |
| int (*get_library_system_property)(char* buffer) = GetLibrarySystemProperty); |
| |
| static JniInvocationImpl& GetJniInvocation(); |
| static bool IsInitialized(); |
| |
| 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_; |
| } |
| |
| bool JniInvocationImpl::IsInitialized() { |
| return jni_invocation_ != nullptr; |
| } |
| |
| jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) { |
| return JniInvocationImpl::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args); |
| } |
| |
| 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); |
| } |
| |
| jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) { |
| if (!JniInvocationImpl::IsInitialized()) { |
| *vm_count = 0; |
| return JNI_OK; |
| } |
| return JniInvocationImpl::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count); |
| } |
| |
| 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); |
| } |
| |
| JniInvocationImpl* JniInvocationCreate() { |
| return new JniInvocationImpl(); |
| } |
| |
| void JniInvocationDestroy(JniInvocationImpl* instance) { |
| delete instance; |
| } |
| |
| int JniInvocationInit(JniInvocationImpl* instance, const char* library) { |
| return instance->Init(library) ? 1 : 0; |
| } |
| |
| const char* JniInvocationGetLibrary(const char* library, char* buffer) { |
| return JniInvocationImpl::GetLibrary(library, buffer); |
| } |