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 <utils/Log.h> 35 36#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 37 38namespace android { 39namespace { 40 41void* gReservedAddress = NULL; 42size_t gReservedSize = 0; 43 44jint LIBLOAD_SUCCESS; 45jint LIBLOAD_FAILED_TO_OPEN_RELRO_FILE; 46jint LIBLOAD_FAILED_TO_LOAD_LIBRARY; 47jint LIBLOAD_FAILED_JNI_CALL; 48 49jboolean DoReserveAddressSpace(jlong size) { 50 size_t vsize = static_cast<size_t>(size); 51 52 void* addr = mmap(NULL, vsize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 53 if (addr == MAP_FAILED) { 54 ALOGE("Failed to reserve %zd bytes of address space for future load of " 55 "libwebviewchromium.so: %s", 56 vsize, strerror(errno)); 57 return JNI_FALSE; 58 } 59 gReservedAddress = addr; 60 gReservedSize = vsize; 61 ALOGV("Reserved %zd bytes at %p", vsize, addr); 62 return JNI_TRUE; 63} 64 65jboolean DoCreateRelroFile(const char* lib, const char* relro) { 66 // Try to unlink the old file, since if this is being called, the old one is 67 // obsolete. 68 if (unlink(relro) != 0 && errno != ENOENT) { 69 // If something went wrong other than the file not existing, log a warning 70 // but continue anyway in the hope that we can successfully overwrite the 71 // existing file with rename() later. 72 ALOGW("Failed to unlink old file %s: %s", relro, strerror(errno)); 73 } 74 static const char tmpsuffix[] = ".XXXXXX"; 75 char relro_tmp[strlen(relro) + sizeof(tmpsuffix)]; 76 strlcpy(relro_tmp, relro, sizeof(relro_tmp)); 77 strlcat(relro_tmp, tmpsuffix, sizeof(relro_tmp)); 78 int tmp_fd = TEMP_FAILURE_RETRY(mkstemp(relro_tmp)); 79 if (tmp_fd == -1) { 80 ALOGE("Failed to create temporary file %s: %s", relro_tmp, strerror(errno)); 81 return JNI_FALSE; 82 } 83 android_dlextinfo extinfo; 84 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO; 85 extinfo.reserved_addr = gReservedAddress; 86 extinfo.reserved_size = gReservedSize; 87 extinfo.relro_fd = tmp_fd; 88 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo); 89 int close_result = close(tmp_fd); 90 if (handle == NULL) { 91 ALOGE("Failed to load library %s: %s", lib, dlerror()); 92 unlink(relro_tmp); 93 return JNI_FALSE; 94 } 95 if (close_result != 0 || 96 chmod(relro_tmp, S_IRUSR | S_IRGRP | S_IROTH) != 0 || 97 rename(relro_tmp, relro) != 0) { 98 ALOGE("Failed to update relro file %s: %s", relro, strerror(errno)); 99 unlink(relro_tmp); 100 return JNI_FALSE; 101 } 102 ALOGV("Created relro file %s for library %s", relro, lib); 103 return JNI_TRUE; 104} 105 106jint DoLoadWithRelroFile(const char* lib, const char* relro) { 107 int relro_fd = TEMP_FAILURE_RETRY(open(relro, O_RDONLY)); 108 if (relro_fd == -1) { 109 ALOGE("Failed to open relro file %s: %s", relro, strerror(errno)); 110 return LIBLOAD_FAILED_TO_OPEN_RELRO_FILE; 111 } 112 android_dlextinfo extinfo; 113 extinfo.flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_USE_RELRO; 114 extinfo.reserved_addr = gReservedAddress; 115 extinfo.reserved_size = gReservedSize; 116 extinfo.relro_fd = relro_fd; 117 void* handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo); 118 close(relro_fd); 119 if (handle == NULL) { 120 ALOGE("Failed to load library %s: %s", lib, dlerror()); 121 return LIBLOAD_FAILED_TO_LOAD_LIBRARY; 122 } 123 ALOGV("Loaded library %s with relro file %s", lib, relro); 124 return LIBLOAD_SUCCESS; 125} 126 127/******************************************************************************/ 128/* JNI wrappers - handle string lifetimes and 32/64 ABI choice */ 129/******************************************************************************/ 130 131jboolean ReserveAddressSpace(JNIEnv*, jclass, jlong size) { 132 return DoReserveAddressSpace(size); 133} 134 135jboolean CreateRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64, 136 jstring relro32, jstring relro64) { 137#ifdef __LP64__ 138 jstring lib = lib64; 139 jstring relro = relro64; 140 (void)lib32; (void)relro32; 141#else 142 jstring lib = lib32; 143 jstring relro = relro32; 144 (void)lib64; (void)relro64; 145#endif 146 jboolean ret = JNI_FALSE; 147 const char* lib_utf8 = env->GetStringUTFChars(lib, NULL); 148 if (lib_utf8 != NULL) { 149 const char* relro_utf8 = env->GetStringUTFChars(relro, NULL); 150 if (relro_utf8 != NULL) { 151 ret = DoCreateRelroFile(lib_utf8, relro_utf8); 152 env->ReleaseStringUTFChars(relro, relro_utf8); 153 } 154 env->ReleaseStringUTFChars(lib, lib_utf8); 155 } 156 return ret; 157} 158 159jint LoadWithRelroFile(JNIEnv* env, jclass, jstring lib32, jstring lib64, 160 jstring relro32, jstring relro64) { 161#ifdef __LP64__ 162 jstring lib = lib64; 163 jstring relro = relro64; 164 (void)lib32; (void)relro32; 165#else 166 jstring lib = lib32; 167 jstring relro = relro32; 168 (void)lib64; (void)relro64; 169#endif 170 jint ret = LIBLOAD_FAILED_JNI_CALL; 171 const char* lib_utf8 = env->GetStringUTFChars(lib, NULL); 172 if (lib_utf8 != NULL) { 173 const char* relro_utf8 = env->GetStringUTFChars(relro, NULL); 174 if (relro_utf8 != NULL) { 175 ret = DoLoadWithRelroFile(lib_utf8, relro_utf8); 176 env->ReleaseStringUTFChars(relro, relro_utf8); 177 } 178 env->ReleaseStringUTFChars(lib, lib_utf8); 179 } 180 return ret; 181} 182 183const char kClassName[] = "android/webkit/WebViewFactory"; 184const JNINativeMethod kJniMethods[] = { 185 { "nativeReserveAddressSpace", "(J)Z", 186 reinterpret_cast<void*>(ReserveAddressSpace) }, 187 { "nativeCreateRelroFile", 188 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", 189 reinterpret_cast<void*>(CreateRelroFile) }, 190 { "nativeLoadWithRelroFile", 191 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", 192 reinterpret_cast<void*>(LoadWithRelroFile) }, 193}; 194 195} // namespace 196 197void RegisterWebViewFactory(JNIEnv* env) { 198 // If either of these fail, it will set an exception that will be thrown on 199 // return, so no need to handle errors here. 200 jclass clazz = env->FindClass(kClassName); 201 if (clazz) { 202 env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods)); 203 204 LIBLOAD_SUCCESS = env->GetStaticIntField( 205 clazz, 206 env->GetStaticFieldID(clazz, "LIBLOAD_SUCCESS", "I")); 207 208 LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = env->GetStaticIntField( 209 clazz, 210 env->GetStaticFieldID(clazz, "LIBLOAD_FAILED_TO_OPEN_RELRO_FILE", "I")); 211 212 LIBLOAD_FAILED_TO_LOAD_LIBRARY = env->GetStaticIntField( 213 clazz, 214 env->GetStaticFieldID(clazz, "LIBLOAD_FAILED_TO_LOAD_LIBRARY", "I")); 215 216 LIBLOAD_FAILED_JNI_CALL = env->GetStaticIntField( 217 clazz, 218 env->GetStaticFieldID(clazz, "LIBLOAD_FAILED_JNI_CALL", "I")); 219 } 220} 221 222} // namespace android 223 224JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { 225 JNIEnv* env = NULL; 226 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { 227 ALOGE("GetEnv failed"); 228 return JNI_ERR; 229 } 230 android::RegisterWebViewFactory(env); 231 return JNI_VERSION_1_6; 232} 233