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

Side by Side Diff: android/linker/modern_linker_jni.cc

Issue 2043183002: Update to Chromium //base at Chromium commit 01cb97b2e09618bbc3a60c7348f0a844eea20547. (Closed) Base URL: https://github.com/domokit/base.git@master
Patch Set: Created 4 years, 6 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
« no previous file with comments | « android/linker/modern_linker_jni.h ('k') | callback_internal.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 version of the Android-specific Chromium linker that uses
6 // the Android M and later system linker to load libraries.
7
8 // This source code *cannot* depend on anything from base/ or the C++
9 // STL, to keep the final library small, and avoid ugly dependency issues.
10
11 #include "modern_linker_jni.h"
12
13 #include <sys/mman.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <dlfcn.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <jni.h>
20 #include <limits.h>
21 #include <link.h>
22 #include <string.h>
23
24 #include "android_dlext.h"
25 #include "linker_jni.h"
26
27 #define PAGE_START(x) ((x)&PAGE_MASK)
28 #define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
29
30 namespace chromium_android_linker {
31 namespace {
32
33 // Record of the Java VM passed to JNI_OnLoad().
34 static JavaVM* s_java_vm = nullptr;
35
36 // Get the CPU ABI string for which the linker is running.
37 //
38 // The returned string is used to construct the path to libchrome.so when
39 // loading directly from APK.
40 //
41 // |env| is the current JNI environment handle.
42 // |clazz| is the static class handle for org.chromium.base.Linker,
43 // and is ignored here.
44 // Returns the CPU ABI string for which the linker is running.
45 jstring GetCpuAbi(JNIEnv* env, jclass clazz) {
46 #if defined(__arm__) && defined(__ARM_ARCH_7A__)
47 static const char* kCurrentABI = "armeabi-v7a";
48 #elif defined(__arm__)
49 static const char* kCurrentABI = "armeabi";
50 #elif defined(__i386__)
51 static const char* kCurrentABI = "x86";
52 #elif defined(__mips__)
53 static const char* kCurrentABI = "mips";
54 #elif defined(__x86_64__)
55 static const char* kCurrentABI = "x86_64";
56 #elif defined(__aarch64__)
57 static const char* kCurrentABI = "arm64-v8a";
58 #else
59 #error "Unsupported target abi"
60 #endif
61 return env->NewStringUTF(kCurrentABI);
62 }
63
64 // Convenience wrapper around dlsym() on the main executable. Returns
65 // the address of the requested symbol, or nullptr if not found. Status
66 // is available from dlerror().
67 void* Dlsym(const char* symbol_name) {
68 static void* handle = nullptr;
69
70 if (!handle)
71 handle = dlopen(nullptr, RTLD_NOW);
72
73 void* result = dlsym(handle, symbol_name);
74 return result;
75 }
76
77 // dl_iterate_phdr() wrapper, accessed via dlsym lookup. Done this way.
78 // so that this code compiles for Android versions that are too early to
79 // offer it. Checks in LibraryLoader.java should ensure that we
80 // never reach here at runtime on Android versions that are too old to
81 // supply dl_iterate_phdr; that is, earlier than Android M. Returns
82 // false if no dl_iterate_phdr() is available, otherwise true with the
83 // return value from dl_iterate_phdr() in |status|.
84 bool DlIteratePhdr(int (*callback)(dl_phdr_info*, size_t, void*),
85 void* data,
86 int* status) {
87 using DlIteratePhdrCallback = int (*)(dl_phdr_info*, size_t, void*);
88 using DlIteratePhdrFunctionPtr = int (*)(DlIteratePhdrCallback, void*);
89 static DlIteratePhdrFunctionPtr function_ptr = nullptr;
90
91 if (!function_ptr) {
92 function_ptr =
93 reinterpret_cast<DlIteratePhdrFunctionPtr>(Dlsym("dl_iterate_phdr"));
94 if (!function_ptr) {
95 LOG_ERROR("dlsym: dl_iterate_phdr: %s", dlerror());
96 return false;
97 }
98 }
99
100 *status = (*function_ptr)(callback, data);
101 return true;
102 }
103
104 // Convenience struct wrapper round android_dlextinfo.
105 struct AndroidDlextinfo {
106 AndroidDlextinfo(int flags,
107 void* reserved_addr,
108 size_t reserved_size,
109 int relro_fd) {
110 memset(&extinfo, 0, sizeof(extinfo));
111 extinfo.flags = flags;
112 extinfo.reserved_addr = reserved_addr;
113 extinfo.reserved_size = reserved_size;
114 extinfo.relro_fd = relro_fd;
115 }
116
117 android_dlextinfo extinfo;
118 };
119
120 // android_dlopen_ext() wrapper, accessed via dlsym lookup. Returns false
121 // if no android_dlopen_ext() is available, otherwise true with the return
122 // value from android_dlopen_ext() in |status|.
123 bool AndroidDlopenExt(const char* filename,
124 int flag,
125 const AndroidDlextinfo* dlextinfo,
126 void** status) {
127 using DlopenExtFunctionPtr =
128 void* (*)(const char*, int, const android_dlextinfo*);
129 static DlopenExtFunctionPtr function_ptr = nullptr;
130
131 if (!function_ptr) {
132 function_ptr =
133 reinterpret_cast<DlopenExtFunctionPtr>(Dlsym("android_dlopen_ext"));
134 if (!function_ptr) {
135 LOG_ERROR("dlsym: android_dlopen_ext: %s", dlerror());
136 return false;
137 }
138 }
139
140 const android_dlextinfo* extinfo = &dlextinfo->extinfo;
141 LOG_INFO(
142 "android_dlopen_ext:"
143 " flags=0x%llx, reserved_addr=%p, reserved_size=%d, relro_fd=%d",
144 extinfo->flags, extinfo->reserved_addr, extinfo->reserved_size,
145 extinfo->relro_fd);
146
147 *status = (*function_ptr)(filename, flag, extinfo);
148 return true;
149 }
150
151 // Callback data for FindLoadedLibrarySize().
152 struct CallbackData {
153 explicit CallbackData(void* address) : load_address(address), load_size(0) {}
154
155 const void* load_address;
156 size_t load_size;
157 };
158
159 // Callback for dl_iterate_phdr(). Read phdrs to identify whether or not
160 // this library's load address matches the |load_address| passed in
161 // |data|. If yes, pass back load size via |data|. A non-zero return value
162 // terminates iteration.
163 int FindLoadedLibrarySize(dl_phdr_info* info, size_t size UNUSED, void* data) {
164 CallbackData* callback_data = reinterpret_cast<CallbackData*>(data);
165
166 // Use max and min vaddr to compute the library's load size.
167 ElfW(Addr) min_vaddr = ~0;
168 ElfW(Addr) max_vaddr = 0;
169
170 bool is_matching = false;
171 for (size_t i = 0; i < info->dlpi_phnum; ++i) {
172 const ElfW(Phdr)* phdr = &info->dlpi_phdr[i];
173 if (phdr->p_type != PT_LOAD)
174 continue;
175
176 // See if this segment's load address matches what we passed to
177 // android_dlopen_ext as extinfo.reserved_addr.
178 void* load_addr = reinterpret_cast<void*>(info->dlpi_addr + phdr->p_vaddr);
179 if (load_addr == callback_data->load_address)
180 is_matching = true;
181
182 if (phdr->p_vaddr < min_vaddr)
183 min_vaddr = phdr->p_vaddr;
184 if (phdr->p_vaddr + phdr->p_memsz > max_vaddr)
185 max_vaddr = phdr->p_vaddr + phdr->p_memsz;
186 }
187
188 // If this library matches what we seek, return its load size.
189 if (is_matching) {
190 callback_data->load_size = PAGE_END(max_vaddr) - PAGE_START(min_vaddr);
191 return true;
192 }
193
194 return false;
195 }
196
197 // Helper class for anonymous memory mapping.
198 class ScopedAnonymousMmap {
199 public:
200 ScopedAnonymousMmap(void* addr, size_t size);
201
202 ~ScopedAnonymousMmap() { munmap(addr_, size_); }
203
204 void* GetAddr() const { return effective_addr_; }
205 void Release() {
206 addr_ = nullptr;
207 size_ = 0;
208 effective_addr_ = nullptr;
209 }
210
211 private:
212 void* addr_;
213 size_t size_;
214
215 // The effective_addr_ is the address seen by client code. It may or may
216 // not be the same as addr_, the real start of the anonymous mapping.
217 void* effective_addr_;
218 };
219
220 // ScopedAnonymousMmap constructor. |addr| is a requested mapping address, or
221 // zero if any address will do, and |size| is the size of mapping required.
222 ScopedAnonymousMmap::ScopedAnonymousMmap(void* addr, size_t size) {
223 #if RESERVE_BREAKPAD_GUARD_REGION
224 // Increase size to extend the address reservation mapping so that it will
225 // also include a guard region from load_bias_ to start_addr. If loading
226 // at a fixed address, move our requested address back by the guard region
227 // size.
228 size += kBreakpadGuardRegionBytes;
229 if (addr) {
230 if (addr < reinterpret_cast<void*>(kBreakpadGuardRegionBytes)) {
231 LOG_ERROR("Fixed address %p is too low to accommodate Breakpad guard",
232 addr);
233 addr_ = MAP_FAILED;
234 size_ = 0;
235 return;
236 }
237 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) -
238 kBreakpadGuardRegionBytes);
239 }
240 LOG_INFO("Added %d to size, for Breakpad guard", kBreakpadGuardRegionBytes);
241 #endif
242
243 addr_ = mmap(addr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
244 if (addr_ != MAP_FAILED) {
245 size_ = size;
246 } else {
247 LOG_INFO("mmap failed: %s", strerror(errno));
248 size_ = 0;
249 }
250 effective_addr_ = addr_;
251
252 #if RESERVE_BREAKPAD_GUARD_REGION
253 // If we increased size to accommodate a Breakpad guard region, move
254 // the effective address, if valid, upwards by the size of the guard region.
255 if (addr_ == MAP_FAILED)
256 return;
257 if (addr_ < reinterpret_cast<void*>(kBreakpadGuardRegionBytes)) {
258 LOG_ERROR("Map address %p is too low to accommodate Breakpad guard", addr_);
259 effective_addr_ = MAP_FAILED;
260 } else {
261 effective_addr_ = reinterpret_cast<void*>(
262 reinterpret_cast<uintptr_t>(addr_) + kBreakpadGuardRegionBytes);
263 }
264 #endif
265 }
266
267 // Helper for LoadLibrary(). Return the actual size of the library loaded
268 // at |addr| in |load_size|. Returns false if the library appears not to
269 // be loaded.
270 bool GetLibraryLoadSize(void* addr, size_t* load_size) {
271 LOG_INFO("Called for %p", addr);
272
273 // Find the real load size for the library loaded at |addr|.
274 CallbackData callback_data(addr);
275 int status = 0;
276 if (!DlIteratePhdr(&FindLoadedLibrarySize, &callback_data, &status)) {
277 LOG_ERROR("No dl_iterate_phdr function found");
278 return false;
279 }
280 if (!status) {
281 LOG_ERROR("Failed to find library at address %p", addr);
282 return false;
283 }
284
285 *load_size = callback_data.load_size;
286 return true;
287 }
288
289 // Helper for LoadLibrary(). We reserve an address space larger than
290 // needed. After library loading we want to trim that reservation to only
291 // what is needed.
292 bool ResizeReservedAddressSpace(void* addr,
293 size_t reserved_size,
294 size_t load_size) {
295 LOG_INFO("Called for %p, reserved %d, loaded %d", addr, reserved_size,
296 load_size);
297
298 if (reserved_size < load_size) {
299 LOG_ERROR("WARNING: library reservation was too small");
300 return true;
301 }
302
303 // Unmap the part of the reserved address space that is beyond the end of
304 // the loaded library data.
305 void* unmap =
306 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) + load_size);
307 size_t length = reserved_size - load_size;
308 if (munmap(unmap, length) == -1) {
309 LOG_ERROR("Failed to unmap %d at %p", static_cast<int>(length), unmap);
310 return false;
311 }
312
313 return true;
314 }
315
316 // Load a library with the chromium linker, using android_dlopen_ext().
317 //
318 // android_dlopen_ext() understands how to directly load from a zipfile,
319 // based on the format of |dlopen_ext_path|. If it contains a "!/" separator
320 // then the string indicates <zip_path>!/<file_path> and indicates the
321 // file_path element within the zip file at zip_path. A library in a
322 // zipfile must be uncompressed and page aligned. The library is expected
323 // to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the
324 // same as the abi for this linker. The "crazy." prefix is included
325 // so that the Android Package Manager doesn't extract the library into
326 // /data/app-lib.
327 //
328 // If |dlopen_ext_path| contains no "!/" separator then android_dlopen_ext()
329 // assumes that it is a normal path to a standalone library file.
330 //
331 // Loading the library will also call its JNI_OnLoad() method, which
332 // shall register its methods. Note that lazy native method resolution
333 // will _not_ work after this, because Dalvik uses the system's dlsym()
334 // which won't see the new library, so explicit registration is mandatory.
335 // Load a library with the chromium linker. This will also call its
336 // JNI_OnLoad() method, which shall register its methods. Note that
337 // lazy native method resolution will _not_ work after this, because
338 // Dalvik uses the system's dlsym() which won't see the new library,
339 // so explicit registration is mandatory.
340 //
341 // |env| is the current JNI environment handle.
342 // |clazz| is the static class handle for org.chromium.base.Linker,
343 // and is ignored here.
344 // |dlopen_ext_path| is the library identifier (e.g. libfoo.so).
345 // |load_address| is an explicit load address.
346 // |relro_path| is the path to the file into which RELRO data is held.
347 // |lib_info_obj| is a LibInfo handle used to communicate information
348 // with the Java side.
349 // Return true on success.
350 jboolean LoadLibrary(JNIEnv* env,
351 jclass clazz,
352 jstring dlopen_ext_path,
353 jlong load_address,
354 jobject lib_info_obj) {
355 String dlopen_library_path(env, dlopen_ext_path);
356 LOG_INFO("Called for %s, at address 0x%llx", dlopen_library_path.c_str(),
357 load_address);
358
359 if (!IsValidAddress(load_address)) {
360 LOG_ERROR("Invalid address 0x%llx", load_address);
361 return false;
362 }
363
364 const size_t size = kAddressSpaceReservationSize;
365 void* wanted_addr = reinterpret_cast<void*>(load_address);
366
367 // Reserve the address space into which we load the library.
368 ScopedAnonymousMmap mapping(wanted_addr, size);
369 void* addr = mapping.GetAddr();
370 if (addr == MAP_FAILED) {
371 LOG_ERROR("Failed to reserve space for load");
372 return false;
373 }
374 if (wanted_addr && addr != wanted_addr) {
375 LOG_ERROR("Failed to obtain fixed address for load");
376 return false;
377 }
378
379 // Build dlextinfo to load the library into the reserved space, using
380 // the shared RELRO if supplied and if its start address matches addr.
381 int relro_fd = -1;
382 int flags = ANDROID_DLEXT_RESERVED_ADDRESS;
383 if (wanted_addr && lib_info_obj) {
384 void* relro_start;
385 s_lib_info_fields.GetRelroInfo(env, lib_info_obj,
386 reinterpret_cast<size_t*>(&relro_start),
387 nullptr, &relro_fd);
388 if (relro_fd != -1 && relro_start == addr) {
389 flags |= ANDROID_DLEXT_USE_RELRO;
390 }
391 }
392 AndroidDlextinfo dlextinfo(flags, addr, size, relro_fd);
393
394 // Load the library into the reserved space.
395 const char* path = dlopen_library_path.c_str();
396 void* handle = nullptr;
397 if (!AndroidDlopenExt(path, RTLD_NOW, &dlextinfo, &handle)) {
398 LOG_ERROR("No android_dlopen_ext function found");
399 return false;
400 }
401 if (handle == nullptr) {
402 LOG_ERROR("android_dlopen_ext: %s", dlerror());
403 return false;
404 }
405
406 // After loading, trim the mapping to match the library's actual size.
407 size_t load_size = 0;
408 if (!GetLibraryLoadSize(addr, &load_size)) {
409 LOG_ERROR("Unable to find size for load at %p", addr);
410 return false;
411 }
412 if (!ResizeReservedAddressSpace(addr, size, load_size)) {
413 LOG_ERROR("Unable to resize reserved address mapping");
414 return false;
415 }
416
417 // Locate and then call the loaded library's JNI_OnLoad() function. Check
418 // that it returns a usable JNI version.
419 using JNI_OnLoadFunctionPtr = int (*)(void* vm, void* reserved);
420 auto jni_onload =
421 reinterpret_cast<JNI_OnLoadFunctionPtr>(dlsym(handle, "JNI_OnLoad"));
422 if (jni_onload == nullptr) {
423 LOG_ERROR("dlsym: JNI_OnLoad: %s", dlerror());
424 return false;
425 }
426
427 int jni_version = (*jni_onload)(s_java_vm, nullptr);
428 if (jni_version < JNI_VERSION_1_4) {
429 LOG_ERROR("JNI version is invalid: %d", jni_version);
430 return false;
431 }
432
433 // Release mapping before returning so that we do not unmap reserved space.
434 mapping.Release();
435
436 // Note the load address and load size in the supplied libinfo object.
437 const size_t cast_addr = reinterpret_cast<size_t>(addr);
438 s_lib_info_fields.SetLoadInfo(env, lib_info_obj, cast_addr, load_size);
439
440 LOG_INFO("Success loading library %s", dlopen_library_path.c_str());
441 return true;
442 }
443
444 // Create a shared RELRO file for a library, using android_dlopen_ext().
445 //
446 // Loads the library similarly to LoadLibrary() above, by reserving address
447 // space and then using android_dlopen_ext() to load into the reserved
448 // area. Adds flags to android_dlopen_ext() to saved the library's RELRO
449 // memory into the given file path, then unload the library and returns.
450 //
451 // Does not call JNI_OnLoad() or otherwise execute any code from the library.
452 //
453 // |env| is the current JNI environment handle.
454 // |clazz| is the static class handle for org.chromium.base.Linker,
455 // and is ignored here.
456 // |dlopen_ext_path| is the library identifier (e.g. libfoo.so).
457 // |load_address| is an explicit load address.
458 // |relro_path| is the path to the file into which RELRO data is written.
459 // |lib_info_obj| is a LibInfo handle used to communicate information
460 // with the Java side.
461 // Return true on success.
462 jboolean CreateSharedRelro(JNIEnv* env,
463 jclass clazz,
464 jstring dlopen_ext_path,
465 jlong load_address,
466 jstring relro_path,
467 jobject lib_info_obj) {
468 String dlopen_library_path(env, dlopen_ext_path);
469 LOG_INFO("Called for %s, at address 0x%llx", dlopen_library_path.c_str(),
470 load_address);
471
472 if (!IsValidAddress(load_address) || load_address == 0) {
473 LOG_ERROR("Invalid address 0x%llx", load_address);
474 return false;
475 }
476
477 const size_t size = kAddressSpaceReservationSize;
478 void* wanted_addr = reinterpret_cast<void*>(load_address);
479
480 // Reserve the address space into which we load the library.
481 ScopedAnonymousMmap mapping(wanted_addr, size);
482 void* addr = mapping.GetAddr();
483 if (addr == MAP_FAILED) {
484 LOG_ERROR("Failed to reserve space for load");
485 return false;
486 }
487 if (addr != wanted_addr) {
488 LOG_ERROR("Failed to obtain fixed address for load");
489 return false;
490 }
491
492 // Open the shared RELRO file for write. Overwrites any prior content.
493 String shared_relro_path(env, relro_path);
494 const char* filepath = shared_relro_path.c_str();
495 unlink(filepath);
496 int relro_fd = open(filepath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
497 if (relro_fd == -1) {
498 LOG_ERROR("open: %s: %s", filepath, strerror(errno));
499 return false;
500 }
501
502 // Use android_dlopen_ext() to create the shared RELRO.
503 const int flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO;
504 AndroidDlextinfo dlextinfo(flags, addr, size, relro_fd);
505
506 const char* path = dlopen_library_path.c_str();
507 void* handle = nullptr;
508 if (!AndroidDlopenExt(path, RTLD_NOW, &dlextinfo, &handle)) {
509 LOG_ERROR("No android_dlopen_ext function found");
510 close(relro_fd);
511 return false;
512 }
513 if (handle == nullptr) {
514 LOG_ERROR("android_dlopen_ext: %s", dlerror());
515 close(relro_fd);
516 return false;
517 }
518
519 // Unload the library from this address. The reserved space is
520 // automatically unmapped on exit from this function.
521 dlclose(handle);
522
523 // Reopen the shared RELFO fd in read-only mode. This ensures that nothing
524 // can write to it through the RELRO fd that we return in libinfo.
525 close(relro_fd);
526 relro_fd = open(filepath, O_RDONLY);
527 if (relro_fd == -1) {
528 LOG_ERROR("open: %s: %s", filepath, strerror(errno));
529 return false;
530 }
531
532 // Delete the directory entry for the RELRO file. The fd we hold ensures
533 // that its data remains intact.
534 if (unlink(filepath) == -1) {
535 LOG_ERROR("unlink: %s: %s", filepath, strerror(errno));
536 return false;
537 }
538
539 // Note the shared RELRO fd in the supplied libinfo object. In this
540 // implementation the RELRO start is set to the library's load address,
541 // and the RELRO size is unused.
542 const size_t cast_addr = reinterpret_cast<size_t>(addr);
543 s_lib_info_fields.SetRelroInfo(env, lib_info_obj, cast_addr, 0, relro_fd);
544
545 LOG_INFO("Success creating shared RELRO %s", shared_relro_path.c_str());
546 return true;
547 }
548
549 const JNINativeMethod kNativeMethods[] = {
550 {"nativeGetCpuAbi",
551 "("
552 ")"
553 "Ljava/lang/String;",
554 reinterpret_cast<void*>(&GetCpuAbi)},
555 {"nativeLoadLibrary",
556 "("
557 "Ljava/lang/String;"
558 "J"
559 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
560 ")"
561 "Z",
562 reinterpret_cast<void*>(&LoadLibrary)},
563 {"nativeCreateSharedRelro",
564 "("
565 "Ljava/lang/String;"
566 "J"
567 "Ljava/lang/String;"
568 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
569 ")"
570 "Z",
571 reinterpret_cast<void*>(&CreateSharedRelro)},
572 };
573
574 } // namespace
575
576 bool ModernLinkerJNIInit(JavaVM* vm, JNIEnv* env) {
577 LOG_INFO("Entering");
578
579 // Register native methods.
580 jclass linker_class;
581 if (!InitClassReference(env, "org/chromium/base/library_loader/ModernLinker",
582 &linker_class))
583 return false;
584
585 LOG_INFO("Registering native methods");
586 env->RegisterNatives(linker_class, kNativeMethods,
587 sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
588
589 // Record the Java VM handle.
590 s_java_vm = vm;
591
592 return true;
593 }
594
595 } // namespace chromium_android_linker
OLDNEW
« no previous file with comments | « android/linker/modern_linker_jni.h ('k') | callback_internal.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698