Merge tag 'android-7.1.2_r29' into HEAD

Android 7.1.2 Release 29 (NJH47F)
diff --git a/libc/Android.bp b/libc/Android.bp
index 950b848..e14a12e 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -687,6 +687,13 @@
                     "upstream-openbsd/lib/libc/string/strcat.c",
                 ],
             },
+            scorpion: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
         },
         arm64: {
             exclude_srcs: [
@@ -1062,6 +1069,33 @@
                     "bionic/__strcpy_chk.cpp",
                 ],
             },
+            scorpion: {
+                srcs: [
+                    // Use krait memset/strcmp/memmove.
+                    "arch-arm/krait/bionic/memset.S",
+                    "arch-arm/krait/bionic/strcmp.S",
+
+                    // Use cortex-a15 versions of strcat/strcpy/strlen.
+                    "arch-arm/cortex-a15/bionic/memcpy.S",
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/__strcat_chk.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/__strcpy_chk.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+
+                    "arch-arm/denver/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
 
         },
         arm64: {
diff --git a/libc/Android.mk b/libc/Android.mk
index 1ca84c1..9568b4b 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -621,6 +621,10 @@
   use_clang := false
 endif
 
+ifeq ($(TARGET_NEEDS_GCC_LIBC),true)
+  use_clang := false
+endif
+
 ifeq ($(use_clang),)
   use_clang := true
 endif
@@ -639,6 +643,10 @@
 libc_malloc_src := bionic/jemalloc_wrapper.cpp
 libc_common_c_includes += external/jemalloc/include
 
+ifeq ($(BOARD_USES_LEGACY_MMAP),true)
+  libc_common_cflags += -DLEGACY_MMAP
+endif
+
 # Define some common conlyflags
 libc_common_conlyflags := \
     -std=gnu99
@@ -1449,6 +1457,9 @@
 LOCAL_SANITIZE := never
 LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
 
+# Allow devices to provide additional symbols
+LOCAL_WHOLE_STATIC_LIBRARIES += $(BOARD_PROVIDES_ADDITIONAL_BIONIC_STATIC_LIBS)
+
 include $(BUILD_SHARED_LIBRARY)
 
 # ========================================================
diff --git a/libc/arch-arm/arm.mk b/libc/arch-arm/arm.mk
index 76f465e..e170376 100644
--- a/libc/arch-arm/arm.mk
+++ b/libc/arch-arm/arm.mk
@@ -41,7 +41,7 @@
 ifneq ($(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT),generic)
 cpu_variant_mk := $(LOCAL_PATH)/arch-arm/$(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT)/$(TARGET_$(my_2nd_arch_prefix)CPU_VARIANT).mk
 ifeq ($(wildcard $(cpu_variant_mk)),)
-$(error "TARGET_$(my_2nd_arch_prefix)CPU_VARIANT not set or set to an unknown value. Possible values are cortex-a7, cortex-a8, cortex-a9, cortex-a15, krait, denver. Use generic for devices that do not have a CPU similar to any of the supported cpu variants.")
+$(error "TARGET_$(my_2nd_arch_prefix)CPU_VARIANT not set or set to an unknown value. Possible values are cortex-a7, cortex-a8, cortex-a9, cortex-a15, krait, scorpion, denver. Use generic for devices that do not have a CPU similar to any of the supported cpu variants.")
 endif
 include $(cpu_variant_mk)
 libc_common_additional_dependencies += $(cpu_variant_mk)
diff --git a/libc/arch-arm/scorpion/scorpion.mk b/libc/arch-arm/scorpion/scorpion.mk
new file mode 100644
index 0000000..f3c390e
--- /dev/null
+++ b/libc/arch-arm/scorpion/scorpion.mk
@@ -0,0 +1,29 @@
+libc_openbsd_src_files_exclude_arm += \
+    upstream-openbsd/lib/libc/string/memmove.c \
+    upstream-openbsd/lib/libc/string/stpcpy.c \
+    upstream-openbsd/lib/libc/string/strcat.c \
+
+libc_bionic_src_files_exclude_arm += \
+    arch-arm/generic/bionic/memcpy.S \
+    arch-arm/generic/bionic/memset.S \
+    arch-arm/generic/bionic/strcmp.S \
+    arch-arm/generic/bionic/strcpy.S \
+    arch-arm/generic/bionic/strlen.c \
+    bionic/__strcat_chk.cpp \
+    bionic/__strcpy_chk.cpp \
+
+libc_bionic_src_files_arm += \
+    arch-arm/krait/bionic/memset.S \
+    arch-arm/krait/bionic/strcmp.S \
+
+libc_bionic_src_files_arm += \
+    arch-arm/cortex-a15/bionic/memcpy.S \
+    arch-arm/cortex-a15/bionic/stpcpy.S \
+    arch-arm/cortex-a15/bionic/strcat.S \
+    arch-arm/cortex-a15/bionic/__strcat_chk.S \
+    arch-arm/cortex-a15/bionic/strcpy.S \
+    arch-arm/cortex-a15/bionic/__strcpy_chk.S \
+    arch-arm/cortex-a15/bionic/strlen.S \
+
+libc_bionic_src_files_arm += \
+    arch-arm/denver/bionic/memmove.S
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 4f1226d..71c0b5f 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -243,6 +243,7 @@
     "LD_ORIGIN_PATH",
     "LD_PRELOAD",
     "LD_PROFILE",
+    "LD_SHIM_LIBS",
     "LD_SHOW_AUXV",
     "LD_USE_LOAD_BIAS",
     "LOCALDOMAIN",
diff --git a/libc/bionic/mmap.cpp b/libc/bionic/mmap.cpp
index 57a8cdf..9919f40 100644
--- a/libc/bionic/mmap.cpp
+++ b/libc/bionic/mmap.cpp
@@ -38,6 +38,11 @@
 extern "C" void*  __mmap2(void*, size_t, int, int, int, size_t);
 
 #define MMAP2_SHIFT 12 // 2**12 == 4096
+#ifdef LEGACY_MMAP
+#define TO_64(a) ((a) & 0x00000000ffffffff)
+#else
+#define TO_64(a) (a)
+#endif
 
 static bool kernel_has_MADV_MERGEABLE = true;
 
@@ -73,5 +78,5 @@
 }
 
 void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) {
-  return mmap64(addr, size, prot, flags, fd, static_cast<off64_t>(offset));
+  return mmap64(addr, size, prot, flags, fd, TO_64(static_cast<off64_t>(offset)));
 }
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index fd6c004..bb483b6 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -108,6 +108,8 @@
 #include <stdarg.h>
 #include "nsswitch.h"
 
