Add flag that makes linker honor min(p_vaddr)

(cherry picked from commit 8a1162835597be38705b381ac34c07da17932568)

Bug: 21924613
Bug: http://b/21523078
Change-Id: I3f944a08dd2ed1df4d8a807da4fee423fdd35eb7
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index f10a8a2..40f610f 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -73,6 +73,13 @@
    */
   ANDROID_DLEXT_FORCE_LOAD = 0x40,
 
+  /* When set, if the minimum p_vaddr of the ELF file's PT_LOAD segments is non-zero,
+   * the dynamic linker will load it at that address.
+   *
+   * This flag is for ART internal use only.
+   */
+  ANDROID_DLEXT_FORCE_FIXED_VADDR = 0x80,
+
   /* Mask of valid bits */
   ANDROID_DLEXT_VALID_FLAG_BITS       = ANDROID_DLEXT_RESERVED_ADDRESS |
                                         ANDROID_DLEXT_RESERVED_ADDRESS_HINT |
@@ -80,7 +87,8 @@
                                         ANDROID_DLEXT_USE_RELRO |
                                         ANDROID_DLEXT_USE_LIBRARY_FD |
                                         ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET |
-                                        ANDROID_DLEXT_FORCE_LOAD,
+                                        ANDROID_DLEXT_FORCE_LOAD |
+                                        ANDROID_DLEXT_FORCE_FIXED_VADDR,
 };
 
 typedef struct {
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 638c9d6..f586b08 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -316,6 +316,8 @@
   void* start;
   size_t reserved_size = 0;
   bool reserved_hint = true;
+  // Assume position independent executable by default.
+  uint8_t* mmap_hint = nullptr;
 
   if (extinfo != nullptr) {
     if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {
@@ -324,6 +326,10 @@
     } else if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS_HINT) {
       reserved_size = extinfo->reserved_size;
     }
+
+    if ((extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) {
+      mmap_hint = addr;
+    }
   }
 
   if (load_size_ > reserved_size) {
@@ -333,7 +339,7 @@
       return false;
     }
     int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
-    start = mmap(nullptr, load_size_, PROT_NONE, mmap_flags, -1, 0);
+    start = mmap(mmap_hint, load_size_, PROT_NONE, mmap_flags, -1, 0);
     if (start == MAP_FAILED) {
       DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_);
       return false;