Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(414)

Side by Side Diff: base/android/linker/crazy_linker_jni.cc

Issue 23717023: Android: Add chrome-specific dynamic linker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address later issues. Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 // This source code *cannot* depend on anything from base/ or the C++
14 // STL, to keep the final library small, and avoid ugly dependency issues.
15
16
17 #include <android/log.h>
18 #include <crazy_linker.h>
19 #include <jni.h>
20 #include <stdlib.h>
21
22 // Set this to 1 to enable debug traces to the Android log.
23 // Note that LOG() from "base/logging.h" cannot be used, since it is
24 // in base/ which hasn't been loaded yet.
25 #define DEBUG 0
26
27 #if DEBUG
28 #define LOG_INFO(...) \
29 __android_log_print(ANDROID_LOG_INFO, "chrome_linker", __VA_ARGS__)
30 #define LOG_ERROR(...) \
31 __android_log_print(ANDROID_LOG_ERROR, "chrome_linker", __VA_ARGS__)
32 #else
33 #define LOG_INFO(...) ((void)0)
34 #define LOG_ERROR(...) ((void)0)
35 #endif
36
37 #define UNUSED __attribute__((unused))
38
39 namespace {
40
41 // A simply scoped UTF String class that can be initialized from
42 // a Java jstring handle. Modeled like std::string, which cannot
43 // be used here.
44 class String {
45 public:
46 String(JNIEnv* env, jstring str);
47
48 ~String() {
49 if (ptr_)
50 ::free(ptr_);
51 }
52
53 const char* c_str() const { return ptr_ ? ptr_ : ""; }
54 size_t size() const { return size_; }
55
56 private:
57 char* ptr_;
58 size_t size_;
59 };
60
61 String::String(JNIEnv* env, jstring str) {
62 size_ = env->GetStringUTFLength(str);
63 ptr_ = reinterpret_cast<char*>(::malloc(size_ + 1));
JF 2013/09/13 05:41:00 static_cast?
digit1 2013/09/18 15:17:30 Done.
64
65 // Note: the result contains Java "modified UTF-8" bytes.
66 // Good enough for the linker though.
67 const char* bytes = env->GetStringUTFChars(str, NULL);
68 ::memcpy(ptr_, bytes, size_);
69 ptr_[size_] = '\0';
70
71 env->ReleaseStringUTFChars(str, bytes);
72 }
73
74 // A scoped crazy_library_t that automatically closes the handle
75 // on scope exit, unless Release() has been called.
76 class ScopedLibrary {
77 public:
78 ScopedLibrary() : lib_(NULL) {}
79
80 ~ScopedLibrary() {
81 if (lib_)
82 crazy_library_close(lib_);
83 }
84
85 crazy_library_t* Get() { return lib_; }
86
87 crazy_library_t** GetPtr() { return &lib_; }
88
89 crazy_library_t* Release() {
90 crazy_library_t* ret = lib_;
91 lib_ = NULL;
92 return ret;
93 }
94
95 private:
96 crazy_library_t* lib_;
97 };
98
99 // Return a pointer to the base name from an input |path| string.
100 const char* GetBaseNamePtr(const char* path) {
101 const char* p = strchr(path, '/');
102 if (p)
103 return p + 1;
104 return path;
105 }
106
107 // Find the jclass JNI reference corresponding to a given |class_name|.
108 // |env| is the current JNI environment handle.
109 // On success, return true and set |*clazz|.
110 bool InitClassReference(JNIEnv* env,
111 const char* class_name,
112 jclass* clazz) {
113 *clazz = env->FindClass(class_name);
114 if (!*clazz) {
115 LOG_ERROR("Could not find class for %s", class_name);
116 return false;
117 }
118 return true;
119 }
120
121 // Initialize a jfieldID corresponding to the field of a given |clazz|,
122 // with name |field_name| and signature |field_sig|.
123 // |env| is the current JNI environment handle.
124 // On success, return true and set |*field_id|.
125 bool InitFieldId(JNIEnv* env,
126 jclass clazz,
127 const char* field_name,
128 const char* field_sig,
129 jfieldID* field_id) {
130 *field_id = env->GetFieldID(clazz, field_name, field_sig);
131 if (!*field_id) {
132 LOG_ERROR("Could not find ID for field '%s'", field_name);
133 return false;
134 }
135 LOG_INFO(
136 "%s: Found ID %p for field '%s", __FUNCTION__, *field_id, field_name);
137 return true;
138 }
139
140 // This JavaVM* handle is saved in the linker's JNI_OnLoad(), then passed
141 // to the JNI_OnLoad() hook of any function loaded through LoadLibrary).
142 JavaVM* g_java_vm;
143
144 // A class used to model the field IDs of the org.chromium.base.Linker
145 // LibInfo inner class, used to communicate data with the Java side
146 // of the linker.
147 struct LibInfo_class {
148 jfieldID load_address_id;
149 jfieldID load_size_id;
150 jfieldID relro_start_id;
151 jfieldID relro_size_id;
152 jfieldID relro_fd_id;
153
154 // Initialize an instance.
155 bool Init(JNIEnv* env) {
156 jclass clazz;
157 if (!InitClassReference(env,
158 "org/chromium/base/Linker$LibInfo",
159 &clazz)) {
160 return false;
161 }
162
163 return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
164 InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
165 InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
166 InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
167 InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
168 }
169
170 // Use this instance to convert a RelroInfo reference into
171 // a crazy_library_info_t.
172 void GetLibraryInfo(JNIEnv* env,
173 jobject library_info_obj,
174 crazy_library_info_t* info) {
175 info->load_address = static_cast<size_t>(
176 env->GetLongField(library_info_obj, load_address_id));
177
178 info->load_size = static_cast<size_t>(
179 env->GetLongField(library_info_obj, load_size_id));
180
181 info->relro_start = static_cast<size_t>(
182 env->GetLongField(library_info_obj, relro_start_id));
183
184 info->relro_size = static_cast<size_t>(
185 env->GetLongField(library_info_obj, relro_size_id));
186
187 info->relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
188 }
189
190 void SetLibraryInfo(JNIEnv* env,
191 jobject library_info_obj,
192 crazy_library_info_t* info) {
193 env->SetLongField(library_info_obj, load_address_id, info->load_address);
194 env->SetLongField(library_info_obj, load_size_id, info->load_size);
195 env->SetLongField(library_info_obj, relro_start_id, info->relro_start);
196 env->SetLongField(library_info_obj, relro_size_id, info->relro_size);
197 env->SetIntField(library_info_obj, relro_fd_id, info->relro_fd);
198 }
199 };
200
201 static LibInfo_class s_lib_info_fields;
202
203 // The linker uses a single crazy_context_t object created on demand.
204 // There is no need to protect this against concurrent access, locking
205 // is already handled on the Java side.
206 static crazy_context_t* s_crazy_context;
207
208 crazy_context_t* GetCrazyContext() {
209 if (!s_crazy_context) {
210 // Create new context.
211 s_crazy_context = crazy_context_create();
212
213 // Ensure libraries located in the same directory than the linker
214 // can be loaded.
215 crazy_context_add_search_path_for_address(
216 s_crazy_context, reinterpret_cast<void*>(&s_crazy_context));
217 }
218
219 return s_crazy_context;
220 }
221
222 // Load a library with the Chrome linker. This will also call its
223 // JNI_OnLoad() method, which shall register its methods. Note that
224 // lazy native method resolution will _not_ work after this, because
225 // Dalvik uses the system's dlsym() which won't see the new library,
226 // so explicit registration is mandatory.
227 // |env| is the current JNI environment handle.
228 // |clazz| is the static class handle for org.chromium.base.Linker,
229 // and is ignored here.
230 // |library_name| is the library name (e.g. libfoo.so).
231 // |load_address| is an explicit load address.
232 // |library_info| is a LibInfo handle used to communicate information
233 // with the Java side.
234 // Return true on success.
235 jboolean LoadLibrary(JNIEnv* env,
236 jclass clazz,
237 jstring library_name,
238 jlong load_address,
239 jobject library_info) {
240 String lib_name(env, library_name);
241 const char* UNUSED lib_basename = GetBaseNamePtr(lib_name.c_str());
242
243 crazy_context_t* context = GetCrazyContext();
244
245 // Set the desired load address (0 means randomize it).
246 crazy_context_set_load_address(context,
247 static_cast<size_t>(load_address));
248
249 // Open the library now.
250 LOG_INFO("%s: Opening shared library: %s", __FUNCTION__, lib_name.c_str());
251
252 // Not using a ScopedLibrary here is intentional, since leaking
253 // the library, even in case of error, is intentional.
254 crazy_library_t* library;
255 if (!crazy_library_open(&library, lib_name.c_str(), context)) {
256 LOG_ERROR("%s: Could not open %s: %s", __FUNCTION__, lib_basename,
257 crazy_context_get_error(context));
258 return false;
259 }
260
261 if (library_info) {
262 crazy_library_info_t info;
263
264 memset(&info, 0, sizeof(info));
265 info.relro_fd = -1;
266
267 if (!crazy_library_get_info(library, context, &info)) {
268 LOG_INFO("%s: Could not get library info for %s: %s",
269 __FUNCTION__, lib_name.c_str(),
270 crazy_context_get_error(context));
271 } else {
272 s_lib_info_fields.SetLibraryInfo(env, library_info, &info);
273 }
274 }
275
276 LOG_INFO("%s: Looking for JNI_OnLoad() in %s", __FUNCTION__, lib_basename);
277
278 int (*jni_on_load_ptr)(JavaVM*, void*);
279 if (crazy_library_find_symbol(
280 library, "JNI_OnLoad", reinterpret_cast<void**>(&jni_on_load_ptr))) {
281
282 LOG_INFO("%s: Calling JNI_OnLoad() in %s", __FUNCTION__, lib_basename);
283
284 // Call it.
285 int ret = (*jni_on_load_ptr)(g_java_vm, NULL);
286 if (ret < JNI_VERSION_1_4) {
287 LOG_ERROR("%s: JNI_OnLoad() of %s returned %d, at least %d expected",
288 __FUNCTION__, lib_basename, ret, JNI_VERSION_1_4);
289 return false;
290 }
291 }
292
293 LOG_INFO("%s: Success loading library %s", __FUNCTION__, lib_basename);
294 return true;
295 }
296
297 jboolean EnableSharedRelro(JNIEnv* env,
298 jclass clazz,
299 jstring library_name,
300 jobject lib_info_obj) {
301 String lib_name(env, library_name);
302
303 LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str());
304
305 ScopedLibrary library;
306 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
307 LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
308 return false;
309 }
310
311 crazy_context_t* context = GetCrazyContext();
312
313 if (!crazy_library_enable_relro_sharing(library.Get(), context)) {
314 LOG_ERROR("%s: Could not enable RELRO sharing for %s: %s",
315 __FUNCTION__, lib_name.c_str(),
316 crazy_context_get_error(context));
317 return false;
318 }
319
320 crazy_library_info_t info;
321 if (!crazy_library_get_info(library.Get(), context, &info)) {
322 LOG_ERROR("%s: Could not retrieve %s information: %s",
323 __FUNCTION__, lib_name.c_str(),
324 crazy_context_get_error(context));
325 return false;
326 }
327
328 s_lib_info_fields.SetLibraryInfo(env, lib_info_obj, &info);
329 return true;
330 }
331
332 jboolean UseSharedRelro(JNIEnv* env,
333 jclass clazz,
334 jstring library_name,
335 jobject lib_info_obj) {
336 String lib_name(env, library_name);
337
338 LOG_INFO("%s: called for %s, lib_info_ref=%p",
339 __FUNCTION__, lib_name.c_str(), lib_info_obj);
340
341 ScopedLibrary library;
342 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
343 LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
344 return false;
345 }
346
347 crazy_context_t* context = GetCrazyContext();
348 crazy_library_info_t info;
349 s_lib_info_fields.GetLibraryInfo(env, lib_info_obj, &info);
350
351 LOG_INFO(
352 "%s: library=%s load_addr=%p load_size=%p relro_start=%p relro_size=%p "
353 "relro_fd=%d",
354 __FUNCTION__,
355 lib_name.c_str(),
356 (void*)info.load_address,
357 (void*)info.load_size,
358 (void*)info.relro_start,
359 (void*)info.relro_size,
360 info.relro_fd);
361
362 if (!crazy_library_use_relro_sharing(library.Get(),
363 info.relro_start,
364 info.relro_size,
365 info.relro_fd,
366 context)) {
367 LOG_ERROR("%s: Could not enable RELRO sharing for %s: %s",
368 __FUNCTION__, lib_name.c_str(),
369 crazy_context_get_error(context));
370 return false;
371 }
372
373 LOG_INFO("%s: Library %s using shared RELRO section!",
374 __FUNCTION__, lib_name.c_str());
375
376 return true;
377 }
378
379 static const JNINativeMethod kNativeMethods[] = {
380 {"nativeLoadLibrary",
381 "("
382 "Ljava/lang/String;"
383 "J"
384 "Lorg/chromium/base/Linker$LibInfo;"
385 ")"
386 "Z",
387 reinterpret_cast<void*>(&LoadLibrary)},
388 {"nativeEnableSharedRelro",
389 "("
390 "Ljava/lang/String;"
391 "Lorg/chromium/base/Linker$LibInfo;"
392 ")"
393 "Z",
394 reinterpret_cast<void*>(&EnableSharedRelro)},
395 {"nativeUseSharedRelro",
396 "("
397 "Ljava/lang/String;"
398 "Lorg/chromium/base/Linker$LibInfo;"
399 ")"
400 "Z",
401 reinterpret_cast<void*>(&UseSharedRelro)}, };
402
403 } // namespace
404
405 // JNI_OnLoad() hook called when the linker library is loaded through
406 // the regular System.LoadLibrary) API. This shall save the Java VM
407 // handle and initialize LibInfo fields.
408 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
409 g_java_vm = vm;
410
411 LOG_INFO("%s: Entering", __FUNCTION__);
412 // Get new JNIEnv
413 JNIEnv* env;
414 if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env),
415 JNI_VERSION_1_4)) {
416 LOG_ERROR("Could not create JNIEnv");
417 return -1;
418 }
419
420 // Register native methods.
421 jclass linker_class;
422 if (!InitClassReference(env, "org/chromium/base/Linker", &linker_class))
423 return -1;
424
425 LOG_INFO("%s: Registering native methods", __FUNCTION__);
426 env->RegisterNatives(linker_class,
427 kNativeMethods,
428 sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
429
430 // Find LibInfo field ids.
431 LOG_INFO("%s: Caching field IDs", __FUNCTION__);
432 if (!s_lib_info_fields.Init(env)) {
433 return -1;
434 }
435
436 LOG_INFO("%s: Done", __FUNCTION__);
437 return JNI_VERSION_1_4;
438 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698