+#include "hosts_cache.h"
+
 #ifdef ANDROID_CHANGES
 #include <sys/system_properties.h>
 #endif /* ANDROID_CHANGES */
@@ -1805,10 +1807,14 @@
 			return -1;
 		}
 	}
-	if (mark != MARK_UNSET && setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0)
+	if (mark != MARK_UNSET && setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+		close(sock);
 		return 0;
-	if (uid > 0 && uid != NET_CONTEXT_INVALID_UID && fchown(sock, uid, (gid_t)-1) < 0)
+	}
+	if (uid > 0 && uid != NET_CONTEXT_INVALID_UID && fchown(sock, uid, (gid_t)-1) < 0) {
+		close(sock);
 		return 0;
+	}
 	do {
 		ret = __connect(sock, addr, len);
 	} while (ret == -1 && errno == EINTR);
@@ -2117,6 +2123,14 @@
 	name = va_arg(ap, char *);
 	pai = va_arg(ap, struct addrinfo *);
 
+	memset(&sentinel, 0, sizeof(sentinel));
+	cur = &sentinel;
+	int gai_error = hc_getaddrinfo(name, NULL, pai, &cur);
+	if (gai_error != EAI_SYSTEM) {
+		*((struct addrinfo **)rv) = sentinel.ai_next;
+		return (gai_error == 0 ? NS_SUCCESS : NS_NOTFOUND);
+	}
+
 //	fprintf(stderr, "_files_getaddrinfo() name = '%s'\n", name);
 	memset(&sentinel, 0, sizeof(sentinel));
 	cur = &sentinel;
