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.
5 // This is the version of the Android-specific Chromium linker that uses
6 // the Android M and later system linker to load libraries.
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.
11 #include "modern_linker_jni.h"
15 #include <sys/types.h>
24 #include "android_dlext.h"
25 #include "linker_jni.h"
27 #define PAGE_START(x) ((x) & PAGE_MASK)
28 #define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
30 namespace chromium_android_linker
{
33 // Record of the Java VM passed to JNI_OnLoad().
34 static JavaVM
* s_java_vm
= nullptr;
36 // Get the CPU ABI string for which the linker is running.
38 // The returned string is used to construct the path to libchrome.so when
39 // loading directly from APK.
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";
59 #error "Unsupported target abi"
61 return env
->NewStringUTF(kCurrentAbi
);
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;
71 handle
= dlopen(nullptr, RTLD_NOW
);
73 void* result
= dlsym(handle
, symbol_name
);
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*),
87 using DlIteratePhdrCallback
= int (*)(dl_phdr_info
*, size_t, void*);
88 using DlIteratePhdrFunctionPtr
= int (*)(DlIteratePhdrCallback
, void*);
89 static DlIteratePhdrFunctionPtr function_ptr
= nullptr;
93 reinterpret_cast<DlIteratePhdrFunctionPtr
>(Dlsym("dl_iterate_phdr"));
95 LOG_ERROR("dlsym: dl_iterate_phdr: %s", dlerror());
100 *status
= (*function_ptr
)(callback
, data
);
104 // Convenience struct wrapper round android_dlextinfo.
105 struct AndroidDlextinfo
{
106 AndroidDlextinfo(int flags
,
107 void* reserved_addr
, size_t reserved_size
, int relro_fd
) {
108 memset(&extinfo
, 0, sizeof(extinfo
));
109 extinfo
.flags
= flags
;
110 extinfo
.reserved_addr
= reserved_addr
;
111 extinfo
.reserved_size
= reserved_size
;
112 extinfo
.relro_fd
= relro_fd
;
115 android_dlextinfo extinfo
;
118 // android_dlopen_ext() wrapper, accessed via dlsym lookup. Returns false
119 // if no android_dlopen_ext() is available, otherwise true with the return
120 // value from android_dlopen_ext() in |status|.
121 bool AndroidDlopenExt(const char* filename
,
123 const AndroidDlextinfo
* dlextinfo
,
125 using DlopenExtFunctionPtr
= void* (*)(const char*,
126 int, const android_dlextinfo
*);
127 static DlopenExtFunctionPtr function_ptr
= nullptr;
131 reinterpret_cast<DlopenExtFunctionPtr
>(Dlsym("android_dlopen_ext"));
133 LOG_ERROR("dlsym: android_dlopen_ext: %s", dlerror());
138 const android_dlextinfo
* extinfo
= &dlextinfo
->extinfo
;
139 LOG_INFO("android_dlopen_ext:"
140 " flags=0x%llx, reserved_addr=%p, reserved_size=%d, relro_fd=%d",
141 static_cast<long long>(extinfo
->flags
),
142 extinfo
->reserved_addr
,
143 static_cast<int>(extinfo
->reserved_size
),
146 *status
= (*function_ptr
)(filename
, flag
, extinfo
);
150 // Callback data for FindLoadedLibrarySize().
151 struct CallbackData
{
152 explicit CallbackData(void* address
)
153 : load_address(address
), load_size(0) { }
155 const void* load_address
;
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
);
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;
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
)
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
)
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
;
188 // If this library matches what we seek, return its load size.
190 callback_data
->load_size
= PAGE_END(max_vaddr
) - PAGE_START(min_vaddr
);
197 // Helper class for anonymous memory mapping.
198 class ScopedAnonymousMmap
{
200 ScopedAnonymousMmap(void* addr
, size_t size
);
202 ~ScopedAnonymousMmap() { munmap(addr_
, size_
); }
204 void* GetAddr() const { return effective_addr_
; }
205 void Release() { addr_
= nullptr; size_
= 0; effective_addr_
= nullptr; }
211 // The effective_addr_ is the address seen by client code. It may or may
212 // not be the same as addr_, the real start of the anonymous mapping.
213 void* effective_addr_
;
216 // ScopedAnonymousMmap constructor. |addr| is a requested mapping address, or
217 // zero if any address will do, and |size| is the size of mapping required.
218 ScopedAnonymousMmap::ScopedAnonymousMmap(void* addr
, size_t size
) {
219 #if RESERVE_BREAKPAD_GUARD_REGION
220 // Increase size to extend the address reservation mapping so that it will
221 // also include a guard region from load_bias_ to start_addr. If loading
222 // at a fixed address, move our requested address back by the guard region
224 size
+= kBreakpadGuardRegionBytes
;
226 if (addr
< reinterpret_cast<void*>(kBreakpadGuardRegionBytes
)) {
227 LOG_ERROR("Fixed address %p is too low to accommodate Breakpad guard",
233 addr
= reinterpret_cast<void*>(
234 reinterpret_cast<uintptr_t>(addr
) - kBreakpadGuardRegionBytes
);
236 LOG_INFO("Added %d to size, for Breakpad guard",
237 static_cast<int>(kBreakpadGuardRegionBytes
));
240 addr_
= mmap(addr
, size
, PROT_NONE
, MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
241 if (addr_
!= MAP_FAILED
) {
244 LOG_INFO("mmap failed: %s", strerror(errno
));
247 effective_addr_
= addr_
;
249 #if RESERVE_BREAKPAD_GUARD_REGION
250 // If we increased size to accommodate a Breakpad guard region, move
251 // the effective address, if valid, upwards by the size of the guard region.
252 if (addr_
== MAP_FAILED
)
254 if (addr_
< reinterpret_cast<void*>(kBreakpadGuardRegionBytes
)) {
255 LOG_ERROR("Map address %p is too low to accommodate Breakpad guard",
257 effective_addr_
= MAP_FAILED
;
259 effective_addr_
= reinterpret_cast<void*>(
260 reinterpret_cast<uintptr_t>(addr_
) + kBreakpadGuardRegionBytes
);
265 // Helper for LoadLibrary(). Return the actual size of the library loaded
266 // at |addr| in |load_size|. Returns false if the library appears not to
268 bool GetLibraryLoadSize(void* addr
, size_t* load_size
) {
269 LOG_INFO("Called for %p", addr
);
271 // Find the real load size for the library loaded at |addr|.
272 CallbackData
callback_data(addr
);
274 if (!DlIteratePhdr(&FindLoadedLibrarySize
, &callback_data
, &status
)) {
275 LOG_ERROR("No dl_iterate_phdr function found");
279 LOG_ERROR("Failed to find library at address %p", addr
);
283 *load_size
= callback_data
.load_size
;
287 // Helper for LoadLibrary(). We reserve an address space larger than
288 // needed. After library loading we want to trim that reservation to only
290 bool ResizeReservedAddressSpace(void* addr
,
291 size_t reserved_size
,
293 LOG_INFO("Called for %p, reserved %d, loaded %d",
294 addr
, static_cast<int>(reserved_size
), static_cast<int>(load_size
));
296 if (reserved_size
< load_size
) {
297 LOG_ERROR("WARNING: library reservation was too small");
301 // Unmap the part of the reserved address space that is beyond the end of
302 // the loaded library data.
304 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr
) + load_size
);
305 size_t length
= reserved_size
- load_size
;
306 if (munmap(unmap
, length
) == -1) {
307 LOG_ERROR("Failed to unmap %d at %p", static_cast<int>(length
), unmap
);
314 // Load a library with the chromium linker, using android_dlopen_ext().
316 // android_dlopen_ext() understands how to directly load from a zipfile,
317 // based on the format of |dlopen_ext_path|. If it contains a "!/" separator
318 // then the string indicates <zip_path>!/<file_path> and indicates the
319 // file_path element within the zip file at zip_path. A library in a
320 // zipfile must be uncompressed and page aligned. The library is expected
321 // to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the
322 // same as the abi for this linker. The "crazy." prefix is included
323 // so that the Android Package Manager doesn't extract the library into
326 // If |dlopen_ext_path| contains no "!/" separator then android_dlopen_ext()
327 // assumes that it is a normal path to a standalone library file.
329 // Loading the library will also call its JNI_OnLoad() method, which
330 // shall register its methods. Note that lazy native method resolution
331 // will _not_ work after this, because Dalvik uses the system's dlsym()
332 // which won't see the new library, so explicit registration is mandatory.
333 // Load a library with the chromium linker. This will also call its
334 // JNI_OnLoad() method, which shall register its methods. Note that
335 // lazy native method resolution will _not_ work after this, because
336 // Dalvik uses the system's dlsym() which won't see the new library,
337 // so explicit registration is mandatory.
339 // |env| is the current JNI environment handle.
340 // |clazz| is the static class handle for org.chromium.base.Linker,
341 // and is ignored here.
342 // |dlopen_ext_path| is the library identifier (e.g. libfoo.so).
343 // |load_address| is an explicit load address.
344 // |relro_path| is the path to the file into which RELRO data is held.
345 // |lib_info_obj| is a LibInfo handle used to communicate information
346 // with the Java side.
347 // Return true on success.
348 jboolean
LoadLibrary(JNIEnv
* env
,
350 jstring dlopen_ext_path
,
352 jobject lib_info_obj
) {
353 String
dlopen_library_path(env
, dlopen_ext_path
);
354 LOG_INFO("Called for %s, at address 0x%llx",
355 dlopen_library_path
.c_str(), load_address
);
357 if (!IsValidAddress(load_address
)) {
358 LOG_ERROR("Invalid address 0x%llx", load_address
);
362 const size_t size
= kAddressSpaceReservationSize
;
363 void* wanted_addr
= reinterpret_cast<void*>(load_address
);
365 // Reserve the address space into which we load the library.
366 ScopedAnonymousMmap
mapping(wanted_addr
, size
);
367 void* addr
= mapping
.GetAddr();
368 if (addr
== MAP_FAILED
) {
369 LOG_ERROR("Failed to reserve space for load");
372 if (wanted_addr
&& addr
!= wanted_addr
) {
373 LOG_ERROR("Failed to obtain fixed address for load");
377 // Build dlextinfo to load the library into the reserved space, using
378 // the shared RELRO if supplied and if its start address matches addr.
380 int flags
= ANDROID_DLEXT_RESERVED_ADDRESS
;
381 if (wanted_addr
&& lib_info_obj
) {
383 s_lib_info_fields
.GetRelroInfo(env
, lib_info_obj
,
384 reinterpret_cast<size_t*>(&relro_start
),
386 if (relro_fd
!= -1 && relro_start
== addr
) {
387 flags
|= ANDROID_DLEXT_USE_RELRO
;
390 AndroidDlextinfo
dlextinfo(flags
, addr
, size
, relro_fd
);
392 // Load the library into the reserved space.
393 const char* path
= dlopen_library_path
.c_str();
394 void* handle
= nullptr;
395 if (!AndroidDlopenExt(path
, RTLD_NOW
, &dlextinfo
, &handle
)) {
396 LOG_ERROR("No android_dlopen_ext function found");
399 if (handle
== nullptr) {
400 LOG_ERROR("android_dlopen_ext: %s", dlerror());
404 // After loading, trim the mapping to match the library's actual size.
405 size_t load_size
= 0;
406 if (!GetLibraryLoadSize(addr
, &load_size
)) {
407 LOG_ERROR("Unable to find size for load at %p", addr
);
410 if (!ResizeReservedAddressSpace(addr
, size
, load_size
)) {
411 LOG_ERROR("Unable to resize reserved address mapping");
415 // Locate and then call the loaded library's JNI_OnLoad() function. Check
416 // that it returns a usable JNI version.
417 using JNI_OnLoadFunctionPtr
= int (*)(void* vm
, void* reserved
);
419 reinterpret_cast<JNI_OnLoadFunctionPtr
>(dlsym(handle
, "JNI_OnLoad"));
420 if (jni_onload
== nullptr) {
421 LOG_ERROR("dlsym: JNI_OnLoad: %s", dlerror());
425 int jni_version
= (*jni_onload
)(s_java_vm
, nullptr);
426 if (jni_version
< JNI_VERSION_1_4
) {
427 LOG_ERROR("JNI version is invalid: %d", jni_version
);
431 // Release mapping before returning so that we do not unmap reserved space.
434 // Note the load address and load size in the supplied libinfo object.
435 const size_t cast_addr
= reinterpret_cast<size_t>(addr
);
436 s_lib_info_fields
.SetLoadInfo(env
, lib_info_obj
, cast_addr
, load_size
);
438 LOG_INFO("Success loading library %s", dlopen_library_path
.c_str());
442 // Create a shared RELRO file for a library, using android_dlopen_ext().
444 // Loads the library similarly to LoadLibrary() above, by reserving address
445 // space and then using android_dlopen_ext() to load into the reserved
446 // area. Adds flags to android_dlopen_ext() to saved the library's RELRO
447 // memory into the given file path, then unload the library and returns.
449 // Does not call JNI_OnLoad() or otherwise execute any code from the library.
451 // |env| is the current JNI environment handle.
452 // |clazz| is the static class handle for org.chromium.base.Linker,
453 // and is ignored here.
454 // |dlopen_ext_path| is the library identifier (e.g. libfoo.so).
455 // |load_address| is an explicit load address.
456 // |relro_path| is the path to the file into which RELRO data is written.
457 // |lib_info_obj| is a LibInfo handle used to communicate information
458 // with the Java side.
459 // Return true on success.
460 jboolean
CreateSharedRelro(JNIEnv
* env
,
462 jstring dlopen_ext_path
,
465 jobject lib_info_obj
) {
466 String
dlopen_library_path(env
, dlopen_ext_path
);
467 LOG_INFO("Called for %s, at address 0x%llx",
468 dlopen_library_path
.c_str(), load_address
);
470 if (!IsValidAddress(load_address
) || load_address
== 0) {
471 LOG_ERROR("Invalid address 0x%llx", load_address
);
475 const size_t size
= kAddressSpaceReservationSize
;
476 void* wanted_addr
= reinterpret_cast<void*>(load_address
);
478 // Reserve the address space into which we load the library.
479 ScopedAnonymousMmap
mapping(wanted_addr
, size
);
480 void* addr
= mapping
.GetAddr();
481 if (addr
== MAP_FAILED
) {
482 LOG_ERROR("Failed to reserve space for load");
485 if (addr
!= wanted_addr
) {
486 LOG_ERROR("Failed to obtain fixed address for load");
490 // Open the shared RELRO file for write. Overwrites any prior content.
491 String
shared_relro_path(env
, relro_path
);
492 const char* filepath
= shared_relro_path
.c_str();
494 int relro_fd
= open(filepath
, O_RDWR
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
495 if (relro_fd
== -1) {
496 LOG_ERROR("open: %s: %s", filepath
, strerror(errno
));
500 // Use android_dlopen_ext() to create the shared RELRO.
501 const int flags
= ANDROID_DLEXT_RESERVED_ADDRESS
502 | ANDROID_DLEXT_WRITE_RELRO
;
503 AndroidDlextinfo
dlextinfo(flags
, addr
, size
, relro_fd
);
505 const char* path
= dlopen_library_path
.c_str();
506 void* handle
= nullptr;
507 if (!AndroidDlopenExt(path
, RTLD_NOW
, &dlextinfo
, &handle
)) {
508 LOG_ERROR("No android_dlopen_ext function found");
512 if (handle
== nullptr) {
513 LOG_ERROR("android_dlopen_ext: %s", dlerror());
518 // Unload the library from this address. The reserved space is
519 // automatically unmapped on exit from this function.
522 // Reopen the shared RELFO fd in read-only mode. This ensures that nothing
523 // can write to it through the RELRO fd that we return in libinfo.
525 relro_fd
= open(filepath
, O_RDONLY
);
526 if (relro_fd
== -1) {
527 LOG_ERROR("open: %s: %s", filepath
, strerror(errno
));
531 // Delete the directory entry for the RELRO file. The fd we hold ensures
532 // that its data remains intact.
533 if (unlink(filepath
) == -1) {
534 LOG_ERROR("unlink: %s: %s", filepath
, strerror(errno
));
538 // Note the shared RELRO fd in the supplied libinfo object. In this
539 // implementation the RELRO start is set to the library's load address,
540 // and the RELRO size is unused.
541 const size_t cast_addr
= reinterpret_cast<size_t>(addr
);
542 s_lib_info_fields
.SetRelroInfo(env
, lib_info_obj
, cast_addr
, 0, relro_fd
);
544 LOG_INFO("Success creating shared RELRO %s", shared_relro_path
.c_str());
548 const JNINativeMethod kNativeMethods
[] = {
552 "Ljava/lang/String;",
553 reinterpret_cast<void*>(&GetCpuAbi
)},
554 {"nativeLoadLibrary",
558 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
561 reinterpret_cast<void*>(&LoadLibrary
)},
562 {"nativeCreateSharedRelro",
567 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
570 reinterpret_cast<void*>(&CreateSharedRelro
)},
575 bool ModernLinkerJNIInit(JavaVM
* vm
, JNIEnv
* env
) {
576 LOG_INFO("Entering");
578 // Register native methods.
580 if (!InitClassReference(env
,
581 "org/chromium/base/library_loader/ModernLinker",
585 LOG_INFO("Registering native methods");
586 env
->RegisterNatives(linker_class
,
588 sizeof(kNativeMethods
) / sizeof(kNativeMethods
[0]));
590 // Record the Java VM handle.
596 } // namespace chromium_android_linker