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

Side by Side Diff: base/android/linker/crazy_linker.cpp

Issue 23717023: Android: Add chrome-specific dynamic linker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rename library 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 #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 }
OLDNEW
« no previous file with comments | « base/android/java/src/org/chromium/base/Linker.java ('k') | base/android/linker/crazy_linker/Android.mk » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698