diff --git a/libc/dns/net/hosts_cache.c b/libc/dns/net/hosts_cache.c
new file mode 100644
index 0000000..deafb78
--- /dev/null
+++ b/libc/dns/net/hosts_cache.c
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <strings.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utime.h>
+#include <pthread.h>
+
+#include <netinet/in6.h>
+#include <arpa/inet.h>
+
+#include "hostent.h"
+#include "resolv_private.h"
+
+#define MAX_ADDRLEN	(INET6_ADDRSTRLEN - (1 + 5))
+#define MAX_HOSTLEN	MAXHOSTNAMELEN
+
+#define ESTIMATED_LINELEN	32
+#define HCFILE_ALLOC_SIZE	256
+
+/* From sethostent.c */
+#define ALIGNBYTES	(sizeof(uintptr_t) - 1)
+#define ALIGN(p)	(((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+
+/*
+ * Host cache entry for hcfile.c_data.
+ * Offsets are into hcfile.h_data.
+ * Strings are *not* terminated by NULL, but by whitespace (isspace) or '#'.
+ * Use hstr* functions with these.
+ */
+struct hcent
+{
+	uint32_t	addr;
+	uint32_t	name;
+};
+
+/*
+ * Overall host cache file state.
+ */
+struct hcfile
+{
+	int		h_fd;
+	struct stat	h_st;
+	char		*h_data;
+
+	uint32_t	c_alloc;
+	uint32_t	c_len;
+	struct hcent	*c_data;
+};
+static struct hcfile hcfile;
+static pthread_mutex_t hclock = PTHREAD_MUTEX_INITIALIZER;
+
+static size_t hstrlen(const char *s)
+{
+	const char *p = s;
+	while (*p && *p != '#' && !isspace(*p))
+		++p;
+	return p - s;
+}
+
+static int hstrcmp(const char *a, const char *b)
+{
+	size_t alen = hstrlen(a);
+	size_t blen = hstrlen(b);
+	int res = strncmp(a, b, MIN(alen, blen));
+	if (res == 0)
+		res = alen - blen;
+	return res;
+}
+
+static char *hstrcpy(char *dest, const char *src)
+{
+	size_t len = hstrlen(src);
+	memcpy(dest, src, len);
+	dest[len] = '\0';
+	return dest;
+}
+
+static char *hstrdup(const char *s)
+{
+	size_t len = hstrlen(s);
+	char *dest = (char *)malloc(len + 1);
+	if (!dest)
+		return NULL;
+	memcpy(dest, s, len);
+	dest[len] = '\0';
+	return dest;
+}
+
+static int cmp_hcent_name(const void *a, const void *b)
+{
+	struct hcent *ea = (struct hcent *)a;
+	const char *na = hcfile.h_data + ea->name;
+	struct hcent *eb = (struct hcent *)b;
+	const char *nb = hcfile.h_data + eb->name;
+
+	return hstrcmp(na, nb);
+}
+
+static struct hcent *_hcfindname(const char *name)
+{
+	size_t first, last, mid;
+	struct hcent *cur = NULL;
+	int cmp;
+
+	if (hcfile.c_len == 0)
+		return NULL;
+
+	first = 0;
+	last = hcfile.c_len - 1;
+	mid = (first + last) / 2;
+	while (first <= last) {
+		cur = hcfile.c_data + mid;
+		cmp = hstrcmp(hcfile.h_data + cur->name, name);
+		if (cmp == 0)
+			goto found;
+		if (cmp < 0)
+			first = mid + 1;
+		else {
+			if (mid > 0)
+				last = mid - 1;
+			else
+				return NULL;
+		}
+		mid = (first + last) / 2;
+	}
+	return NULL;
+
+found:
+	while (cur > hcfile.c_data) {
+		struct hcent *prev = cur - 1;
+		cmp = cmp_hcent_name(cur, prev);
+		if (cmp)
+			break;
+		cur = prev;
+	}
+
+	return cur;
+}
+
+/*
+ * Find next name on line, if any.
+ *
+ * Assumes that line is terminated by LF.
+ */
+static const char *_hcnextname(const char *name)
+{
+	while (!isspace(*name)) {
+		if (*name == '#')
+			return NULL;
+		++name;
+	}
+	while (isspace(*name)) {
+		if (*name == '\n')
+			return NULL;
+		++name;
+	}
+	if (*name == '#')
+		return NULL;
+	return name;
+}
+
+static int _hcfilemmap(void)
+{
+	struct stat st;
+	int h_fd;
+	char *h_addr;
+	const char *p, *pend;
+	uint32_t c_alloc;
+
+	h_fd = open(_PATH_HOSTS, O_RDONLY);
+	if (h_fd < 0)
+		return -1;
+	if (flock(h_fd, LOCK_EX) != 0) {
+		close(h_fd);
+		return -1;
+	}
+
+	if (hcfile.h_data) {
+		memset(&st, 0, sizeof(st));
+		if (fstat(h_fd, &st) == 0) {
+			if (st.st_size == hcfile.h_st.st_size &&
+			    st.st_mtime == hcfile.h_st.st_mtime) {
+				flock(h_fd, LOCK_UN);
+				close(h_fd);
+				return 0;
+			}
+		}
+		free(hcfile.c_data);
+		munmap(hcfile.h_data, hcfile.h_st.st_size);
+		close(hcfile.h_fd);
+		memset(&hcfile, 0, sizeof(struct hcfile));
+	}
+
+	if (fstat(h_fd, &st) != 0) {
+		flock(h_fd, LOCK_UN);
+		close(h_fd);
+		return -1;
+	}
+	h_addr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, h_fd, 0);
+	if (h_addr == MAP_FAILED) {
+		flock(h_fd, LOCK_UN);
+		close(h_fd);
+		return -1;
+	}
+
+	hcfile.h_fd = h_fd;
+	hcfile.h_st = st;
+	hcfile.h_data = h_addr;
+
+	c_alloc = 0;
+	/*
+	 * Do an initial allocation if the file is "large".  Estimate
+	 * 32 bytes per line and define "large" as more than half of
+	 * the alloc growth size (256 entries).
+	 */
+	if (st.st_size >= ESTIMATED_LINELEN * HCFILE_ALLOC_SIZE / 2) {
+		c_alloc = st.st_size / ESTIMATED_LINELEN;
+		hcfile.c_data = malloc(c_alloc * sizeof(struct hcent));
+		if (!hcfile.c_data) {
+			goto oom;
+		}
+	}
+
+	p = (const char *)h_addr;
+	pend = p + st.st_size;
+	while (p < pend) {
+		const char *eol, *addr, *name;
+		size_t len;
+		addr = p;
+		eol = memchr(p, '\n', pend - p);
+		if (!eol)
+			break;
+		p = eol + 1;
+		if (*addr == '#' || *addr == '\n')
+			continue;
+		len = hstrlen(addr);
+		if (len > MAX_ADDRLEN)
+			continue;
+		name = addr + len;
+		while (name < eol && isspace(*name))
+			++name;
+		while (name < eol) {
+			len = hstrlen(name);
+			if (len == 0)
+				break;
+			if (len < MAX_HOSTLEN) {
+				struct hcent *ent;
+				if (c_alloc <= hcfile.c_len) {
+					struct hcent *c_data;
+					c_alloc += HCFILE_ALLOC_SIZE;
+					c_data = realloc(hcfile.c_data, c_alloc * sizeof(struct hcent));
+					if (!c_data) {
+						goto oom;
+					}
+					hcfile.c_data = c_data;
+				}
+				ent = hcfile.c_data + hcfile.c_len;
+				ent->addr = addr - h_addr;
+				ent->name = name - h_addr;
+				++hcfile.c_len;
+			}
+			name += len;
+			while (name < eol && isspace(*name))
+				++name;
+		}
+	}
+
+	qsort(hcfile.c_data, hcfile.c_len,
+	    sizeof(struct hcent), cmp_hcent_name);
+
+	flock(h_fd, LOCK_UN);
+
+	return 0;
+
+oom:
+	free(hcfile.c_data);
+	munmap(hcfile.h_data, hcfile.h_st.st_size);
+	flock(hcfile.h_fd, LOCK_UN);
+	close(hcfile.h_fd);
+	memset(&hcfile, 0, sizeof(struct hcfile));
+	return -1;
+}
+
+/*
+ * Caching version of getaddrinfo.
+ *
+ * If we find the requested host name in the cache, use getaddrinfo to
+ * populate the result for each address we find.
+ *
+ * Note glibc and bionic differ in the handling of ai_canonname.  POSIX
+ * says that ai_canonname is only populated in the first result entry.
+ * glibc does this.  bionic populates ai_canonname in all result entries.
+ * We choose the POSIX/glibc way here.
+ */
+int hc_getaddrinfo(const char *host, const char *service,
+		   const struct addrinfo *hints,
+		   struct addrinfo **result)
+{
+	int ret = 0;
+	struct hcent *ent, *cur;
+	struct addrinfo *ai;
+	struct addrinfo rhints;
+	struct addrinfo *last;
+	int canonname = 0;
+	int cmp;
+
+	if (getenv("ANDROID_HOSTS_CACHE_DISABLE") != NULL)
+		return EAI_SYSTEM;
+
+	/* Avoid needless work and recursion */
+	if (hints && (hints->ai_flags & AI_NUMERICHOST))
+		return EAI_SYSTEM;
+	if (!host)
+		return EAI_SYSTEM;
+
+	pthread_mutex_lock(&hclock);
+
+	if (_hcfilemmap() != 0) {
+		ret = EAI_SYSTEM;
+		goto out;
+	}
+	ent = _hcfindname(host);
+	if (!ent) {
+		ret = EAI_NONAME;
+		goto out;
+	}
+
+	if (hints) {
+		canonname = (hints->ai_flags & AI_CANONNAME);
+		memcpy(&rhints, hints, sizeof(rhints));
+		rhints.ai_flags &= ~AI_CANONNAME;
+	}
+	else {
+		memset(&rhints, 0, sizeof(rhints));
+	}
+	rhints.ai_flags |= AI_NUMERICHOST;
+
+	last = NULL;
+	cur = ent;
+	do {
+		char addrstr[MAX_ADDRLEN];
+		struct addrinfo *res;
+
+		hstrcpy(addrstr, hcfile.h_data + cur->addr);
+
+		if (getaddrinfo(addrstr, service, &rhints, &res) == 0) {
+			if (!last)
+				(*result)->ai_next = res;
+			else
+				last->ai_next = res;
+			last = res;
+			while (last->ai_next)
+				last = last->ai_next;
+		}
+
+		if(cur + 1 >= hcfile.c_data + hcfile.c_len)
+			break;
+		cmp = cmp_hcent_name(cur, cur + 1);
+		cur = cur + 1;
+	}
+	while (!cmp);
+
+	if (last == NULL) {
+		/* This check is equivalent to (*result)->ai_next == NULL */
+		ret = EAI_NODATA;
+		goto out;
+	}
+
+	if (canonname) {
+		ai = (*result)->ai_next;
+		free(ai->ai_canonname);
+		ai->ai_canonname = hstrdup(hcfile.h_data + ent->name);
+	}
+
+out:
+	pthread_mutex_unlock(&hclock);
+	return ret;
+}
+
+/*
+ * Caching version of gethtbyname.
+ *
+ * Note glibc and bionic differ in the handling of aliases.  glibc returns
+ * all aliases for all entries, regardless of whether they match h_addrtype.
+ * bionic returns only the aliases for the first hosts entry.  We return all
+ * aliases for all IPv4 entries.
+ *
+ * Additionally, if an alias is IPv6 and the primary name for an alias also
+ * has an IPv4 entry, glibc will return the IPv4 address(es), but bionic
+ * will not.  Neither do we.
+ */
+int hc_gethtbyname(const char *host, int af, struct getnamaddr *info)
+{
+	int ret = NETDB_SUCCESS;
+	struct hcent *ent, *cur;
+	int cmp;
+	size_t addrlen;
+	unsigned int naliases = 0;
+	char *aliases[MAXALIASES];
+	unsigned int naddrs = 0;
+	char *addr_ptrs[MAXADDRS];
+	unsigned int n;
+
+	if (getenv("ANDROID_HOSTS_CACHE_DISABLE") != NULL)
+		return NETDB_INTERNAL;
+
+	switch (af) {
+	case AF_INET:  addrlen = NS_INADDRSZ;  break;
+	case AF_INET6: addrlen = NS_IN6ADDRSZ; break;
+	default:
+		return NETDB_INTERNAL;
+	}
+
+	pthread_mutex_lock(&hclock);
+
+	if (_hcfilemmap() != 0) {
+		ret = NETDB_INTERNAL;
+		goto out;
+	}
+
+	ent = _hcfindname(host);
+	if (!ent) {
+		ret = HOST_NOT_FOUND;
+		goto out;
+	}
+
+	cur = ent;
+	do {
+		char addr[16];
+		char addrstr[MAX_ADDRLEN];
+		char namestr[MAX_HOSTLEN];
+		const char *name;
+
+		hstrcpy(addrstr, hcfile.h_data + cur->addr);
+		if (inet_pton(af, addrstr, &addr) == 1) {
+			char *aligned;
+			/* First match is considered the official hostname */
+			if (naddrs == 0) {
+				hstrcpy(namestr, hcfile.h_data + cur->name);
+				HENT_SCOPY(info->hp->h_name, namestr, info->buf, info->buflen);
+			}
+			for (name = hcfile.h_data + cur->name; name; name = _hcnextname(name)) {
+				if (!hstrcmp(name, host))
+					continue;
+				hstrcpy(namestr, name);
+				HENT_SCOPY(aliases[naliases], namestr, info->buf, info->buflen);
+				++naliases;
+				if (naliases >= MAXALIASES)
+					goto nospc;
+			}
+			aligned = (char *)ALIGN(info->buf);
+			if (info->buf != aligned) {
+				if ((ptrdiff_t)info->buflen < (aligned - info->buf))
+					goto nospc;
+				info->buflen -= (aligned - info->buf);
+				info->buf = aligned;
+			}
+			HENT_COPY(addr_ptrs[naddrs], addr, addrlen, info->buf, info->buflen);
+			++naddrs;
+			if (naddrs >= MAXADDRS)
+				goto nospc;
+		}
+
+		if(cur + 1 >= hcfile.c_data + hcfile.c_len)
+			break;
+		cmp = cmp_hcent_name(cur, cur + 1);
+		cur = cur + 1;
+	}
+	while (!cmp);
+
+	if (naddrs == 0) {
+		ret = HOST_NOT_FOUND;
+		goto out;
+	}
+
+	addr_ptrs[naddrs++] = NULL;
+	aliases[naliases++] = NULL;
+
+	/* hp->h_name already populated */
+	HENT_ARRAY(info->hp->h_aliases, naliases, info->buf, info->buflen);
+	for (n = 0; n < naliases; ++n) {
+		info->hp->h_aliases[n] = aliases[n];
+	}
+	info->hp->h_addrtype = af;
+	info->hp->h_length = addrlen;
+	HENT_ARRAY(info->hp->h_addr_list, naddrs, info->buf, info->buflen);
+	for (n = 0; n < naddrs; ++n) {
+		info->hp->h_addr_list[n] = addr_ptrs[n];
+	}
+
+out:
+	pthread_mutex_unlock(&hclock);
+	*info->he = ret;
+	return ret;
+
+nospc:
+	ret = NETDB_INTERNAL;
+	goto out;
+}
diff --git a/libc/dns/net/hosts_cache.h b/libc/dns/net/hosts_cache.h
new file mode 100644
index 0000000..fa5488f
--- /dev/null
+++ b/libc/dns/net/hosts_cache.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+struct getnamaddr;
+
+int hc_getaddrinfo(const char *host, const char *service,
+		   const struct addrinfo *hints,
+		   struct addrinfo **result);
+
+int hc_gethtbyname(const char *host, int af, struct getnamaddr *info);
diff --git a/libc/dns/net/sethostent.c b/libc/dns/net/sethostent.c
index 916421e..be29621 100644
--- a/libc/dns/net/sethostent.c
+++ b/libc/dns/net/sethostent.c
@@ -55,6 +55,8 @@
 #include "hostent.h"
 #include "resolv_private.h"
 
