1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17// Uncomment for verbose logging. 18// #define LOG_NDEBUG 0 19#define LOG_TAG "webviewchromiumloader" 20 21#include <dlfcn.h> 22#include <errno.h> 23#include <fcntl.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <unistd.h> 28#include <sys/mman.h> 29#include <sys/stat.h> 30#include <sys/types.h> 31 32#include <jni.h> 33#include <android/dlext.h> 34#include <nativeloader/native_loader.h> 35#include <utils/Log.h> 36 37#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 38 39namespace android { 40namespace { 41 42void* gReservedAddress = NULL; 43size_t gReservedSize = 0; 44 45jint LIBLOAD_SUCCESS; 46jint LIBLOAD_FAILED_TO_OPEN_RELRO_FILE; 47jint LIBLOAD_FAILED_TO_LOAD_LIBRARY; 48jint LIBLOAD_FAILED_JNI_CALL; 49jint LIBLOAD_FAILED_TO_FIND_NAMESPACE; 50 51jboolean DoReserveAddressSpace(jlong size) { 52 size_t vsize = static_cast<size_t>(size); 53 54 void* addr = mmap(NULL, vsize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 55 if (addr == MAP_FAILED) { 56 ALOGE("Failed to reserve %zd bytes of address space for future load of " 57 "libwebviewchromium.so: %s", 58 vsize, strerror(errno)); 59 return JNI_FALSE; 60 } 61 gReservedAddress = addr; 62 gReservedSize = vsize; 63 ALOGV("Reserved %zd bytes at %p", vsize, addr); 64 return JNI_TRUE; 65} 66 67jboolean DoCreateRelroFile(const char* lib, const char* relro) { 68 // Try to unlink the old file, since if this is being called, the old one is 69 // obsolete. 70 if (unlink(relro) != 0 && errno != ENOENT) { 71 // If something went wrong other than the file not existing, log a warning 72 // but continue anyway in the hope that we can successfully overwrite the 73 // existing file with rename() later. 74 ALOGW("Failed to unlink old file %s: %s", relro, strerror(errno)); 75 } 76 static const char tmpsuffix[] = ".XXXXXX"; 77 char relro_tmp[strlen(relro) + sizeof(tmpsuffix)]; 78 strlcpy(relro_tmp, relro, sizeof(relro_tmp)); 79 strlcat(relro_tmp, tmpsuffix, sizeof(relro_tmp)); 80 int tmp_fd = TEMP_FAILURE_RETRY(mkstemp(relro_tmp)); 81 if (tmp_fd == -1) { 82 ALOGE("Failed to create temporary file %s: %s", relro_tmp, strerror(errno)); 83 return JNI_FALSE; 84 } 85 android_dlextinfo extinfo; 86 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO; 87 extinfo.reserved_addr = gReservedAddress; 88 extinfo.reserved_size = gReservedSize; 89 extinfo.relro_fd = tmp_fd; 90 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo); 91 int close_result = close(tmp_fd); 92 if (handle == NULL) { 93 ALOGE("Failed to load library %s: %s", lib, dlerror()); 94 unlink(relro_tmp); 95 return JNI_FALSE; 96 } 97 if (close_result != 0 || 98 chmod(relro_tmp, S_IRUSR | S_IRGRP | S_IROTH) != 0 || 99 rename(relro_tmp, relro) != 0) { 100 ALOGE("Failed to update relro file %s: %s", relro, strerror(errno)); 101 unlink(relro_tmp); 102 return JNI_FALSE; 103 } 104 ALOGV("Created relro file %s for library %s", relro, lib); 105 return JNI_TRUE; 106} 107 108jint DoLoadWithRelroFile(JNIEnv* env, const char* lib, const char* relro, 109 jobject clazzLoader) { 110 int relro_fd = TEMP_FAILURE_RETRY(open(relro, O_RDONLY)); 111 if (relro_fd == -1) { 112 ALOGE("Failed to open relro file %s: %s", relro, strerror(errno)); 113 return LIBLOAD_FAILED_TO_OPEN_RELRO_FILE; 114 } 115 android_namespace_t* ns = 116 android::FindNamespaceByClassLoader(env, clazzLoader); 117 if (ns == NULL) { 118 ALOGE("Failed to find classloader namespace"); 119 return LIBLOAD_FAILED_TO_FIND_NAMESPACE; 120 } 121 android_dlextinfo extinfo; 122 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_USE_RELRO | 123 ANDROID_DLEXT_USE_NAMESPACE; 124 extinfo.reserved_addr = gReservedAddress; 125 extinfo.reserved_size = gReservedSize; 126 extinfo.relro_fd = relro_fd; 127 extinfo.library_namespace = ns; 128 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo); 129 close(relro_fd); 130 if (handle == NULL) { 131 ALOGE("Failed to load library %s: %s", lib, dlerror()); 132 return LIBLOAD_FAILED_TO_LOAD_LIBRARY; 133 } 134 ALOGV("Loaded library %s with relro file %s", lib, relro); 135 return LIBLOAD_SUCCESS; 136} 137 138/******************************************************************************/ 139/* JNI wrappers - handle string lifetimes and 32/64 ABI choice */ 140/******************************************************************************/ 141 142jboolean ReserveAddressSpace(JNIEnv*, jclass, jlong size) { 143 return DoReserveAddressSpace(size); 144} 145 146jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64, 147 jstring relro32, jstring relro64) { 148#ifdef __LP64__ 149 jstring lib = lib64; 150 jstring relro = relro64; 151 (void)lib32; (void)relro32; 152#else 153 jstring lib = lib32; 154 jstring relro = relro32; 155 (void)lib64; (void)relro64; 156#endif 157 jboolean ret = JNI_FALSE; 158 const char* lib_utf8 = env->GetStringUTFChars(lib, NULL); 159 if (lib_utf8 != NULL) { 160 const char* relro_utf8 = env->GetStringUTFChars(relro, NULL); 161 if (relro_utf8 != NULL) { 162 ret = DoCreateRelroFile(lib_utf8, relro_utf8); 163 env->ReleaseStringUTFChars(relro, relro_utf8); 164 } 165 env->ReleaseStringUTFChars(lib, lib_utf8); 166 } 167 return ret; 168} 169 170jint LoadWithRelroFile(JNIEnv* env, jclass, jstring lib, jstring relro32, 171 jstring relro64, jobject clazzLoader) { 172#ifdef __LP64__ 173 jstring relro = relro64; 174 (void)relro32; 175#else 176 jstring relro = relro32; 177 (void)relro64; 178#endif 179 jint ret = LIBLOAD_FAILED_JNI_CALL; 180 const char* lib_utf8 = env->GetStringUTFChars(lib, NULL); 181 if (lib_utf8 != NULL) { 182 const char* relro_utf8 = env->GetStringUTFChars(relro, NULL); 183 if (relro_utf8 != NULL) { 184 ret = DoLoadWithRelroFile(env, lib_utf8, relro_utf8, clazzLoader); 185 env->ReleaseStringUTFChars(relro, relro_utf8); 186 } 187 env->ReleaseStringUTFChars(lib, lib_utf8); 188 } 189 return ret; 190} 191 192const char kWebViewFactoryClassName[] = "android/webkit/WebViewFactory"; 193const char kWebViewLibraryLoaderClassName[] = 194 "android/webkit/WebViewLibraryLoader"; 195const JNINativeMethod kJniMethods[] = { 196 { "nativeReserveAddressSpace", "(J)Z", 197 reinterpret_cast<void*>(ReserveAddressSpace) }, 198 { "nativeCreateRelroFile", 199 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", 200 reinterpret_cast<void*>(CreateRelroFile) }, 201 { "nativeLoadWithRelroFile", 202 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)I", 203 reinterpret_cast<void*>(LoadWithRelroFile) }, 204}; 205 206} // namespace 207 208void RegisterWebViewFactory(JNIEnv* env) { 209 // If either of these fail, it will set an exception that will be thrown on 210 // return, so no need to handle errors here. 211 jclass clazz = env->FindClass(kWebViewFactoryClassName); 212 if (clazz) { 213 LIBLOAD_SUCCESS = env->GetStaticIntField( 214 clazz, 215 env->GetStaticFieldID(clazz, "LIBLOAD_SUCCESS", "I")); 216 217 LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = env->GetStaticIntField( 218 clazz, 219 env->GetStaticFieldID(clazz, "LIBLOAD_FAILED_TO_OPEN_RELRO_FILE", "I")); 220 221 LIBLOAD_FAILED_TO_LOAD_LIBRARY = env->GetStaticIntField( 222 clazz, 223 env->GetStaticFieldID(clazz, "LIBLOAD_FAILED_TO_LOAD_LIBRARY", "I")); 224 225 LIBLOAD_FAILED_JNI_CALL = env->GetStaticIntField( 226 clazz, 227 env->GetStaticFieldID(clazz, "LIBLOAD_FAILED_JNI_CALL", "I")); 228 229 LIBLOAD_FAILED_TO_FIND_NAMESPACE = env->GetStaticIntField( 230 clazz, 231 env->GetStaticFieldID(clazz, "LIBLOAD_FAILED_TO_FIND_NAMESPACE", "I")); 232 } 233} 234 235void RegisterWebViewLibraryLoader(JNIEnv* env) { 236 // If either of these fail, it will set an exception that will be thrown on 237 // return, so no need to handle errors here. 238 jclass clazz = env->FindClass(kWebViewLibraryLoaderClassName); 239 if (clazz) { 240 env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods)); 241 } 242} 243 244} // namespace android 245 246JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { 247 JNIEnv* env = NULL; 248 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { 249 ALOGE("GetEnv failed"); 250 return JNI_ERR; 251 } 252 android::RegisterWebViewFactory(env); 253 // Ensure there isn't a pending Java exception before registering methods from 254 // WebViewLibraryLoader 255 if (!env->ExceptionCheck()) { 256 android::RegisterWebViewLibraryLoader(env); 257 } 258 return JNI_VERSION_1_6; 259} 260