OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // This is the Android-specific Chrome linker, a tiny shared library | |
6 // implementing a custom dynamic linker that can be used to load the | |
7 // real Chrome libraries (e.g. libchromeview.so). | |
8 | |
9 // The main point of this linker is to be able to share the RELRO | |
10 // section of libchromeview.so between the browser and renderer | |
11 // process. This saves about 1.3 MB of private RAM per renderer process. | |
12 | |
13 #include <stdlib.h> | |
14 | |
15 #include <android/log.h> | |
16 #include <jni.h> | |
17 #include <crazy_linker.h> | |
digit1
2013/09/03 14:31:54
Can we rename this source file to crazy_linker_chr
| |
18 | |
19 // Set this to 1 to enable debug traces to the Android log. | |
20 #define DEBUG 0 | |
21 | |
22 #if DEBUG | |
23 #define LOG_INFO(...) \ | |
24 __android_log_print(ANDROID_LOG_INFO, "chrome_linker", __VA_ARGS__) | |
25 #define LOG_ERROR(...) \ | |
26 __android_log_print(ANDROID_LOG_ERROR, "chrome_linker", __VA_ARGS__) | |
27 #else | |
28 #define LOG_INFO(...) ((void)0) | |
29 #define LOG_ERROR(...) ((void)0) | |
30 #endif | |
31 | |
32 #define UNUSED __attribute__((unused)) | |
33 | |
34 namespace { | |
35 | |
36 // A simply scoped UTF String class that can be initialized from | |
37 // a Java jstring handle. | |
38 class String { | |
39 public: | |
40 String(JNIEnv* env, jstring str); | |
41 | |
42 ~String() { | |
43 if (ptr_) | |
44 ::free(ptr_); | |
45 } | |
46 | |
47 const char* c_str() const { return ptr_ ? ptr_ : ""; } | |
48 size_t size() const { return size_; } | |
49 | |
50 private: | |
51 char* ptr_; | |
52 size_t size_; | |
53 }; | |
54 | |
55 String::String(JNIEnv* env, jstring str) { | |
56 size_ = env->GetStringUTFLength(str); | |
57 ptr_ = reinterpret_cast<char*>(::malloc(size_ + 1)); | |
58 | |
59 // Note: the result contains Java "modified UTF-8" bytes. | |
60 // Good enough for the linker though. | |
61 const char* bytes = env->GetStringUTFChars(str, NULL); | |
62 ::memcpy(ptr_, bytes, size_); | |
63 ptr_[size_] = '\0'; | |
64 | |
65 env->ReleaseStringUTFChars(str, bytes); | |
66 } | |
67 | |
68 // A scoped crazy_library_t that automatically closes the handle | |
69 // on scope exit, unless Release() has been called. | |
70 class ScopedLibrary { | |
71 public: | |
72 ScopedLibrary() : lib_(NULL) {} | |
73 | |
74 ~ScopedLibrary() { | |
75 if (lib_) | |
76 crazy_library_close(lib_); | |
77 } | |
78 | |
79 crazy_library_t* Get() { return lib_; } | |
80 | |
81 crazy_library_t** GetPtr() { return &lib_; } | |
82 | |
83 crazy_library_t* Release() { | |
84 crazy_library_t* ret = lib_; | |
85 lib_ = NULL; | |
86 return ret; | |
87 } | |
88 | |
89 private: | |
90 crazy_library_t* lib_; | |
91 }; | |
92 | |
93 static const char* GetBaseNamePtr(const char* path) { | |
94 const char* p = strchr(path, '/'); | |
95 if (p) | |
96 return p + 1; | |
97 else | |
98 return path; | |
99 } | |
100 | |
101 static bool InitClassReference(JNIEnv* env, | |
102 const char* class_name, | |
103 jclass* clazz) { | |
104 *clazz = env->FindClass(class_name); | |
105 if (!*clazz) { | |
106 LOG_ERROR("Could not find class for %s", class_name); | |
107 return false; | |
108 } | |
109 return true; | |
110 } | |
111 | |
112 static bool InitFieldId(JNIEnv* env, | |
113 jclass clazz, | |
114 const char* field_name, | |
115 const char* field_sig, | |
116 jfieldID* field_id) { | |
117 *field_id = env->GetFieldID(clazz, field_name, field_sig); | |
118 if (!*field_id) { | |
119 LOG_ERROR("Could not find ID for field '%s'", field_name); | |
120 return false; | |
121 } | |
122 LOG_INFO( | |
123 "%s: Found ID %p for field '%s", __FUNCTION__, *field_id, field_name); | |
124 return true; | |
125 } | |
126 | |
127 | |
128 static JavaVM* s_java_vm; | |
129 | |
130 struct LibInfo_class { | |
131 jfieldID load_address_id; | |
132 jfieldID load_size_id; | |
133 jfieldID relro_start_id; | |
134 jfieldID relro_size_id; | |
135 jfieldID relro_fd_id; | |
136 | |
137 // Initialize an instance. | |
138 bool Init(JNIEnv* env) { | |
139 jclass clazz; | |
140 if (!InitClassReference(env, | |
141 "org/chromium/base/Linker$LibInfo", | |
142 &clazz)) { | |
143 return false; | |
144 } | |
145 | |
146 return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) && | |
147 InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) && | |
148 InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) && | |
149 InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) && | |
150 InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id); | |
151 } | |
152 | |
153 // Use this instance to convert a RelroInfo reference into | |
154 // a crazy_library_info_t. | |
155 void GetLibraryInfo(JNIEnv* env, | |
156 jobject library_info_obj, | |
157 crazy_library_info_t* info) { | |
158 info->load_address = static_cast<size_t>( | |
159 env->GetLongField(library_info_obj, load_address_id)); | |
160 | |
161 info->load_size = static_cast<size_t>( | |
162 env->GetLongField(library_info_obj, load_size_id)); | |
163 | |
164 info->relro_start = static_cast<size_t>( | |
165 env->GetLongField(library_info_obj, relro_start_id)); | |
166 | |
167 info->relro_size = static_cast<size_t>( | |
168 env->GetLongField(library_info_obj, relro_size_id)); | |
169 | |
170 info->relro_fd = env->GetIntField(library_info_obj, relro_fd_id); | |
171 } | |
172 | |
173 void SetLibraryInfo(JNIEnv* env, | |
174 jobject library_info_obj, | |
175 crazy_library_info_t* info) { | |
176 env->SetLongField(library_info_obj, load_address_id, info->load_address); | |
177 env->SetLongField(library_info_obj, load_size_id, info->load_size); | |
178 env->SetLongField(library_info_obj, relro_start_id, info->relro_start); | |
179 env->SetLongField(library_info_obj, relro_size_id, info->relro_size); | |
180 env->SetIntField(library_info_obj, relro_fd_id, info->relro_fd); | |
181 } | |
182 }; | |
183 | |
184 | |
185 static LibInfo_class s_lib_info_fields; | |
186 | |
187 static crazy_context_t* s_crazy_context; | |
188 | |
189 crazy_context_t* GetCrazyContext() { | |
190 if (!s_crazy_context) { | |
191 // Create new context. | |
192 s_crazy_context = crazy_context_create(); | |
193 | |
194 // Ensure libraries located in the same directory than the linker | |
195 // can be loaded. | |
196 crazy_context_add_search_path_for_address( | |
197 s_crazy_context, reinterpret_cast<void*>(&s_crazy_context)); | |
198 } | |
199 | |
200 return s_crazy_context; | |
201 } | |
202 | |
203 // Load a library with the Chrome linker. This will also call its | |
204 // JNI_OnLoad() method, which shall register its methods. Note that | |
205 // lazy native method resolution will _not_ work after this, because | |
206 // Dalvik uses the system's dlsym() which won't see the new library, | |
207 // so explicit registration is mandatory. | |
208 jboolean nativeLoadLibrary(JNIEnv* env, | |
209 jclass clazz, | |
210 jstring library_name, | |
211 jlong load_address, | |
212 jobject library_info) { | |
213 String lib_name(env, library_name); | |
214 const char* UNUSED lib_basename = GetBaseNamePtr(lib_name.c_str()); | |
215 | |
216 crazy_context_t* context = GetCrazyContext(); | |
217 | |
218 // Set the desired load address (0 means randomize it). | |
219 crazy_context_set_load_address(context, | |
220 static_cast<size_t>(load_address)); | |
221 | |
222 // Open the library now. | |
223 LOG_INFO("%s: Opening shared library: %s", __FUNCTION__, lib_name.c_str()); | |
224 | |
225 // Not using a ScopedLibrary here is intentional, since leaking | |
226 // the library, even in case of error, is intentional. | |
227 crazy_library_t* library; | |
228 if (!crazy_library_open(&library, lib_name.c_str(), context)) { | |
229 LOG_ERROR("%s: Could not open %s: %s", __FUNCTION__, lib_basename, | |
230 crazy_context_get_error(context)); | |
231 return false; | |
232 } | |
233 | |
234 if (library_info) { | |
235 crazy_library_info_t info; | |
236 | |
237 memset(&info, 0, sizeof(info)); | |
238 info.relro_fd = -1; | |
239 | |
240 if (!crazy_library_get_info(library, context, &info)) { | |
241 LOG_INFO("%s: Could not get library info for %s: %s", | |
242 __FUNCTION__, lib_name.c_str(), | |
243 crazy_context_get_error(context)); | |
244 } else { | |
245 s_lib_info_fields.SetLibraryInfo(env, library_info, &info); | |
246 } | |
247 } | |
248 | |
249 LOG_INFO("%s: Looking for JNI_OnLoad() in %s", __FUNCTION__, lib_basename); | |
250 | |
251 int (*jni_on_load_ptr)(JavaVM*, void*); | |
252 if (crazy_library_find_symbol( | |
253 library, | |
254 "JNI_OnLoad", | |
255 reinterpret_cast<void**>(&jni_on_load_ptr))) { | |
256 | |
257 LOG_INFO("%s: Calling JNI_OnLoad() in %s", __FUNCTION__, lib_basename); | |
258 | |
259 // Call it. | |
260 int ret = (*jni_on_load_ptr)(s_java_vm, NULL); | |
261 if (ret < JNI_VERSION_1_4) { | |
262 LOG_ERROR("%s: JNI_OnLoad() of %s returned %d, at least %d expected", | |
263 __FUNCTION__, lib_basename, ret, JNI_VERSION_1_4); | |
264 return false; | |
265 } | |
266 } | |
267 | |
268 LOG_INFO("%s: Success loading library %s", __FUNCTION__, lib_basename); | |
269 return true; | |
270 } | |
271 | |
272 jboolean nativeEnableSharedRelro(JNIEnv* env, | |
273 jclass clazz, | |
274 jstring library_name, | |
275 jobject lib_info_obj) { | |
276 String lib_name(env, library_name); | |
277 | |
278 LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str()); | |
279 | |
280 ScopedLibrary library; | |
281 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { | |
282 LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str()); | |
283 return false; | |
284 } | |
285 | |
286 crazy_context_t* context = GetCrazyContext(); | |
287 | |
288 if (!crazy_library_enable_relro_sharing(library.Get(), context)) { | |
289 LOG_ERROR("%s: Could not enable RELRO sharing for %s: %s", | |
290 __FUNCTION__, lib_name.c_str(), | |
291 crazy_context_get_error(context)); | |
292 return false; | |
293 } | |
294 | |
295 crazy_library_info_t info; | |
296 if (!crazy_library_get_info(library.Get(), context, &info)) { | |
297 LOG_ERROR("%s: Could not retrieve %s information: %s", | |
298 __FUNCTION__, lib_name.c_str(), | |
299 crazy_context_get_error(context)); | |
300 return false; | |
301 } | |
302 | |
303 s_lib_info_fields.SetLibraryInfo(env, lib_info_obj, &info); | |
304 return true; | |
305 } | |
306 | |
307 jboolean nativeUseSharedRelro(JNIEnv* env, | |
308 jclass clazz, | |
309 jstring library_name, | |
310 jobject lib_info_obj) { | |
311 String lib_name(env, library_name); | |
312 | |
313 LOG_INFO("%s: called for %s, lib_info_ref=%p", | |
314 __FUNCTION__, lib_name.c_str(), lib_info_obj); | |
315 | |
316 ScopedLibrary library; | |
317 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { | |
318 LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str()); | |
319 return false; | |
320 } | |
321 | |
322 crazy_context_t* context = GetCrazyContext(); | |
323 crazy_library_info_t info; | |
324 s_lib_info_fields.GetLibraryInfo(env, lib_info_obj, &info); | |
325 | |
326 LOG_INFO( | |
327 "%s: library=%s load_addr=%p load_size=%p relro_start=%p relro_size=%p " | |
328 "relro_fd=%d", | |
329 __FUNCTION__, | |
330 lib_name.c_str(), | |
331 (void*)info.load_address, | |
332 (void*)info.load_size, | |
333 (void*)info.relro_start, | |
334 (void*)info.relro_size, | |
335 info.relro_fd); | |
336 | |
337 if (!crazy_library_use_relro_sharing(library.Get(), | |
338 info.relro_start, | |
339 info.relro_size, | |
340 info.relro_fd, | |
341 context)) { | |
342 LOG_ERROR("%s: Could not enable RELRO sharing for %s: %s", | |
343 __FUNCTION__, lib_name.c_str(), | |
344 crazy_context_get_error(context)); | |
345 return false; | |
346 } | |
347 | |
348 LOG_INFO("%s: Library %s using shared RELRO section!", | |
349 __FUNCTION__, lib_name.c_str()); | |
350 | |
351 return true; | |
352 } | |
353 | |
354 static const JNINativeMethod kNativeMethods[] = { | |
355 { "nativeLoadLibrary", | |
356 "(" | |
357 "Ljava/lang/String;" | |
358 "J" | |
359 "Lorg/chromium/base/Linker$LibInfo;" | |
360 ")" | |
361 "Z", | |
362 reinterpret_cast<void*>(&nativeLoadLibrary) }, | |
363 | |
364 { "nativeEnableSharedRelro", | |
365 "(" | |
366 "Ljava/lang/String;" | |
367 "Lorg/chromium/base/Linker$LibInfo;" | |
368 ")" | |
369 "Z", | |
370 reinterpret_cast<void*>(&nativeEnableSharedRelro) }, | |
371 | |
372 { "nativeUseSharedRelro", | |
373 "(" | |
374 "Ljava/lang/String;" | |
375 "Lorg/chromium/base/Linker$LibInfo;" | |
376 ")" | |
377 "Z", | |
378 reinterpret_cast<void*>(&nativeUseSharedRelro) }, | |
379 }; | |
380 | |
381 } // namespace | |
382 | |
383 jint JNI_OnLoad(JavaVM* vm, void* reserved) { | |
384 s_java_vm = vm; | |
385 | |
386 LOG_INFO("%s: Entering", __FUNCTION__); | |
387 // Get new JNIEnv | |
388 JNIEnv* env; | |
389 if (vm->GetEnv( | |
390 reinterpret_cast<void**>(&env), JNI_VERSION_1_4) != JNI_OK) { | |
391 LOG_ERROR("Could not create JNIEnv"); | |
392 return -1; | |
393 } | |
394 | |
395 // Register native methods. | |
396 jclass linker_class; | |
397 if (!InitClassReference(env, "org/chromium/base/Linker", &linker_class)) | |
398 return -1; | |
399 | |
400 LOG_INFO("%s: Registering native methods", __FUNCTION__); | |
401 env->RegisterNatives(linker_class, | |
402 kNativeMethods, | |
403 sizeof(kNativeMethods)/sizeof(kNativeMethods[0])); | |
404 | |
405 | |
406 // Find LibInfo field ids. | |
407 LOG_INFO("%s: Caching field IDs", __FUNCTION__); | |
408 if (!s_lib_info_fields.Init(env)) { | |
409 return -1; | |
410 } | |
411 | |
412 LOG_INFO("%s: Done", __FUNCTION__); | |
413 return JNI_VERSION_1_4; | |
414 } | |
OLD | NEW |