+#include "hosts_cache.h"
+
 #define ALIGNBYTES (sizeof(uintptr_t) - 1)
 #define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
 
@@ -99,6 +101,11 @@
 	/* NOSTRICT skip string len */(void)va_arg(ap, int);
 	af = va_arg(ap, int);
 
+	int rc = hc_gethtbyname(name, af, info);
+	if (rc != NETDB_INTERNAL) {
+		return (rc == NETDB_SUCCESS ? NS_SUCCESS : NS_NOTFOUND);
+	}
+
 #if 0
 	{
 		res_state res = __res_get_state();
diff --git a/libc/include/paths.h b/libc/include/paths.h
index 82c2804..7700cdd 100644
--- a/libc/include/paths.h
+++ b/libc/include/paths.h
@@ -33,6 +33,7 @@
 #define	_PATHS_H_
 
 #define	_PATH_BSHELL	"/system/bin/sh"
+#define	_PATH_BSHELL2	"/sbin/sh"
 #define	_PATH_CONSOLE	"/dev/console"
 #define	_PATH_DEFPATH	"/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin"
 #define	_PATH_DEV	"/dev/"
diff --git a/libc/upstream-netbsd/lib/libc/gen/popen.c b/libc/upstream-netbsd/lib/libc/gen/popen.c
index 593e346..b6ce47c 100644
--- a/libc/upstream-netbsd/lib/libc/gen/popen.c
+++ b/libc/upstream-netbsd/lib/libc/gen/popen.c
@@ -152,6 +152,8 @@
 		}
 
 		execl(_PATH_BSHELL, "sh", "-c", command, NULL);
