linker: restore prelink support

Prelink support is required to load old vendor binary blobs
on many devices properly

This commit partially reverts 4688279db5dcc4004941e7f133c4a1c3617d842c

Change-Id: Ibc835095579c0bbd18aff61f37bd420de353e94d
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 46d1335..2362099 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -641,33 +641,35 @@
     return -1;
 }
 
-// Returns 'true' if the library is prelinked or on failure so we error out
-// either way. We no longer support prelinking.
-static bool is_prelinked(int fd, const char* name)
-{
-    struct prelink_info_t {
-        long mmap_addr;
-        char tag[4]; // "PRE ".
-    };
+typedef struct {
+    long mmap_addr;
+    char tag[4]; /* 'P', 'R', 'E', ' ' */
+} prelink_info_t;
 
+/* Returns the requested base address if the library is prelinked,
+ * and 0 otherwise.  */
+static unsigned long
+is_prelinked(int fd, const char *name)
+{
     off_t sz = lseek(fd, -sizeof(prelink_info_t), SEEK_END);
     if (sz < 0) {
-        DL_ERR("lseek failed: %s", strerror(errno));
-        return true;
+        DL_ERR("lseek() failed!");
+        return 0;
     }
 
     prelink_info_t info;
     int rc = TEMP_FAILURE_RETRY(read(fd, &info, sizeof(info)));
     if (rc != sizeof(info)) {
-        DL_ERR("could not read prelink_info_t structure for \"%s\":", name, strerror(errno));
-        return true;
+        WARN("Could not read prelink_info_t structure for `%s`\n", name);
+        return 0;
     }
 
-    if (memcmp(info.tag, "PRE ", 4) == 0) {
-        DL_ERR("prelinked libraries no longer supported: %s", name);
-        return true;
+    if (memcmp(info.tag, "PRE ", 4)) {
+        WARN("`%s` is not a prelinked library\n", name);
+        return 0;
     }
-    return false;
+
+    return (unsigned long)info.mmap_addr;
 }
 
 /* verify_elf_header
@@ -781,10 +783,21 @@
         return NULL;
     }
 
-    // We no longer support pre-linked libraries.
-    if (is_prelinked(fd.fd, name)) {
+    unsigned req_base = (unsigned) is_prelinked(fd.fd, name);
+    if (req_base == (unsigned)-1) {
+        DL_ERR("%5d can't read end of library: %s: %s", pid, name,
+               strerror(errno));
         return NULL;
     }
+    if (req_base != 0) {
+        TRACE("[ %5d - Prelinked library '%s' requesting base @ 0x%08x ]\n",
+              pid, name, req_base);
+    } else {
+        TRACE("[ %5d - Non-prelinked library '%s' found. ]\n", pid, name);
+    }
+
+    TRACE("[ %5d - '%s' (%s) wants base=0x%08x sz=0x%08x ]\n", pid, name,
+          (req_base ? "prelinked" : "not pre-linked"), req_base, ext_sz);
 
     // Reserve address space for all loadable segments.
     void* load_start = NULL;
@@ -792,6 +805,7 @@
     Elf32_Addr load_bias = 0;
     ret = phdr_table_reserve_memory(phdr_table,
                                     phdr_count,
+                                    req_base,
                                     &load_start,
                                     &load_size,
                                     &load_bias);
diff --git a/linker/linker_phdr.c b/linker/linker_phdr.c
index 250ca20..36f848b 100644
--- a/linker/linker_phdr.c
+++ b/linker/linker_phdr.c
@@ -218,6 +218,8 @@
  * Input:
  *   phdr_table    -> program header table
  *   phdr_count    -> number of entries in the tables
+ *   required_base -> for prelinked libraries, mandatory load address
+ *                    of the first loadable segment. 0 otherwise.
  * Output:
  *   load_start    -> first page of reserved address space range
  *   load_size     -> size in bytes of reserved address space range
@@ -229,18 +231,22 @@
 int
 phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
                           size_t phdr_count,
+                          Elf32_Addr required_base,
                           void** load_start,
                           Elf32_Addr* load_size,
                           Elf32_Addr* load_bias)
 {
     Elf32_Addr size = phdr_table_get_load_size(phdr_table, phdr_count);
+
     if (size == 0) {
         errno = EINVAL;
         return -1;
     }
 
     int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
-    void* start = mmap(NULL, size, PROT_NONE, mmap_flags, -1, 0);
+    if (required_base != 0)
+        mmap_flags |= MAP_FIXED;
+    void* start = mmap((void*)required_base, size, PROT_NONE, mmap_flags, -1, 0);
     if (start == MAP_FAILED) {
         return -1;
     }
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index a759262..19e281b 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -61,6 +61,7 @@
 int
 phdr_table_reserve_memory(const Elf32_Phdr* phdr_table,
                           size_t phdr_count,
+                          Elf32_Addr required_base,
                           void** load_start,
                           Elf32_Addr* load_size,
                           Elf32_Addr* load_bias);