Honor p_vaddr if set

(cherry picked from commit 88ff15c2c279d2bbe3569101b36cd2aa0931a0a9)

Change-Id: I4aabbe911d30aea8ace69e29bb6e980a4e89de90
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index d1387f6..83f163c 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -226,24 +226,32 @@
   return true;
 }
 
-/* Compute the extent of all loadable segments in an ELF program header
- * table. This corresponds to the page-aligned size in bytes that needs to be
- * reserved in the process' address space
+/* Returns the size of the extent of all the possibly non-contiguous
+ * loadable segments in an ELF program header table. This corresponds
+ * to the page-aligned size in bytes that needs to be reserved in the
+ * process' address space. If there are no loadable segments, 0 is
+ * returned.
  *
- * This returns 0 if there are no loadable segments.
+ * If out_min_vaddr or out_max_vaddr are non-NULL, they will be
+ * set to the minimum and maximum addresses of pages to be reserved,
+ * or 0 if there is nothing to load.
  */
-Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
-                                    size_t phdr_count)
+size_t phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
+                                size_t phdr_count,
+                                Elf32_Addr* out_min_vaddr,
+                                Elf32_Addr* out_max_vaddr)
 {
     Elf32_Addr min_vaddr = 0xFFFFFFFFU;
     Elf32_Addr max_vaddr = 0x00000000U;
 
+    bool found_pt_load = false;
     for (size_t i = 0; i < phdr_count; ++i) {
         const Elf32_Phdr* phdr = &phdr_table[i];
 
         if (phdr->p_type != PT_LOAD) {
             continue;
         }
+        found_pt_load = true;
 
         if (phdr->p_vaddr < min_vaddr) {
             min_vaddr = phdr->p_vaddr;
@@ -253,14 +261,19 @@
             max_vaddr = phdr->p_vaddr + phdr->p_memsz;
         }
     }
-
-    if (min_vaddr > max_vaddr) {
-        return 0;
+    if (!found_pt_load) {
+        min_vaddr = 0x00000000U;
     }
 
     min_vaddr = PAGE_START(min_vaddr);
     max_vaddr = PAGE_END(max_vaddr);
 
+    if (out_min_vaddr != NULL) {
+        *out_min_vaddr = min_vaddr;
+    }
+    if (out_max_vaddr != NULL) {
+        *out_max_vaddr = max_vaddr;
+    }
     return max_vaddr - min_vaddr;
 }
 
@@ -268,29 +281,23 @@
 // segments of a program header table. This is done by creating a
 // private anonymous mmap() with PROT_NONE.
 bool ElfReader::ReserveAddressSpace() {
-  load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_);
+  Elf32_Addr min_vaddr;
+  load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr);
   if (load_size_ == 0) {
     DL_ERR("\"%s\" has no loadable segments", name_);
     return false;
   }
 
+  uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
   int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
-  void* start = mmap(NULL, load_size_, PROT_NONE, mmap_flags, -1, 0);
+  void* start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0);
   if (start == MAP_FAILED) {
     DL_ERR("couldn't reserve %d bytes of address space for \"%s\"", load_size_, name_);
     return false;
   }
 
   load_start_ = start;
-  load_bias_ = 0;
-
-  for (size_t i = 0; i < phdr_num_; ++i) {
-    const Elf32_Phdr* phdr = &phdr_table_[i];
-    if (phdr->p_type == PT_LOAD) {
-      load_bias_ = reinterpret_cast<Elf32_Addr>(start) - PAGE_START(phdr->p_vaddr);
-      break;
-    }
-  }
+  load_bias_ = reinterpret_cast<uint8_t*>(start) - addr;
   return true;
 }
 
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index a31d1d9..992d95e 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -80,7 +80,11 @@
   const Elf32_Phdr* loaded_phdr_;
 };
 
-Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr, size_t phnum);
+size_t
+phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
+                         size_t phdr_count,
+                         Elf32_Addr* min_vaddr = NULL,
+                         Elf32_Addr* max_vaddr = NULL);
 
 int
 phdr_table_protect_segments(const Elf32_Phdr* phdr_table,