+		if (errno == ENOENT)
+			execl(_PATH_BSHELL2, "sh", "-c", command, NULL);
 		_exit(127);
 		/* NOTREACHED */
 	}
diff --git a/libc/upstream-openbsd/lib/libc/gen/fnmatch.c b/libc/upstream-openbsd/lib/libc/gen/fnmatch.c
index 0d0f18f..ac6280e33 100644
--- a/libc/upstream-openbsd/lib/libc/gen/fnmatch.c
+++ b/libc/upstream-openbsd/lib/libc/gen/fnmatch.c
@@ -89,12 +89,72 @@
 #include <string.h>
 #include <ctype.h>
 
+#include <stdint.h>
+
 #include "charclass.h"
 
 #define	RANGE_MATCH	1
 #define	RANGE_NOMATCH	0
 #define	RANGE_ERROR	(-1)
 
+static unsigned int
+utf8_len(const char *s)
+{
+    const unsigned char *b = (const unsigned char *)s;
+    unsigned int len = 1;
+    unsigned char c;
+
+    if ((b[0] & 0xc0) != 0xc0) {
+        return 1;
+    }
+    c = b[0] << 1;
+    while (len < 6 && (c & 0x80)) {
+        if ((b[len] & 0xc0) != 0x80) {
+            return 1;
+        }
+        c <<= 1;
+        ++len;
+    }
+
+    return len;
+}
+
+static void
+utf8_inc(const char **s)
+{
+    *s += utf8_len(*s);
+}
+
+static uint32_t
+utf8_get_inc(const char **s)
+{
+    unsigned int len = utf8_len(*s);
+    const unsigned char *b = (const unsigned char *)(*s);
+    unsigned int n;
+    uint32_t ch;
+
+    *s += len;
+
+    if (len == 1) {
+        return b[0];
+    }
+
+    ch = b[0] & (0xff >> len);
+    for (n = 1; n < len; ++n) {
+        ch <<= 6;
+        ch |= (b[n] & 0x3f);
+    }
+
+    return ch;
+}
+
+static uint32_t
+utf8_get(const char *s)
+{
+    const char *tmp = s;
+    return utf8_get_inc(&tmp);
+}
+
 static int
 classmatch(const char *pattern, char test, int foldcase, const char **ep)
 {
@@ -149,7 +209,7 @@
     const int escape = !(flags & FNM_NOESCAPE);
     const int slash = !!(flags & FNM_PATHNAME);
     int result = FNM_NOMATCH;
-    const char *startch;
+    uint32_t startch, endch, compch;
     int negate;
 
     if (**pattern == '[')
@@ -170,7 +230,7 @@
             if (**pattern == ']') {
                 ++*pattern;
                 /* XXX: Fix for MBCS character width */
-                ++*string;
+                utf8_inc(string);
                 return (result ^ negate);
             }
 
@@ -200,10 +260,13 @@
              * "x-]" is not allowed unless escaped ("x-\]")
              * XXX: Fix for locale/MBCS character width
              */
-            if (((*pattern)[1] == '-') && ((*pattern)[2] != ']'))
+            startch = utf8_get_inc(pattern);
+            compch = utf8_get(*string);
+            if (((*pattern)[0] == '-') && ((*pattern)[1] != ']'))
             {
-                startch = *pattern;
-                *pattern += (escape && ((*pattern)[2] == '\\')) ? 3 : 2;
+                *pattern += 1;
+                if (escape && **pattern == '\\')
+                    *pattern += 1;
 
                 /* NOT a properly balanced [expr] pattern, EOS terminated 
                  * or ranges containing a slash in FNM_PATHNAME mode pattern
@@ -212,32 +275,35 @@
                 if (!**pattern || (slash && (**pattern == '/')))
                     break;
 
+                endch = utf8_get_inc(pattern);
+
+                /* Refuse to attempt collation for non-ASCII chars */
+                if (startch >= 0x80 || endch >= 0x80)
+                    continue;
+
                 /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
-                if ((**string >= *startch) && (**string <= **pattern))
+                if ((compch >= startch) && (compch <= endch))
                     result = 0;
-                else if (nocase && (isupper((unsigned char)**string) ||
-			    isupper((unsigned char)*startch) ||
-                            isupper((unsigned char)**pattern))
-                            && (tolower((unsigned char)**string) >=
-			        tolower((unsigned char)*startch)) 
-                            && (tolower((unsigned char)**string) <=
-				tolower((unsigned char)**pattern)))
+                else if (nocase && (isupper(compch) ||
+			    isupper(startch) ||
+                            isupper(endch))
+                            && (tolower(compch) >=
+			        tolower(startch))
+                            && (tolower(compch) <=
+				tolower(endch)))
                     result = 0;
 
-                ++*pattern;
                 continue;
             }
 
             /* XXX: handle locale/MBCS comparison, advance by MBCS char width */
-            if ((**string == **pattern))
+            if (compch == startch)
                 result = 0;
-            else if (nocase && (isupper((unsigned char)**string) ||
-			    isupper((unsigned char)**pattern))
-                            && (tolower((unsigned char)**string) ==
-				tolower((unsigned char)**pattern)))
+            else if (nocase && (isupper(compch) ||
+			    isupper(startch))
+                            && (tolower(compch) ==
+				tolower(startch)))
                 result = 0;
-
-            ++*pattern;
         }
 
         /* NOT a properly balanced [expr] pattern; Rewind
@@ -258,7 +324,7 @@
     }
 
     /* XXX: handle locale/MBCS comparison, advance by the MBCS char width */
