Implement load at fixed address feature

Bug: http://b/24683631
Change-Id: I3a39ab526c8f9e213339b60e135e5459d0f41381
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index 40f610f..4af5de3 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -80,6 +80,24 @@
    */
   ANDROID_DLEXT_FORCE_FIXED_VADDR = 0x80,
 
+  /* Instructs dlopen to load the library at the address specified by reserved_addr.
+   *
+   * The difference between ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS and ANDROID_DLEXT_RESERVED_ADDRESS
+   * is that for ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS the linker reserves memory at reserved_addr
+   * whereas for ANDROID_DLEXT_RESERVED_ADDRESS the linker relies on the caller to reserve the memory.
+   *
+   * This flag can be used with ANDROID_DLEXT_FORCE_FIXED_VADDR; when ANDROID_DLEXT_FORCE_FIXED_VADDR
+   * is set and load_bias is not 0 (load_bias is min(p_vaddr) of PT_LOAD segments) this flag is ignored.
+   * This is implemented this way because the linker has to pick one address over the other and this
+   * way is more convenient for art. Note that ANDROID_DLEXT_FORCE_FIXED_VADDR does not generate
+   * an error when min(p_vaddr) is 0.
+   *
+   * Cannot be used with ANDROID_DLEXT_RESERVED_ADDRESS or ANDROID_DLEXT_RESERVED_ADDRESS_HINT.
+   *
+   * This flag is for ART internal use only.
+   */
+  ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS = 0x100,
+
   /* Mask of valid bits */
   ANDROID_DLEXT_VALID_FLAG_BITS       = ANDROID_DLEXT_RESERVED_ADDRESS |
                                         ANDROID_DLEXT_RESERVED_ADDRESS_HINT |
@@ -88,7 +106,8 @@
                                         ANDROID_DLEXT_USE_LIBRARY_FD |
                                         ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET |
                                         ANDROID_DLEXT_FORCE_LOAD |
-                                        ANDROID_DLEXT_FORCE_FIXED_VADDR,
+                                        ANDROID_DLEXT_FORCE_FIXED_VADDR |
+                                        ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS,
 };
 
 typedef struct {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 926fc9b..9f0b559 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1995,12 +1995,20 @@
       DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags);
       return nullptr;
     }
+
     if ((extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) == 0 &&
         (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET) != 0) {
       DL_ERR("invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without "
           "ANDROID_DLEXT_USE_LIBRARY_FD): 0x%" PRIx64, extinfo->flags);
       return nullptr;
     }
+
+    if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0 &&
+        (extinfo->flags & (ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT)) != 0) {
+      DL_ERR("invalid extended flag combination: ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS is not "
+             "compatible with ANDROID_DLEXT_RESERVED_ADDRESS/ANDROID_DLEXT_RESERVED_ADDRESS_HINT");
+      return nullptr;
+    }
   }
 
   ProtectedDataGuard guard;
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index a26187a..347983e 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -386,8 +386,9 @@
   void* start;
   size_t reserved_size = 0;
   bool reserved_hint = true;
+  bool strict_hint = false;
   // Assume position independent executable by default.
-  uint8_t* mmap_hint = nullptr;
+  void* mmap_hint = nullptr;
 
   if (extinfo != nullptr) {
     if (extinfo->flags & ANDROID_DLEXT_RESERVED_ADDRESS) {
@@ -397,8 +398,11 @@
       reserved_size = extinfo->reserved_size;
     }
 
-    if ((extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) {
+    if (addr != nullptr && (extinfo->flags & ANDROID_DLEXT_FORCE_FIXED_VADDR) != 0) {
       mmap_hint = addr;
+    } else if ((extinfo->flags & ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS) != 0) {
+      mmap_hint = extinfo->reserved_addr;
+      strict_hint = true;
     }
   }
 
@@ -414,6 +418,12 @@
       DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str());
       return false;
     }
+    if (strict_hint && (start != mmap_hint)) {
+      munmap(start, load_size_);
+      DL_ERR("couldn't reserve %zd bytes of address space at %p for \"%s\"",
+             load_size_, mmap_hint, name_.c_str());
+      return false;
+    }
   } else {
     start = extinfo->reserved_addr;
   }
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index d5a5e56..baf7eb5 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -343,6 +343,43 @@
   EXPECT_EQ(4, f());
 }
 
+TEST_F(DlExtTest, LoadAtFixedAddress) {
+  void* start = mmap(nullptr, LIBSIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
+                     -1, 0);
+  ASSERT_TRUE(start != MAP_FAILED);
+  munmap(start, LIBSIZE);
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS;
+  extinfo.reserved_addr = start;
+
+  handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
+  ASSERT_DL_NOTNULL(handle_);
+  fn f = reinterpret_cast<fn>(dlsym(handle_, "getRandomNumber"));
+  ASSERT_DL_NOTNULL(f);
+  EXPECT_GE(reinterpret_cast<void*>(f), start);
+  EXPECT_LT(reinterpret_cast<void*>(f), reinterpret_cast<char*>(start) + LIBSIZE);
+
+  EXPECT_EQ(4, f());
+}
+
+TEST_F(DlExtTest, LoadAtFixedAddressTooSmall) {
+  void* start = mmap(nullptr, LIBSIZE + PAGE_SIZE, PROT_NONE,
+                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_TRUE(start != MAP_FAILED);
+  munmap(start, LIBSIZE + PAGE_SIZE);
+  void* new_addr = mmap(reinterpret_cast<uint8_t*>(start) + PAGE_SIZE, LIBSIZE, PROT_NONE,
+                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_TRUE(new_addr != MAP_FAILED);
+
+  android_dlextinfo extinfo;
+  extinfo.flags = ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS;
+  extinfo.reserved_addr = start;
+
+  handle_ = android_dlopen_ext(LIBNAME, RTLD_NOW, &extinfo);
+  ASSERT_TRUE(handle_ == nullptr);
+}
+
 class DlExtRelroSharingTest : public DlExtTest {
 protected:
   virtual void SetUp() {