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_;