-    if (**string == **pattern)
+    if (utf8_get(*string) == utf8_get(*pattern))
         result = 0;
     else if (nocase && (isupper((unsigned char)**string) ||
 		    isupper((unsigned char)**pattern))
@@ -272,8 +338,8 @@
         return result;
 
 fnmatch_ch_success:
-    ++*pattern;
-    ++*string;
+    utf8_inc(pattern);
+    utf8_inc(string);
     return result;
 }
 
diff --git a/linker/Android.mk b/linker/Android.mk
index 4a4ca5c..65dd7dd 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -47,16 +47,32 @@
 LOCAL_CONLYFLAGS += \
     -std=gnu99 \
 
+ifneq ($(TARGET_NEEDS_PRELINK_SUPPORT),true)
 LOCAL_CPPFLAGS += \
-    -Wold-style-cast \
+    -Wold-style-cast
+else
+  LOCAL_CFLAGS += -DENABLE_PRELINK_SUPPORT
+endif
 
 ifeq ($(TARGET_IS_64_BIT),true)
 LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
 endif
 
+ifeq ($(TARGET_NEEDS_PLATFORM_TEXT_RELOCATIONS),true)
+ifeq ($(user_variant),user)
+$(error Do not enable text relocations on user builds)
+else
+LOCAL_CPPFLAGS += -DTARGET_NEEDS_PLATFORM_TEXT_RELOCATIONS
+endif
+endif
+
 # We need to access Bionic private headers in the linker.
 LOCAL_CFLAGS += -I$(LOCAL_PATH)/../libc/
 
+ifeq ($(TARGET_NEEDS_NON_PIE_SUPPORT),true)
+  LOCAL_CFLAGS += -DENABLE_NON_PIE_SUPPORT
+endif
+
 # we don't want crtbegin.o (because we have begin.o), so unset it
 # just for this module
 LOCAL_NO_CRT := true
diff --git a/linker/linker.cpp b/linker/linker.cpp
index a043b85..322cf5d 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1230,6 +1230,67 @@
 typedef linked_list_t<const char> StringLinkedList;
 typedef std::vector<LoadTask*> LoadTaskList;
 
