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