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

Side by Side Diff: content/common/android/linker/chrome_linker_jni.cc

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

Powered by Google App Engine
This is Rietveld 408576698