+static soinfo* find_library(android_namespace_t* ns,
+                           const char* name, int rtld_flags,
+                           const android_dlextinfo* extinfo,
+                           soinfo* needed_by);
+
+// g_ld_all_shim_libs maintains the references to memory as it used
+// in the soinfo structures and in the g_active_shim_libs list.
+
+typedef std::pair<std::string, std::string> ShimDescriptor;
+static std::vector<ShimDescriptor> g_ld_all_shim_libs;
+
+// g_active_shim_libs are all shim libs that are still eligible
+// to be loaded.  We must remove a shim lib from the list before
+// we load the library to avoid recursive loops (load shim libA
+// for libB where libA also links against libB).
+
+static linked_list_t<const ShimDescriptor> g_active_shim_libs;
+
+static void reset_g_active_shim_libs(void) {
+  g_active_shim_libs.clear();
+  for (const auto& pair : g_ld_all_shim_libs) {
+    g_active_shim_libs.push_back(&pair);
+  }
+}
+
+static void parse_LD_SHIM_LIBS(const char* path) {
+  g_ld_all_shim_libs.clear();
+  if (path != nullptr) {
+    // We have historically supported ':' as well as ' ' in LD_SHIM_LIBS.
+    for (const auto& pair : android::base::Split(path, " :")) {
+      size_t pos = pair.find('|');
+      if (pos > 0 && pos < pair.length() - 1) {
+        auto desc = std::pair<std::string, std::string>(pair.substr(0, pos), pair.substr(pos + 1));
+        g_ld_all_shim_libs.push_back(desc);
+      }
+    }
+  }
+  reset_g_active_shim_libs();
+}
+
+template<typename F>
+static void for_each_matching_shim(const char *const path, F action) {
+  if (path == nullptr) return;
+  INFO("Finding shim libs for \"%s\"\n", path);
+  std::vector<const ShimDescriptor *> matched;
+
+  g_active_shim_libs.for_each([&](const ShimDescriptor *a_pair) {
+    if (a_pair->first == path) {
+      matched.push_back(a_pair);
+    }
+  });
+
+  g_active_shim_libs.remove_if([&](const ShimDescriptor *a_pair) {
+    return a_pair->first == path;
+  });
+
+  for (const auto& one_pair : matched) {
+    INFO("Injecting shim lib \"%s\" as needed for %s", one_pair->second.c_str(), path);
+    action(one_pair->second.c_str());
+  }
+}
 
 // This function walks down the tree of soinfo dependencies
 // in breadth-first order and
