From 6abffe3959f93a129c2ccda185379b02cc688ee3 Mon Sep 17 00:00:00 2001
From: Pawit Pornkitprasan
Date: Fri, 23 Nov 2012 12:27:25 +0700
Subject: 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
---
linker/linker.cpp | 50 ++++++++++++++++++++++++++++++++------------------
linker/linker_phdr.c | 8 +++++++-
linker/linker_phdr.h | 1 +
3 files changed, 40 insertions(+), 19 deletions(-)
(limited to 'linker')
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 @@ static int open_library(const char *name)
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)
+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)
{
- struct prelink_info_t {
- long mmap_addr;
- char tag[4]; // "PRE ".
- };
-
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 @@ static soinfo* load_library(const char* name)
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 @@ static soinfo* load_library(const char* name)
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 @@ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
* 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 @@ Elf32_Addr phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
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 @@ phdr_table_get_load_size(const Elf32_Phdr* phdr_table,
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);
--
cgit v1.1