Implement load at fixed address feature

Bug: http://b/24683631
Change-Id: I3a39ab526c8f9e213339b60e135e5459d0f41381
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;
   }