@@ -1668,6 +1729,7 @@
 
 template<typename F>
 static void for_each_dt_needed(const soinfo* si, F action) {
+  for_each_matching_shim(si->get_realpath(), action);
   for (const ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
     if (d->d_tag == DT_NEEDED) {
       action(fix_dt_needed(si->get_string(d->d_un.d_val), si->get_realpath()));
@@ -1677,6 +1739,7 @@
 
 template<typename F>
 static void for_each_dt_needed(const ElfReader& elf_reader, F action) {
+  for_each_matching_shim(elf_reader.name(), action);
   for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) {
     if (d->d_tag == DT_NEEDED) {
       action(fix_dt_needed(elf_reader.get_string(d->d_un.d_val), elf_reader.name()));
@@ -2400,6 +2463,7 @@
   }
 
   ProtectedDataGuard guard;
+  reset_g_active_shim_libs();
   soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
   if (si != nullptr) {
     si->call_constructors();
@@ -2766,10 +2830,10 @@
 
     const ElfW(Sym)* s = nullptr;
     soinfo* lsi = nullptr;
+    const version_info* vi = nullptr;
 
     if (sym != 0) {
       sym_name = get_string(symtab_[sym].st_name);
-      const version_info* vi = nullptr;
 
       if (!lookup_version_info(version_tracker, sym, sym_name, &vi)) {
         return false;
@@ -3071,6 +3135,7 @@
         *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr - rel->r_offset;
         break;
       case R_ARM_COPY:
+#ifndef ENABLE_NON_PIE_SUPPORT
         /*
          * ET_EXEC is not supported so this should not happen.
          *
@@ -3082,6 +3147,50 @@
          */
         DL_ERR("%s R_ARM_COPY relocations are not supported", get_realpath());
         return false;
+#else
+        if ((flags_ & FLAG_EXE) == 0) {
+            /*
+             * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044d/IHI0044D_aaelf.pdf
+             *
+             * Section 4.6.1.10 "Dynamic relocations"
+             * R_ARM_COPY may only appear in executable objects where e_type is
+             * set to ET_EXEC.
+             *
+             * TODO: FLAG_EXE is set for both ET_DYN and ET_EXEC executables.
+             * We should explicitly disallow ET_DYN executables from having
+             * R_ARM_COPY relocations.
+             */
+            DL_ERR("%s R_ARM_COPY relocations only supported for ET_EXEC", get_realpath());
+            return false;
+        }
+        count_relocation(kRelocCopy);
+        MARK(rel->r_offset);
+        TRACE_TYPE(RELO, "RELO %08x <- %d @ %08x %s", reloc, s->st_size, sym_addr, sym_name);
+        if (reloc == sym_addr) {
+            const ElfW(Sym)* src = nullptr;
+
+            if (!soinfo_do_lookup(NULL, sym_name, vi, &lsi, global_group, local_group, &src)) {
+                DL_ERR("%s R_ARM_COPY relocation source cannot be resolved", get_realpath());
+                return false;
+            }
+            if (lsi->has_DT_SYMBOLIC) {
+                DL_ERR("%s invalid R_ARM_COPY relocation against DT_SYMBOLIC shared "
+                       "library %s (built with -Bsymbolic?)", get_realpath(), lsi->soname_);
+                return false;
+            }
+            if (s->st_size < src->st_size) {
+                DL_ERR("%s R_ARM_COPY relocation size mismatch (%d < %d)",
+                       get_realpath(), s->st_size, src->st_size);
+                return false;
+            }
+            memcpy(reinterpret_cast<void*>(reloc),
+                   reinterpret_cast<void*>(src->st_value + lsi->load_bias), src->st_size);
+        } else {
+            DL_ERR("%s R_ARM_COPY relocation target cannot be resolved", get_realpath());
+            return false;
+        }
+        break;
+#endif
 #elif defined(__i386__)
       case R_386_32:
         count_relocation(kRelocRelative);
@@ -3953,14 +4062,27 @@
 #if !defined(__LP64__)
   if (has_text_relocations) {
     // Fail if app is targeting sdk version > 22
+#if !defined(__i386__) // ffmpeg says that they require text relocations on x86
+#if defined(TARGET_NEEDS_PLATFORM_TEXT_RELOCATIONS)
+    if (get_application_target_sdk_version() != __ANDROID_API__
+        && get_application_target_sdk_version() > 22) {
+#else
     if (get_application_target_sdk_version() > 22) {
+#endif
       PRINT("%s: has text relocations", get_realpath());
       DL_ERR("%s: has text relocations", get_realpath());
       return false;
     }
+#endif
     // Make segments writable to allow text relocations to work properly. We will later call
     // phdr_table_protect_segments() after all of them are applied.
+#if defined(TARGET_NEEDS_PLATFORM_TEXT_RELOCATIONS)
+    // Silence the warning for targets that need
+    // text relocations since it can't really be helped
+    DEBUG("%s has text relocations. This is wasting memory and prevents "
+#else
     DL_WARN("%s has text relocations. This is wasting memory and prevents "
+#endif
             "security hardening. Please fix.", get_realpath());
     add_dlwarning(get_realpath(), "text relocations");
     if (phdr_table_unprotect_segments(phdr, phnum, load_bias) < 0) {
@@ -4220,6 +4342,7 @@
   // doesn't cost us anything.
   const char* ldpath_env = nullptr;
   const char* ldpreload_env = nullptr;
+  const char* ldshim_libs_env = nullptr;
   if (!getauxval(AT_SECURE)) {
     ldpath_env = getenv("LD_LIBRARY_PATH");
     if (ldpath_env != nullptr) {
@@ -4229,6 +4352,7 @@
     if (ldpreload_env != nullptr) {
       INFO("[ LD_PRELOAD set to \"%s\" ]", ldpreload_env);
     }
+    ldshim_libs_env = getenv("LD_SHIM_LIBS");
   }
 
   struct stat file_stat;
@@ -4278,15 +4402,18 @@
   }
   si->dynamic = nullptr;
 
+#ifndef ENABLE_NON_PIE_SUPPORT
   ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
   if (elf_hdr->e_type != ET_DYN) {
     __libc_fatal("\"%s\": error: only position independent executables (PIE) are supported.",
                  args.argv[0]);
   }
+#endif
 
   // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
   parse_LD_LIBRARY_PATH(ldpath_env);
   parse_LD_PRELOAD(ldpreload_env);
+  parse_LD_SHIM_LIBS(ldshim_libs_env);
 
   somain = si;
 
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 9ed612f..5ef30fc 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -139,7 +139,11 @@
 ElfReader::ElfReader()
     : did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0),
       phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr),
+#ifdef ENABLE_PRELINK_SUPPORT
+      strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), required_base_(0), loaded_phdr_(nullptr),
+#else
       strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr),
+#endif
       mapped_by_caller_(false) {
 }
 
@@ -423,6 +427,38 @@
   return max_vaddr - min_vaddr;
 }
 
+#ifdef ENABLE_PRELINK_SUPPORT
+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 ElfW(Addr) 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!");
+        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`\n", name);
+        return 0;
+    }
+
+    if (memcmp(info.tag, "PRE ", 4)) {
+        DL_ERR("`%s` is not a prelinked library\n", name);
+        return 0;
+    }
+
+    return (unsigned long)info.mmap_addr;
+}
+#endif
+
 // Reserve a virtual address range big enough to hold all loadable
 // segments of a program header table. This is done by creating a
 // private anonymous mmap() with PROT_NONE.
@@ -464,7 +500,16 @@
              reserved_size - load_size_, load_size_, name_.c_str());
       return false;
     }
+#ifdef ENABLE_PRELINK_SUPPORT
+    required_base_ = is_prelinked(fd_, name_.c_str());
+#endif
     int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#ifdef ENABLE_PRELINK_SUPPORT
+    if (required_base_ != 0) {
+      mmap_flags |= MAP_FIXED;
+      mmap_hint = (uint8_t*) required_base_;
+    }
+#endif
     start = mmap(mmap_hint, load_size_, PROT_NONE, mmap_flags, -1, 0);
     if (start == MAP_FAILED) {
       DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str());
@@ -482,7 +527,11 @@
   }
 
   load_start_ = start;
+#ifdef ENABLE_PRELINK_SUPPORT
+  load_bias_ = reinterpret_cast<uint8_t*>(start) - reinterpret_cast<uint8_t*>(min_vaddr);
+#else
   load_bias_ = reinterpret_cast<uint8_t*>(start) - addr;
+#endif
   return true;
 }
 
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index d6276ed..94f669b 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -50,6 +50,9 @@
   ElfW(Addr) load_start() const { return reinterpret_cast<ElfW(Addr)>(load_start_); }
   size_t load_size() const { return load_size_; }
   ElfW(Addr) load_bias() const { return load_bias_; }
+#ifdef ENABLE_PRELINK_SUPPORT
+  ElfW(Addr) required_base() { return required_base_; }
+#endif
   const ElfW(Phdr)* loaded_phdr() const { return loaded_phdr_; }
   const ElfW(Dyn)* dynamic() const { return dynamic_; }
   const char* get_string(ElfW(Word) index) const;
@@ -97,6 +100,11 @@
   size_t load_size_;
   // Load bias.
   ElfW(Addr) load_bias_;
+#ifdef ENABLE_PRELINK_SUPPORT
+  // For prelinked libraries, mandatory load address of the first
+  // loadable segment. 0 otherwise.
+  ElfW(Addr) required_base_;
+#endif
 
   // Loaded phdr.
   const ElfW(Phdr)* loaded_phdr_;