Merge "Make bionic-unit-tests default run isolate mode."
diff --git a/ABI-bugs.txt b/ABI-bugs.txt
deleted file mode 100644
index 51da9f0..0000000
--- a/ABI-bugs.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-KNOWN ABI BUGS
---------------
-
- time_t is 32-bit. http://b/5819737
-
- off_t is 32-bit. There is off64_t, but no _FILE_OFFSET_BITS support.
-
- sigset_t is too small on ARM and x86 (but correct on MIPS), so support
- for real-time signals is broken. http://b/5828899
-
- atexit(3) handlers registered by a shared library aren't called on
- dlclose(3); this only affects ARM. http://b/4998315
diff --git a/README.md b/README.md
index a031fbc..2c42b3b 100644
--- a/README.md
+++ b/README.md
@@ -256,3 +256,20 @@
$ genhtml -o covreport coverage.info # or lcov --list coverage.info
The coverage report is now available at `covreport/index.html`.
+
+
+LP32 ABI bugs
+-------------
+
+This probably belongs in the NDK documentation rather than here, but these
+are the known ABI bugs in LP32:
+
+ * `time_t` is 32-bit. <http://b/5819737>
+
+ * `off_t` is 32-bit. There is `off64_t`, but no `_FILE_OFFSET_BITS` support.
+ Many of the `off64_t` functions are missing in older releases, and
+ stdio uses 32-bit offsets, so there's no way to fully implement
+ `_FILE_OFFSET_BITS`.
+
+ * `sigset_t` is too small on ARM and x86 (but correct on MIPS), so support
+ for real-time signals is broken. <http://b/5828899>
diff --git a/libc/arch-arm/include/machine/elf_machdep.h b/libc/arch-arm/include/machine/elf_machdep.h
index 97d8434..542f638 100644
--- a/libc/arch-arm/include/machine/elf_machdep.h
+++ b/libc/arch-arm/include/machine/elf_machdep.h
@@ -102,6 +102,8 @@
/* 112-127 are reserved for private experiments. */
+#define R_ARM_IRELATIVE 160
+
#define R_ARM_RXPC25 249
#define R_ARM_RSBREL32 250
#define R_ARM_THM_RPC22 251
diff --git a/libc/bionic/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp
index b86e2af..7d3c76e 100644
--- a/libc/bionic/debug_stacktrace.cpp
+++ b/libc/bionic/debug_stacktrace.cpp
@@ -44,12 +44,7 @@
#define PAD_PTR "08" PRIxPTR
#endif
-/* depends how the system includes define this */
-#ifdef HAVE_UNWIND_CONTEXT_STRUCT
typedef struct _Unwind_Context __unwind_context;
-#else
-typedef _Unwind_Context __unwind_context;
-#endif
static mapinfo_t* g_map_info = NULL;
static void* g_demangler;
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 15b3fd5..94b7dd2 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -85,8 +85,10 @@
// because things like environment variables with global scope live on it.
// We also can't free the pthread_internal_t itself, since that lives on the main
// thread's stack rather than on the heap.
+ // The main thread has no mmap allocated space for stack or pthread_internal_t.
+ main_thread.mmap_size = 0;
pthread_attr_init(&main_thread.attr);
- main_thread.attr.flags = PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK | PTHREAD_ATTR_FLAG_MAIN_THREAD;
+ main_thread.attr.flags = PTHREAD_ATTR_FLAG_MAIN_THREAD;
main_thread.attr.guard_size = 0; // The main thread has no guard page.
main_thread.attr.stack_size = 0; // User code should never see this; we'll compute it when asked.
// TODO: the main thread's sched_policy and sched_priority need to be queried.
diff --git a/libc/bionic/pthread_create.cpp b/libc/bionic/pthread_create.cpp
index 9b45161..e4abee9 100644
--- a/libc/bionic/pthread_create.cpp
+++ b/libc/bionic/pthread_create.cpp
@@ -52,8 +52,9 @@
// This code is used both by each new pthread and the code that initializes the main thread.
void __init_tls(pthread_internal_t* thread) {
- if (thread->user_allocated_stack()) {
- // We don't know where the user got their stack, so assume the worst and zero the TLS area.
+ if (thread->mmap_size == 0) {
+ // If the TLS area was not allocated by mmap(), it may not have been cleared to zero.
+ // So assume the worst and zero the TLS area.
memset(&thread->tls[0], 0, BIONIC_TLS_SLOTS * sizeof(void*));
}
@@ -106,62 +107,63 @@
return error;
}
-static void* __create_thread_stack(size_t stack_size, size_t guard_size) {
+static void* __create_thread_mapped_space(size_t mmap_size, size_t stack_guard_size) {
// Create a new private anonymous map.
int prot = PROT_READ | PROT_WRITE;
int flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE;
- void* stack = mmap(NULL, stack_size, prot, flags, -1, 0);
- if (stack == MAP_FAILED) {
+ void* space = mmap(NULL, mmap_size, prot, flags, -1, 0);
+ if (space == MAP_FAILED) {
__libc_format_log(ANDROID_LOG_WARN,
"libc",
- "pthread_create failed: couldn't allocate %zd-byte stack: %s",
- stack_size, strerror(errno));
+ "pthread_create failed: couldn't allocate %zu-bytes mapped space: %s",
+ mmap_size, strerror(errno));
return NULL;
}
- // Set the guard region at the end of the stack to PROT_NONE.
- if (mprotect(stack, guard_size, PROT_NONE) == -1) {
+ // Stack is at the lower end of mapped space, stack guard region is at the lower end of stack.
+ // Set the stack guard region to PROT_NONE, so we can detect thread stack overflow.
+ if (mprotect(space, stack_guard_size, PROT_NONE) == -1) {
__libc_format_log(ANDROID_LOG_WARN, "libc",
- "pthread_create failed: couldn't mprotect PROT_NONE %zd-byte stack guard region: %s",
- guard_size, strerror(errno));
- munmap(stack, stack_size);
+ "pthread_create failed: couldn't mprotect PROT_NONE %zu-byte stack guard region: %s",
+ stack_guard_size, strerror(errno));
+ munmap(space, mmap_size);
return NULL;
}
- return stack;
+ return space;
}
static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp, void** child_stack) {
- size_t allocate_stack_size;
+ size_t mmap_size;
uint8_t* stack_top;
if (attr->stack_base == NULL) {
// The caller didn't provide a stack, so allocate one.
// Make sure the stack size and guard size are multiples of PAGE_SIZE.
- allocate_stack_size = BIONIC_ALIGN(attr->stack_size + sizeof(pthread_internal_t), PAGE_SIZE);
+ mmap_size = BIONIC_ALIGN(attr->stack_size + sizeof(pthread_internal_t), PAGE_SIZE);
attr->guard_size = BIONIC_ALIGN(attr->guard_size, PAGE_SIZE);
- attr->stack_base = __create_thread_stack(allocate_stack_size, attr->guard_size);
+ attr->stack_base = __create_thread_mapped_space(mmap_size, attr->guard_size);
if (attr->stack_base == NULL) {
return EAGAIN;
}
- stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + allocate_stack_size;
+ stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + mmap_size;
} else {
- // The caller did provide a stack, so remember we're not supposed to free it.
- attr->flags |= PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK;
- allocate_stack_size = 0;
+ // Remember the mmap size is zero and we don't need to free it.
+ mmap_size = 0;
stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + attr->stack_size;
}
- // Thread stack is used for two sections:
- // pthread_internal_t.
- // regular stack, from top to down.
+ // Mapped space(or user allocated stack) is used for:
+ // thread_internal_t (including tls array)
+ // thread stack (including guard page)
stack_top -= sizeof(pthread_internal_t);
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
+ attr->stack_size = stack_top - reinterpret_cast<uint8_t*>(attr->stack_base);
// No need to check stack_top alignment. The size of pthread_internal_t is 16-bytes aligned,
// and user allocated stack is guaranteed by pthread_attr_setstack.
- thread->allocated_stack_size = allocate_stack_size;
+ thread->mmap_size = mmap_size;
thread->attr = *attr;
__init_tls(thread);
@@ -248,8 +250,8 @@
// be unblocked, but we're about to unmap the memory the mutex is stored in, so this serves as a
// reminder that you can't rewrite this function to use a ScopedPthreadMutexLocker.
pthread_mutex_unlock(&thread->startup_handshake_mutex);
- if (!thread->user_allocated_stack()) {
- munmap(thread->attr.stack_base, thread->allocated_stack_size);
+ if (thread->mmap_size != 0) {
+ munmap(thread->attr.stack_base, thread->mmap_size);
}
__libc_format_log(ANDROID_LOG_WARN, "libc", "pthread_create failed: clone failed: %s", strerror(errno));
return clone_errno;
diff --git a/libc/bionic/pthread_exit.cpp b/libc/bionic/pthread_exit.cpp
index ee76e2b..9603a79 100644
--- a/libc/bionic/pthread_exit.cpp
+++ b/libc/bionic/pthread_exit.cpp
@@ -87,30 +87,23 @@
thread->alternate_signal_stack = NULL;
}
- // Keep track of what we need to know about the stack before we lose the pthread_internal_t.
- void* stack_base = thread->attr.stack_base;
- size_t stack_size = thread->allocated_stack_size;
- bool free_stack = false;
-
+ bool free_mapped_space = false;
pthread_mutex_lock(&g_thread_list_lock);
if ((thread->attr.flags & PTHREAD_ATTR_FLAG_DETACHED) != 0) {
- // The thread is detached, so we can free the pthread_internal_t.
+ // The thread is detached, no one will use pthread_internal_t after pthread_exit.
+ // So we can free mapped space, which includes pthread_internal_t and thread stack.
// First make sure that the kernel does not try to clear the tid field
// because we'll have freed the memory before the thread actually exits.
__set_tid_address(NULL);
// pthread_internal_t is freed below with stack, not here.
_pthread_internal_remove_locked(thread, false);
- if (!thread->user_allocated_stack()) {
- free_stack = true;
- }
+ free_mapped_space = true;
}
pthread_mutex_unlock(&g_thread_list_lock);
- // Detached threads exit with stack teardown, and everything deallocated here.
- // Threads that can be joined exit but leave their stacks for the pthread_join caller to clean up.
- if (free_stack) {
- // We need to munmap the stack we're running on before calling exit.
+ if (free_mapped_space && thread->mmap_size != 0) {
+ // We need to free mapped space for detached threads when they exit.
// That's not something we can do in C.
// We don't want to take a signal after we've unmapped the stack.
@@ -119,8 +112,10 @@
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
- _exit_with_stack_teardown(stack_base, stack_size);
+ _exit_with_stack_teardown(thread->attr.stack_base, thread->mmap_size);
} else {
+ // No need to free mapped space. Either there was no space mapped, or it is left for
+ // the pthread_join caller to clean up.
__exit(0);
}
}
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index 62ec543..80002e9 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -35,11 +35,8 @@
/* Has the thread been detached by a pthread_join or pthread_detach call? */
#define PTHREAD_ATTR_FLAG_DETACHED 0x00000001
-/* Was the thread's stack allocated by the user rather than by us? */
-#define PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK 0x00000002
-
/* Has the thread been joined by another thread? */
-#define PTHREAD_ATTR_FLAG_JOINED 0x00000004
+#define PTHREAD_ATTR_FLAG_JOINED 0x00000002
/* Is this the main thread? */
#define PTHREAD_ATTR_FLAG_MAIN_THREAD 0x80000000
@@ -70,10 +67,6 @@
return (*cached_pid != 0);
}
- bool user_allocated_stack() {
- return (attr.flags & PTHREAD_ATTR_FLAG_USER_ALLOCATED_STACK) != 0;
- }
-
pthread_attr_t attr;
__pthread_cleanup_t* cleanup_stack;
@@ -86,8 +79,7 @@
pthread_mutex_t startup_handshake_mutex;
- /* Store real allocated stack size, including thread stack and pthread_internal_t. */
- int allocated_stack_size;
+ size_t mmap_size;
void* tls[BIONIC_TLS_SLOTS];
diff --git a/libc/bionic/pthread_internals.cpp b/libc/bionic/pthread_internals.cpp
index a0a8df0..14061d1 100644
--- a/libc/bionic/pthread_internals.cpp
+++ b/libc/bionic/pthread_internals.cpp
@@ -51,11 +51,9 @@
g_thread_list = thread->next;
}
- // For threads using user allocated stack (including the main thread), the pthread_internal_t
- // can't be freed since it is on the stack.
- if (free_thread && !thread->user_allocated_stack()) {
- // Use one munmap to free allocated stack size, including thread stack and pthread_internal_t.
- munmap(thread->attr.stack_base, thread->allocated_stack_size);
+ if (free_thread && thread->mmap_size != 0) {
+ // Free mapped space, including thread stack and pthread_internal_t.
+ munmap(thread->attr.stack_base, thread->mmap_size);
}
}
diff --git a/libc/dns/net/getaddrinfo.c b/libc/dns/net/getaddrinfo.c
index f0d522a..c73c085 100644
--- a/libc/dns/net/getaddrinfo.c
+++ b/libc/dns/net/getaddrinfo.c
@@ -324,7 +324,11 @@
{
struct addrinfo *next;
- assert(ai != NULL);
+#if __ANDROID__
+ if (ai == NULL) return;
+#else
+ _DIAGASSERT(ai != NULL);
+#endif
do {
next = ai->ai_next;
diff --git a/libc/include/paths.h b/libc/include/paths.h
index a72162f..1eba536 100644
--- a/libc/include/paths.h
+++ b/libc/include/paths.h
@@ -33,42 +33,18 @@
#define _PATHS_H_
/* Default search path. */
-#define _PATH_DEFPATH "/usr/bin:/bin"
-/* All standard utilities path. */
-#define _PATH_STDPATH \
- "/usr/bin:/bin:/usr/sbin:/sbin"
+#define _PATH_DEFPATH "/system/bin:/system/xbin"
#define _PATH_BSHELL "/system/bin/sh"
#define _PATH_CONSOLE "/dev/console"
-#define _PATH_CSHELL "/bin/csh"
-#define _PATH_DEVDB "/var/run/dev.db"
#define _PATH_DEVNULL "/dev/null"
-#define _PATH_DRUM "/dev/drum"
#define _PATH_KLOG "/proc/kmsg"
-#define _PATH_KMEM "/dev/kmem"
-#define _PATH_LASTLOG "/var/log/lastlog"
-#define _PATH_MAILDIR "/var/mail"
-#define _PATH_MAN "/usr/share/man"
#define _PATH_MEM "/dev/mem"
-#define _PATH_MNTTAB "/etc/fstab"
-#define _PATH_MOUNTED "/etc/mtab"
-#define _PATH_NOLOGIN "/etc/nologin"
-#define _PATH_PRESERVE "/var/lib"
-#define _PATH_RWHODIR "/var/spool/rwho"
-#define _PATH_SENDMAIL "/usr/sbin/sendmail"
-#define _PATH_SHADOW "/etc/shadow"
-#define _PATH_SHELLS "/etc/shells"
+
+#define _PATH_MOUNTED "/proc/mounts"
#define _PATH_TTY "/dev/tty"
-#define _PATH_UNIX "/boot/vmlinux"
-#define _PATH_UTMP "/var/run/utmp"
-#define _PATH_VI "/bin/vi"
-#define _PATH_WTMP "/var/log/wtmp"
/* Provide trailing slash, since mostly used for building pathnames. */
#define _PATH_DEV "/dev/"
-#define _PATH_TMP "/tmp/"
-#define _PATH_VARDB "/var/db/"
-#define _PATH_VARRUN "/var/run/"
-#define _PATH_VARTMP "/var/tmp/"
#endif /* !_PATHS_H_ */
diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h
index 0349e4c..44fe991 100644
--- a/libc/include/sys/_system_properties.h
+++ b/libc/include/sys/_system_properties.h
@@ -50,7 +50,7 @@
__BEGIN_DECLS
-struct prop_msg
+struct prop_msg
{
unsigned cmd;
char name[PROP_NAME_MAX];
@@ -58,7 +58,7 @@
};
#define PROP_MSG_SETPROP 1
-
+
/*
** Rules:
**
@@ -82,6 +82,7 @@
#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop"
#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop"
+#define PROP_PATH_BOOTIMAGE_BUILD "/build.prop"
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
#define PROP_PATH_FACTORY "/factory/factory.prop"
diff --git a/libc/include/sys/ucontext.h b/libc/include/sys/ucontext.h
index dd2a0bb..b68d704 100644
--- a/libc/include/sys/ucontext.h
+++ b/libc/include/sys/ucontext.h
@@ -180,6 +180,25 @@
} fp_r;
} fpregset_t;
+#ifdef __LP64__
+typedef struct {
+ gregset_t gregs;
+ fpregset_t fpregs;
+ greg_t mdhi;
+ greg_t hi1;
+ greg_t hi2;
+ greg_t hi3;
+ greg_t mdlo;
+ greg_t lo1;
+ greg_t lo2;
+ greg_t lo3;
+ greg_t pc;
+ uint32_t fpc_csr;
+ uint32_t used_math;
+ uint32_t dsp;
+ uint32_t reserved;
+} mcontext_t;
+#else
typedef struct {
unsigned regmask;
unsigned status;
@@ -200,6 +219,7 @@
unsigned long hi3;
unsigned long lo3;
} mcontext_t;
+#endif
typedef struct ucontext {
unsigned long uc_flags;
@@ -209,10 +229,6 @@
sigset_t uc_sigmask;
} ucontext_t;
-#elif defined(__mips64__)
-
-#error TODO
-
#elif defined(__x86_64__)
enum {
diff --git a/libc/upstream-openbsd/android/include/openbsd-compat.h b/libc/upstream-openbsd/android/include/openbsd-compat.h
index 8783467..8f55a26 100644
--- a/libc/upstream-openbsd/android/include/openbsd-compat.h
+++ b/libc/upstream-openbsd/android/include/openbsd-compat.h
@@ -59,6 +59,11 @@
#define ALIGNBYTES (sizeof(uintptr_t) - 1)
#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+/* OpenBSD has this in paths.h. But this directory doesn't normally exist.
+ * Even when it does exist, only the 'shell' user has permissions.
+ */
+#define _PATH_TMP "/data/local/tmp/"
+
/* We have OpenBSD's getentropy_linux.c, but we don't mention getentropy in any header. */
__LIBC_HIDDEN__ extern int getentropy(void*, size_t);
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fgetln.c b/libc/upstream-openbsd/lib/libc/stdio/fgetln.c
index d0c0809..1109cf2 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/fgetln.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/fgetln.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: fgetln.c,v 1.12 2013/11/12 07:04:06 deraadt Exp $ */
+/* $OpenBSD: fgetln.c,v 1.13 2015/01/05 21:58:52 millert Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -38,19 +38,12 @@
/*
* Expand the line buffer. Return -1 on error.
-#ifdef notdef
- * The `new size' does not account for a terminating '\0',
- * so we add 1 here.
-#endif
*/
static int
__slbexpand(FILE *fp, size_t newsize)
{
void *p;
-#ifdef notdef
- ++newsize;
-#endif
if (fp->_lb._size >= newsize)
return (0);
if ((p = realloc(fp->_lb._base, newsize)) == NULL)
@@ -141,14 +134,11 @@
}
*lenp = len;
ret = (char *)fp->_lb._base;
-#ifdef notdef
- ret[len] = '\0';
-#endif
FUNLOCKFILE(fp);
return (ret);
error:
- *lenp = 0; /* ??? */
FUNLOCKFILE(fp);
- return (NULL); /* ??? */
+ *lenp = 0;
+ return (NULL);
}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/getdelim.c b/libc/upstream-openbsd/lib/libc/stdio/getdelim.c
index dcde0c3..5e583cb 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/getdelim.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/getdelim.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: getdelim.c,v 1.1 2012/03/21 23:44:35 fgsch Exp $ */
+/* $OpenBSD: getdelim.c,v 1.2 2014/10/16 17:31:51 millert Exp $ */
/* $NetBSD: getdelim.c,v 1.13 2011/07/22 23:12:30 joerg Exp $ */
/*
@@ -78,13 +78,12 @@
else
len = (p - fp->_p) + 1;
- newlen = off + len;
/* Ensure we can handle it */
- if (newlen < off || newlen > SSIZE_MAX) {
+ if (off > SSIZE_MAX || len + 1 > SSIZE_MAX - off) {
errno = EOVERFLOW;
goto error;
}
- newlen++; /* reserve space for the NULL terminator */
+ newlen = off + len + 1; /* reserve space for NUL terminator */
if (newlen > *buflen) {
if (newlen < MINBUF)
newlen = MINBUF;
diff --git a/libc/upstream-openbsd/lib/libc/stdio/makebuf.c b/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
index d47e27c..56e5f21 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/makebuf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: makebuf.c,v 1.8 2005/12/28 18:50:22 millert Exp $ */
+/* $OpenBSD: makebuf.c,v 1.9 2015/01/13 07:18:21 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -65,7 +65,6 @@
fp->_bf._size = 1;
return;
}
- __atexit_register_cleanup(_cleanup);
flags |= __SMBF;
fp->_bf._base = fp->_p = p;
fp->_bf._size = size;
diff --git a/libc/upstream-openbsd/lib/libc/stdio/mktemp.c b/libc/upstream-openbsd/lib/libc/stdio/mktemp.c
index 2a17e52..956608c 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/mktemp.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/mktemp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mktemp.c,v 1.34 2014/08/31 02:21:18 guenther Exp $ */
+/* $OpenBSD: mktemp.c,v 1.35 2014/10/31 15:54:14 millert Exp $ */
/*
* Copyright (c) 1996-1998, 2008 Theo de Raadt
* Copyright (c) 1997, 2008-2009 Todd C. Miller
@@ -45,7 +45,7 @@
mktemp_internal(char *path, int slen, int mode, int flags)
{
char *start, *cp, *ep;
- const char *tempchars = TEMPCHARS;
+ const char tempchars[] = TEMPCHARS;
unsigned int tries;
struct stat sb;
size_t len;
diff --git a/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c b/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c
index 9414187..391a944 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/open_wmemstream.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: open_wmemstream.c,v 1.3 2014/03/06 07:28:21 gerhard Exp $ */
+/* $OpenBSD: open_wmemstream.c,v 1.4 2014/10/08 05:28:19 deraadt Exp $ */
/*
* Copyright (c) 2011 Martin Pieuchot <mpi@openbsd.org>
@@ -52,7 +52,7 @@
if (sz < end + 1)
sz = end + 1;
- p = realloc(st->string, sz * sizeof(wchar_t));
+ p = reallocarray(st->string, sz, sizeof(wchar_t));
if (!p)
return (-1);
bzero(p + st->size, (sz - st->size) * sizeof(wchar_t));
diff --git a/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c b/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
index 6c49f7a..9b2ab57 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/setvbuf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: setvbuf.c,v 1.11 2009/11/09 00:18:27 kurt Exp $ */
+/* $OpenBSD: setvbuf.c,v 1.12 2015/01/13 07:18:21 guenther Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -115,6 +115,13 @@
}
/*
+ * We're committed to buffering from here, so make sure we've
+ * registered to flush buffers on exit.
+ */
+ if (!__sdidinit)
+ __sinit();
+
+ /*
* Kill any seek optimization if the buffer is not the
* right size.
*
@@ -124,8 +131,7 @@
flags |= __SNPT;
/*
- * Fix up the FILE fields, and set __cleanup for output flush on
- * exit (since we are buffered in some way).
+ * Fix up the FILE fields.
*/
if (mode == _IOLBF)
flags |= __SLBF;
@@ -148,7 +154,6 @@
fp->_w = 0;
}
FUNLOCKFILE(fp);
- __atexit_register_cleanup(_cleanup);
return (ret);
}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/ungetc.c b/libc/upstream-openbsd/lib/libc/stdio/ungetc.c
index 675733a..ec98f26 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/ungetc.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/ungetc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ungetc.c,v 1.12 2009/11/09 00:18:27 kurt Exp $ */
+/* $OpenBSD: ungetc.c,v 1.13 2014/10/11 04:05:10 deraadt Exp $ */
/*-
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
@@ -64,14 +64,14 @@
return (0);
}
i = _UB(fp)._size;
- p = realloc(_UB(fp)._base, i << 1);
+ p = reallocarray(_UB(fp)._base, i, 2);
if (p == NULL)
return (EOF);
/* no overlap (hence can use memcpy) because we doubled the size */
(void)memcpy((void *)(p + i), (void *)p, (size_t)i);
fp->_p = p + i;
_UB(fp)._base = p;
- _UB(fp)._size = i << 1;
+ _UB(fp)._size = i * 2;
return (0);
}
diff --git a/linker/Android.mk b/linker/Android.mk
index 0383e7b..720389f 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -16,15 +16,8 @@
LOCAL_SRC_FILES_arm64 := arch/arm64/begin.S
LOCAL_SRC_FILES_x86 := arch/x86/begin.c
LOCAL_SRC_FILES_x86_64 := arch/x86_64/begin.S
-LOCAL_SRC_FILES_mips := arch/mips/begin.S
-LOCAL_SRC_FILES_mips64 := arch/mips64/begin.S
-
-# GNU assembler aborted with clang's output for linker.cpp:
-# Assertion failure in get_line_subseg at
-# /s/ndk-toolchain/src/build/../binutils/binutils-2.24/gas/dwarf2dbg.c line 271.
-ifeq ($(TARGET_ARCH),mips)
- LOCAL_CLANG_CFLAGS += -integrated-as
-endif
+LOCAL_SRC_FILES_mips := arch/mips/begin.S linker_mips.cpp
+LOCAL_SRC_FILES_mips64 := arch/mips64/begin.S linker_mips.cpp
LOCAL_LDFLAGS := \
-shared \
diff --git a/linker/linker.cpp b/linker/linker.cpp
index babefeb..0b0afc3 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -36,6 +36,7 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
+#include <sys/personality.h>
#include <unistd.h>
#include <new>
@@ -52,6 +53,7 @@
#include "linker_debug.h"
#include "linker_environ.h"
#include "linker_phdr.h"
+#include "linker_relocs.h"
#include "linker_allocator.h"
/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<<
@@ -120,14 +122,6 @@
__LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd.
-enum RelocationKind {
- kRelocAbsolute = 0,
- kRelocRelative,
- kRelocCopy,
- kRelocSymbol,
- kRelocMax
-};
-
#if STATS
struct linker_stats_t {
int count[kRelocMax];
@@ -135,30 +129,16 @@
static linker_stats_t linker_stats;
-static void count_relocation(RelocationKind kind) {
+void count_relocation(RelocationKind kind) {
++linker_stats.count[kind];
}
#else
-static void count_relocation(RelocationKind) {
+void count_relocation(RelocationKind) {
}
#endif
#if COUNT_PAGES
-static unsigned bitmask[4096];
-#if defined(__LP64__)
-#define MARK(offset) \
- do { \
- if ((((offset) >> 12) >> 5) < 4096) \
- bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
- } while (0)
-#else
-#define MARK(offset) \
- do { \
- bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
- } while (0)
-#endif
-#else
-#define MARK(x) do {} while (0)
+uint32_t bitmask[4096];
#endif
// You shouldn't try to call memory-allocating functions in the dynamic linker.
@@ -537,7 +517,7 @@
return gnu_hash_;
}
-static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
+ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
SymbolName symbol_name(name);
ElfW(Sym)* s = nullptr;
@@ -1277,17 +1257,33 @@
return ifunc_addr;
}
+#if !defined(__mips__)
#if defined(USE_RELA)
-int soinfo::relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
- for (size_t idx = 0; idx < count; ++idx, ++rela) {
- unsigned type = ELFW(R_TYPE)(rela->r_info);
- unsigned sym = ELFW(R_SYM)(rela->r_info);
- ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rela->r_offset + load_bias);
+static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
+ return rela->r_addend;
+}
+#else
+static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
+ if (ELFW(R_TYPE)(rel->r_info) == R_GENERIC_RELATIVE || ELFW(R_TYPE)(rel->r_info) == R_GENERIC_IRELATIVE) {
+ return *reinterpret_cast<ElfW(Addr)*>(reloc_addr);
+ }
+ return 0;
+}
+#endif
+
+template<typename ElfRelT>
+bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+ for (size_t idx = 0; idx < count; ++idx, ++rel) {
+ ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
+ ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
+
+ ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
ElfW(Addr) sym_addr = 0;
const char* sym_name = nullptr;
+ ElfW(Addr) addend = get_addend(rel, reloc);
- DEBUG("Processing '%s' relocation at index %zd", name, idx);
- if (type == 0) { // R_*_NONE
+ DEBUG("Processing '%s' relocation at index %zd", this->name, idx);
+ if (type == R_GENERIC_NONE) {
continue;
}
@@ -1302,7 +1298,7 @@
s = &symtab_[sym];
if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
- return -1;
+ return false;
}
/* IHI0044C AAELF 4.5.1.1:
@@ -1318,36 +1314,40 @@
*/
switch (type) {
+ case R_GENERIC_JUMP_SLOT:
+ case R_GENERIC_GLOB_DAT:
+ case R_GENERIC_RELATIVE:
+ case R_GENERIC_IRELATIVE:
#if defined(__aarch64__)
- case R_AARCH64_JUMP_SLOT:
- case R_AARCH64_GLOB_DAT:
case R_AARCH64_ABS64:
case R_AARCH64_ABS32:
case R_AARCH64_ABS16:
- case R_AARCH64_RELATIVE:
- case R_AARCH64_IRELATIVE:
+#elif defined(__x86_64__)
+ case R_X86_64_32:
+ case R_X86_64_64:
+#elif defined(__arm__)
+ case R_ARM_ABS32:
+#elif defined(__i386__)
+ case R_386_32:
+#endif
/*
* The sym_addr was initialized to be zero above, or the relocation
* code below does not care about value of sym_addr.
* No need to do anything.
*/
break;
-#elif defined(__x86_64__)
- case R_X86_64_JUMP_SLOT:
- case R_X86_64_GLOB_DAT:
- case R_X86_64_32:
- case R_X86_64_64:
- case R_X86_64_RELATIVE:
- case R_X86_64_IRELATIVE:
- // No need to do anything.
- break;
+#if defined(__x86_64__)
case R_X86_64_PC32:
sym_addr = reloc;
break;
+#elif defined(__i386__)
+ case R_386_PC32:
+ sym_addr = reloc;
+ break;
#endif
default:
- DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rela, idx);
- return -1;
+ DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx);
+ return false;
}
} else {
// We got a definition.
@@ -1357,119 +1357,120 @@
}
switch (type) {
+ case R_GENERIC_JUMP_SLOT:
+ count_relocation(kRelocAbsolute);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO JMP_SLOT %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(sym_addr + addend), sym_name);
+
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
+ break;
+ case R_GENERIC_GLOB_DAT:
+ count_relocation(kRelocAbsolute);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO GLOB_DAT %16p <- %16p %s\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(sym_addr + addend), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + addend);
+ break;
+ case R_GENERIC_RELATIVE:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO RELATIVE %16p <- %16p\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(base + addend));
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = (base + addend);
+ break;
+ case R_GENERIC_IRELATIVE:
+ count_relocation(kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO IRELATIVE %16p <- %16p\n",
+ reinterpret_cast<void*>(reloc),
+ reinterpret_cast<void*>(base + addend));
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + addend);
+ break;
+
#if defined(__aarch64__)
- case R_AARCH64_JUMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + rela->r_addend);
- break;
- case R_AARCH64_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = (sym_addr + rela->r_addend);
- break;
case R_AARCH64_ABS64:
count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO ABS64 %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend);
+ reloc, (sym_addr + addend), sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
break;
case R_AARCH64_ABS32:
count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO ABS32 %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend);
+ reloc, (sym_addr + addend), sym_name);
+ if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)),
static_cast<ElfW(Addr)>(INT32_MIN),
static_cast<ElfW(Addr)>(UINT32_MAX));
- return -1;
+ return false;
}
break;
case R_AARCH64_ABS16:
count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO ABS16 %16llx <- %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), sym_name);
- if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend);
+ reloc, (sym_addr + addend), sym_name);
+ if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + rela->r_addend)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)),
static_cast<ElfW(Addr)>(INT16_MIN),
static_cast<ElfW(Addr)>(UINT16_MAX));
- return -1;
+ return false;
}
break;
case R_AARCH64_PREL64:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO REL64 %16llx <- %16llx - %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + rela->r_addend) - rela->r_offset;
+ reloc, (sym_addr + addend), rel->r_offset, sym_name);
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr + addend) - rel->r_offset;
break;
case R_AARCH64_PREL32:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO REL32 %16llx <- %16llx - %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset, sym_name);
- if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + rela->r_addend) - rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset, sym_name);
+ if ((static_cast<ElfW(Addr)>(INT32_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)) <= static_cast<ElfW(Addr)>(UINT32_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + addend) - rel->r_offset);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)),
static_cast<ElfW(Addr)>(INT32_MIN),
static_cast<ElfW(Addr)>(UINT32_MAX));
- return -1;
+ return false;
}
break;
case R_AARCH64_PREL16:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO REL16 %16llx <- %16llx - %16llx %s\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset, sym_name);
- if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset))) &&
- ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + rela->r_addend) - rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset, sym_name);
+ if ((static_cast<ElfW(Addr)>(INT16_MIN) <= (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset))) &&
+ ((*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)) <= static_cast<ElfW(Addr)>(UINT16_MAX))) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += ((sym_addr + addend) - rel->r_offset);
} else {
DL_ERR("0x%016llx out of range 0x%016llx to 0x%016llx",
- (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)),
+ (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)),
static_cast<ElfW(Addr)>(INT16_MIN),
static_cast<ElfW(Addr)>(UINT16_MAX));
- return -1;
+ return false;
}
break;
- case R_AARCH64_RELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- if (sym) {
- DL_ERR("odd RELATIVE form...");
- return -1;
- }
- TRACE_TYPE(RELO, "RELO RELATIVE %16llx <- %16llx\n",
- reloc, (base + rela->r_addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = (base + rela->r_addend);
- break;
-
- case R_AARCH64_IRELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO IRELATIVE %16llx <- %16llx\n", reloc, (base + rela->r_addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + rela->r_addend);
- break;
-
case R_AARCH64_COPY:
/*
* ET_EXEC is not supported so this should not happen.
@@ -1481,175 +1482,39 @@
* set to ET_EXEC.
*/
DL_ERR("%s R_AARCH64_COPY relocations are not supported", name);
- return -1;
+ return false;
case R_AARCH64_TLS_TPREL64:
TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset);
break;
case R_AARCH64_TLS_DTPREL32:
TRACE_TYPE(RELO, "RELO TLS_DTPREL32 *** %16llx <- %16llx - %16llx\n",
- reloc, (sym_addr + rela->r_addend), rela->r_offset);
+ reloc, (sym_addr + addend), rel->r_offset);
break;
#elif defined(__x86_64__)
- case R_X86_64_JUMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %08zx <- %08zx %s", static_cast<size_t>(reloc),
- static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
- break;
- case R_X86_64_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %08zx <- %08zx %s", static_cast<size_t>(reloc),
- static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
- break;
- case R_X86_64_RELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- if (sym) {
- DL_ERR("odd RELATIVE form...");
- return -1;
- }
- TRACE_TYPE(RELO, "RELO RELATIVE %08zx <- +%08zx", static_cast<size_t>(reloc),
- static_cast<size_t>(base));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = base + rela->r_addend;
- break;
- case R_X86_64_IRELATIVE:
- count_relocation(kRelocRelative);
- MARK(rela->r_offset);
- TRACE_TYPE(RELO, "RELO IRELATIVE %16llx <- %16llx\n", reloc, (base + rela->r_addend));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + rela->r_addend);
- break;
case R_X86_64_32:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO R_X86_64_32 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
static_cast<size_t>(sym_addr), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
break;
case R_X86_64_64:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO R_X86_64_64 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
static_cast<size_t>(sym_addr), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend;
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend;
break;
case R_X86_64_PC32:
count_relocation(kRelocRelative);
- MARK(rela->r_offset);
+ MARK(rel->r_offset);
TRACE_TYPE(RELO, "RELO R_X86_64_PC32 %08zx <- +%08zx (%08zx - %08zx) %s",
static_cast<size_t>(reloc), static_cast<size_t>(sym_addr - reloc),
static_cast<size_t>(sym_addr), static_cast<size_t>(reloc), sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + rela->r_addend - reloc;
+ *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr + addend - reloc;
break;
-#endif
-
- default:
- DL_ERR("unknown reloc type %d @ %p (%zu)", type, rela, idx);
- return -1;
- }
- }
- return 0;
-}
-
-#else // REL, not RELA.
-int soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
- for (size_t idx = 0; idx < count; ++idx, ++rel) {
- unsigned type = ELFW(R_TYPE)(rel->r_info);
- // TODO: don't use unsigned for 'sym'. Use uint32_t or ElfW(Addr) instead.
- unsigned sym = ELFW(R_SYM)(rel->r_info);
- ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
- ElfW(Addr) sym_addr = 0;
- const char* sym_name = nullptr;
-
- DEBUG("Processing '%s' relocation at index %zd", name, idx);
- if (type == 0) { // R_*_NONE
- continue;
- }
-
- ElfW(Sym)* s = nullptr;
- soinfo* lsi = nullptr;
-
- if (sym != 0) {
- sym_name = get_string(symtab_[sym].st_name);
- s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
- if (s == nullptr) {
- // We only allow an undefined symbol if this is a weak reference...
- s = &symtab_[sym];
- if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
- DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
- return -1;
- }
-
- /* IHI0044C AAELF 4.5.1.1:
-
- Libraries are not searched to resolve weak references.
- It is not an error for a weak reference to remain
- unsatisfied.
-
- During linking, the value of an undefined weak reference is:
- - Zero if the relocation type is absolute
- - The address of the place if the relocation is pc-relative
- - The address of nominal base address if the relocation
- type is base-relative.
- */
-
- switch (type) {
-#if defined(__arm__)
- case R_ARM_JUMP_SLOT:
- case R_ARM_GLOB_DAT:
- case R_ARM_ABS32:
- case R_ARM_RELATIVE: /* Don't care. */
- // sym_addr was initialized to be zero above or relocation
- // code below does not care about value of sym_addr.
- // No need to do anything.
- break;
-#elif defined(__i386__)
- case R_386_JMP_SLOT:
- case R_386_GLOB_DAT:
- case R_386_32:
- case R_386_RELATIVE: /* Don't care. */
- case R_386_IRELATIVE:
- // sym_addr was initialized to be zero above or relocation
- // code below does not care about value of sym_addr.
- // No need to do anything.
- break;
- case R_386_PC32:
- sym_addr = reloc;
- break;
-#endif
-
-#if defined(__arm__)
- case R_ARM_COPY:
- // Fall through. Can't really copy if weak symbol is not found at run-time.
-#endif
- default:
- DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx);
- return -1;
- }
- } else {
- // We got a definition.
- sym_addr = lsi->resolve_symbol_address(s);
- }
- count_relocation(kRelocSymbol);
- }
-
- switch (type) {
-#if defined(__arm__)
- case R_ARM_JUMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
- case R_ARM_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
+#elif defined(__arm__)
case R_ARM_ABS32:
count_relocation(kRelocAbsolute);
MARK(rel->r_offset);
@@ -1674,20 +1539,8 @@
* set to ET_EXEC.
*/
DL_ERR("%s R_ARM_COPY relocations are not supported", name);
- return -1;
+ return false;
#elif defined(__i386__)
- case R_386_JMP_SLOT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO JMP_SLOT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
- case R_386_GLOB_DAT:
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO GLOB_DAT %08x <- %08x %s", reloc, sym_addr, sym_name);
- *reinterpret_cast<ElfW(Addr)*>(reloc) = sym_addr;
- break;
case R_386_32:
count_relocation(kRelocRelative);
MARK(rel->r_offset);
@@ -1701,113 +1554,15 @@
reloc, (sym_addr - reloc), sym_addr, reloc, sym_name);
*reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr - reloc);
break;
-#elif defined(__mips__)
- case R_MIPS_REL32:
-#if defined(__LP64__)
- // MIPS Elf64_Rel entries contain compound relocations
- // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case
- if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 ||
- ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) {
- DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)",
- type, (unsigned)ELF64_R_TYPE2(rel->r_info),
- (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx);
- return -1;
- }
#endif
- count_relocation(kRelocAbsolute);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast<size_t>(reloc),
- static_cast<size_t>(sym_addr), sym_name ? sym_name : "*SECTIONHDR*");
- if (s) {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr;
- } else {
- *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
- }
- break;
-#endif
-
-#if defined(__arm__)
- case R_ARM_RELATIVE:
-#elif defined(__i386__)
- case R_386_RELATIVE:
-#endif
- count_relocation(kRelocRelative);
- MARK(rel->r_offset);
- if (sym) {
- DL_ERR("odd RELATIVE form...");
- return -1;
- }
- TRACE_TYPE(RELO, "RELO RELATIVE %p <- +%p",
- reinterpret_cast<void*>(reloc), reinterpret_cast<void*>(base));
- *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
- break;
-#if defined(__i386__)
- case R_386_IRELATIVE:
- count_relocation(kRelocRelative);
- MARK(rel->r_offset);
- TRACE_TYPE(RELO, "RELO IRELATIVE %p <- %p", reinterpret_cast<void*>(reloc), reinterpret_cast<void*>(base));
- *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + *reinterpret_cast<ElfW(Addr)*>(reloc));
- break;
-#endif
-
default:
DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx);
- return -1;
- }
- }
- return 0;
-}
-#endif
-
-#if defined(__mips__)
-bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
- ElfW(Addr)** got = plt_got_;
- if (got == nullptr) {
- return true;
- }
-
- // got[0] is the address of the lazy resolver function.
- // got[1] may be used for a GNU extension.
- // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start).
- // FIXME: maybe this should be in a separate routine?
- if ((flags_ & FLAG_LINKER) == 0) {
- size_t g = 0;
- got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef);
- if (reinterpret_cast<intptr_t>(got[g]) < 0) {
- got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed);
- }
- // Relocate the local GOT entries.
- for (; g < mips_local_gotno_; g++) {
- got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias);
- }
- }
-
- // Now for the global GOT entries...
- ElfW(Sym)* sym = symtab_ + mips_gotsym_;
- got = plt_got_ + mips_local_gotno_;
- for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) {
- // This is an undefined reference... try to locate it.
- const char* sym_name = get_string(sym->st_name);
- soinfo* lsi = nullptr;
- ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
- if (s == nullptr) {
- // We only allow an undefined symbol if this is a weak reference.
- s = &symtab_[g];
- if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
- DL_ERR("cannot locate \"%s\"...", sym_name);
return false;
- }
- *got = 0;
- } else {
- // FIXME: is this sufficient?
- // For reference see NetBSD link loader
- // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
- *got = reinterpret_cast<ElfW(Addr)*>(lsi->resolve_symbol_address(s));
}
}
return true;
}
-#endif
+#endif // !defined(__mips__)
void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) {
if (functions == nullptr) {
@@ -2496,26 +2251,26 @@
#if defined(USE_RELA)
if (rela_ != nullptr) {
DEBUG("[ relocating %s ]", name);
- if (relocate(rela_, rela_count_, global_group, local_group)) {
+ if (!relocate(rela_, rela_count_, global_group, local_group)) {
return false;
}
}
if (plt_rela_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
- if (relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
+ if (!relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
return false;
}
}
#else
if (rel_ != nullptr) {
DEBUG("[ relocating %s ]", name);
- if (relocate(rel_, rel_count_, global_group, local_group)) {
+ if (!relocate(rel_, rel_count_, global_group, local_group)) {
return false;
}
}
if (plt_rel_ != nullptr) {
DEBUG("[ relocating %s plt ]", name);
- if (relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
+ if (!relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
return false;
}
}
@@ -2663,6 +2418,12 @@
ldpreload_env = linker_env_get("LD_PRELOAD");
}
+#if !defined(__LP64__)
+ if (personality(PER_LINUX32) == -1) {
+ __libc_fatal("error setting PER_LINUX32 personality: %s", strerror(errno));
+ }
+#endif
+
INFO("[ android linker & debugger ]");
soinfo* si = soinfo_alloc(args.argv[0], nullptr, 0, RTLD_GLOBAL);
@@ -2717,7 +2478,10 @@
somain = si;
- si->prelink_image();
+ if (!si->prelink_image()) {
+ __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
+ exit(EXIT_FAILURE);
+ }
// add somain to global group
si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
diff --git a/linker/linker.h b/linker/linker.h
index f7aa11c..2afbaf6 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -286,11 +286,8 @@
void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
void call_function(const char* function_name, linker_function_t function);
-#if defined(USE_RELA)
- int relocate(ElfW(Rela)* rela, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
-#else
- int relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
-#endif
+ template<typename ElfRelT>
+ bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
private:
// This part of the structure is only available
@@ -321,6 +318,19 @@
friend soinfo* get_libdl_info();
};
+ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
+ const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group);
+
+enum RelocationKind {
+ kRelocAbsolute = 0,
+ kRelocRelative,
+ kRelocCopy,
+ kRelocSymbol,
+ kRelocMax
+};
+
+void count_relocation(RelocationKind kind);
+
soinfo* get_libdl_info();
void do_android_get_LD_LIBRARY_PATH(char*, size_t);
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 0c7a784..5ded5ab 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -82,4 +82,23 @@
#define TRACE_TYPE(t, x...) do { if (DO_TRACE_##t) { TRACE(x); } } while (0)
+#if COUNT_PAGES
+extern uint32_t bitmask[];
+#if defined(__LP64__)
+#define MARK(offset) \
+ do { \
+ if ((((offset) >> 12) >> 5) < 4096) \
+ bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
+ } while (0)
+#else
+#define MARK(offset) \
+ do { \
+ bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
+ } while (0)
+#endif
+#else
+#define MARK(x) do {} while (0)
+
+#endif
+
#endif /* _LINKER_DEBUG_H_ */
diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp
new file mode 100644
index 0000000..eb2ae84
--- /dev/null
+++ b/linker/linker_mips.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker.h"
+#include "linker_debug.h"
+#include "linker_relocs.h"
+
+template<>
+bool soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+ for (size_t idx = 0; idx < count; ++idx, ++rel) {
+ ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
+ ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
+
+ ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
+ ElfW(Addr) sym_addr = 0;
+ const char* sym_name = nullptr;
+
+ DEBUG("Processing '%s' relocation at index %zd", this->name, idx);
+ if (type == R_GENERIC_NONE) {
+ continue;
+ }
+
+ ElfW(Sym)* s = nullptr;
+ soinfo* lsi = nullptr;
+
+ if (sym != 0) {
+ sym_name = get_string(symtab_[sym].st_name);
+ s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
+ if (s == nullptr) {
+ // mips does not support relocation with weak-undefined symbols
+ DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
+ return false;
+ } else {
+ // We got a definition.
+ sym_addr = lsi->resolve_symbol_address(s);
+ }
+ count_relocation(kRelocSymbol);
+ }
+
+ switch (type) {
+ case R_MIPS_REL32:
+#if defined(__LP64__)
+ // MIPS Elf64_Rel entries contain compound relocations
+ // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case
+ if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 ||
+ ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) {
+ DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)",
+ type, (unsigned)ELF64_R_TYPE2(rel->r_info),
+ (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx);
+ return false;
+ }
+#endif
+ count_relocation(s == nullptr ? kRelocAbsolute : kRelocRelative);
+ MARK(rel->r_offset);
+ TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast<size_t>(reloc),
+ static_cast<size_t>(sym_addr), sym_name ? sym_name : "*SECTIONHDR*");
+ if (s != nullptr) {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr;
+ } else {
+ *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
+ }
+ break;
+ default:
+ DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+ ElfW(Addr)** got = plt_got_;
+ if (got == nullptr) {
+ return true;
+ }
+
+ // got[0] is the address of the lazy resolver function.
+ // got[1] may be used for a GNU extension.
+ // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start).
+ // FIXME: maybe this should be in a separate routine?
+ if ((flags_ & FLAG_LINKER) == 0) {
+ size_t g = 0;
+ got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef);
+ if (reinterpret_cast<intptr_t>(got[g]) < 0) {
+ got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed);
+ }
+ // Relocate the local GOT entries.
+ for (; g < mips_local_gotno_; g++) {
+ got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias);
+ }
+ }
+
+ // Now for the global GOT entries...
+ ElfW(Sym)* sym = symtab_ + mips_gotsym_;
+ got = plt_got_ + mips_local_gotno_;
+ for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) {
+ // This is an undefined reference... try to locate it.
+ const char* sym_name = get_string(sym->st_name);
+ soinfo* lsi = nullptr;
+ ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
+ if (s == nullptr) {
+ // We only allow an undefined symbol if this is a weak reference.
+ s = &symtab_[g];
+ if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
+ DL_ERR("cannot locate \"%s\"...", sym_name);
+ return false;
+ }
+ *got = 0;
+ } else {
+ // FIXME: is this sufficient?
+ // For reference see NetBSD link loader
+ // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
+ *got = reinterpret_cast<ElfW(Addr)*>(lsi->resolve_symbol_address(s));
+ }
+ }
+ return true;
+}
+
diff --git a/linker/linker_relocs.h b/linker/linker_relocs.h
new file mode 100644
index 0000000..12c1497
--- /dev/null
+++ b/linker/linker_relocs.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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.
+ */
+
+#ifndef __LINKER_RELOCS_H
+#define __LINKER_RELOCS_H
+
+#include <elf.h>
+
+#define R_GENERIC_NONE 0 // R_*_NONE is always 0
+
+#if defined (__aarch64__)
+
+#define R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT
+#define R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT
+#define R_GENERIC_RELATIVE R_AARCH64_RELATIVE
+#define R_GENERIC_IRELATIVE R_AARCH64_IRELATIVE
+
+#elif defined (__arm__)
+
+#define R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT
+#define R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT
+#define R_GENERIC_RELATIVE R_ARM_RELATIVE
+#define R_GENERIC_IRELATIVE R_ARM_IRELATIVE
+
+#elif defined (__i386__)
+
+#define R_GENERIC_JUMP_SLOT R_386_JMP_SLOT
+#define R_GENERIC_GLOB_DAT R_386_GLOB_DAT
+#define R_GENERIC_RELATIVE R_386_RELATIVE
+#define R_GENERIC_IRELATIVE R_386_IRELATIVE
+
+#elif defined (__x86_64__)
+
+#define R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT
+#define R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT
+#define R_GENERIC_RELATIVE R_X86_64_RELATIVE
+#define R_GENERIC_IRELATIVE R_X86_64_IRELATIVE
+
+#endif
+
+#endif // __LINKER_RELOCS_H
diff --git a/tests/Android.mk b/tests/Android.mk
index dfb89d2..38d85f8 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -93,6 +93,7 @@
sstream_test.cpp \
sys_epoll_test.cpp \
sys_mman_test.cpp \
+ sys_personality_test.cpp \
sys_resource_test.cpp \
sys_select_test.cpp \
sys_sendfile_test.cpp \
diff --git a/tests/netdb_test.cpp b/tests/netdb_test.cpp
index ab5b487..a35f703 100644
--- a/tests/netdb_test.cpp
+++ b/tests/netdb_test.cpp
@@ -19,10 +19,16 @@
#include <gtest/gtest.h>
#include <arpa/inet.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+// https://code.google.com/p/android/issues/detail?id=13228
+TEST(netdb, freeaddrinfo_NULL) {
+ freeaddrinfo(NULL);
+}
+
TEST(netdb, getaddrinfo_NULL_host) {
// It's okay for the host argument to be NULL, as long as service isn't.
addrinfo* ai = NULL;
@@ -74,16 +80,34 @@
TEST(netdb, getaddrinfo_hints) {
addrinfo hints;
memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
+ hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo* ai = NULL;
ASSERT_EQ(0, getaddrinfo( "localhost", "9999", &hints, &ai));
- ASSERT_EQ(AF_INET, ai->ai_family);
- ASSERT_EQ(SOCK_STREAM, ai->ai_socktype);
- ASSERT_EQ(IPPROTO_TCP, ai->ai_protocol);
- ASSERT_TRUE(ai->ai_next == NULL);
+ ASSERT_TRUE(ai != NULL);
+ // In glibc, getaddrinfo() converts ::1 to 127.0.0.1 for localhost,
+ // so one or two addrinfo may be returned.
+ addrinfo* tai = ai;
+ while (tai != NULL) {
+ ASSERT_EQ(AF_INET, tai->ai_family);
+ ASSERT_EQ(SOCK_STREAM, tai->ai_socktype);
+ ASSERT_EQ(IPPROTO_TCP, tai->ai_protocol);
+ tai = tai->ai_next;
+ }
+ freeaddrinfo(ai);
+}
+
+TEST(netdb, getaddrinfo_ip6_localhost) {
+ addrinfo* ai = NULL;
+ ASSERT_EQ(0, getaddrinfo("ip6-localhost", NULL, NULL, &ai));
+ ASSERT_TRUE(ai != NULL);
+ ASSERT_GE(ai->ai_addrlen, static_cast<socklen_t>(sizeof(sockaddr_in6)));
+ ASSERT_TRUE(ai->ai_addr != NULL);
+ sockaddr_in6 *addr = reinterpret_cast<sockaddr_in6*>(ai->ai_addr);
+ ASSERT_EQ(addr->sin6_family, AF_INET6);
+ ASSERT_EQ(0, memcmp(&addr->sin6_addr, &in6addr_loopback, sizeof(in6_addr)));
freeaddrinfo(ai);
}
@@ -116,8 +140,41 @@
ASSERT_EQ(EAI_FAMILY, getnameinfo(sa, too_little, tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST));
}
-void VerifyLocalhost(hostent *hent) {
+TEST(netdb, getnameinfo_localhost) {
+ sockaddr_in addr;
+ char host[NI_MAXHOST];
+ memset(&addr, 0, sizeof(sockaddr_in));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(0x7f000001);
+ ASSERT_EQ(0, getnameinfo(reinterpret_cast<sockaddr*>(&addr), sizeof(addr),
+ host, sizeof(host), NULL, 0, 0));
+ ASSERT_STREQ(host, "localhost");
+}
+
+static void VerifyLocalhostName(const char* name) {
+ // Test possible localhost name and aliases, which depend on /etc/hosts or /system/etc/hosts.
+ ASSERT_TRUE(strcmp(name, "localhost") == 0 ||
+ strcmp(name, "ip6-localhost") == 0 ||
+ strcmp(name, "ip6-loopback") == 0) << name;
+}
+
+TEST(netdb, getnameinfo_ip6_localhost) {
+ sockaddr_in6 addr;
+ char host[NI_MAXHOST];
+ memset(&addr, 0, sizeof(sockaddr_in6));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = in6addr_loopback;
+ ASSERT_EQ(0, getnameinfo(reinterpret_cast<sockaddr*>(&addr), sizeof(addr),
+ host, sizeof(host), NULL, 0, 0));
+ VerifyLocalhostName(host);
+}
+
+static void VerifyLocalhost(hostent *hent) {
ASSERT_TRUE(hent != NULL);
+ VerifyLocalhostName(hent->h_name);
+ for (size_t i = 0; hent->h_aliases[i] != NULL; ++i) {
+ VerifyLocalhostName(hent->h_aliases[i]);
+ }
ASSERT_EQ(hent->h_addrtype, AF_INET);
ASSERT_EQ(hent->h_addr[0], 127);
ASSERT_EQ(hent->h_addr[1], 0);
@@ -180,21 +237,18 @@
}
TEST(netdb, gethostbyaddr) {
- char addr[4];
- ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
- hostent *hp = gethostbyaddr(addr, sizeof(addr), AF_INET);
+ in_addr addr = { htonl(0x7f000001) };
+ hostent *hp = gethostbyaddr(&addr, sizeof(addr), AF_INET);
VerifyLocalhost(hp);
}
TEST(netdb, gethostbyaddr_r) {
- char addr[4];
- ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
-
+ in_addr addr = { htonl(0x7f000001) };
hostent hent;
hostent *hp;
char buf[512];
int err;
- int result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+ int result = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
ASSERT_EQ(0, result);
VerifyLocalhost(hp);
@@ -204,7 +258,7 @@
hostent hent2;
hostent *hp2;
char buf2[512];
- result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent2, buf2, sizeof(buf2), &hp2, &err);
+ result = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &hent2, buf2, sizeof(buf2), &hp2, &err);
ASSERT_EQ(0, result);
VerifyLocalhost(hp2);
@@ -232,14 +286,12 @@
}
TEST(netdb, gethostbyaddr_r_ERANGE) {
- char addr[4];
- ASSERT_EQ(1, inet_pton(AF_INET, "127.0.0.1", addr));
-
+ in_addr addr = { htonl(0x7f000001) };
hostent hent;
hostent *hp;
char buf[4]; // Use too small buffer.
int err;
- int result = gethostbyaddr_r(addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
+ int result = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &hent, buf, sizeof(buf), &hp, &err);
ASSERT_EQ(ERANGE, result);
ASSERT_EQ(NULL, hp);
}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 1418c76..2398f23 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -32,7 +32,6 @@
#include <time.h>
#include <unistd.h>
-
TEST(pthread, pthread_key_create) {
pthread_key_t key;
ASSERT_EQ(0, pthread_key_create(&key, NULL));
@@ -633,18 +632,18 @@
ASSERT_EQ(default_stack_size, stack_size);
ASSERT_GE(GetActualStackSize(attributes), default_stack_size);
- // Large enough and a multiple of the page size.
+ // Large enough and a multiple of the page size; may be rounded up by pthread_create.
ASSERT_EQ(0, pthread_attr_setstacksize(&attributes, 32*1024));
ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &stack_size));
ASSERT_EQ(32*1024U, stack_size);
- ASSERT_EQ(GetActualStackSize(attributes), 32*1024U);
+ ASSERT_GE(GetActualStackSize(attributes), 32*1024U);
- // Large enough but not a multiple of the page size; will be rounded up by pthread_create.
+ // Large enough but not aligned; will be rounded up by pthread_create.
ASSERT_EQ(0, pthread_attr_setstacksize(&attributes, 32*1024 + 1));
ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &stack_size));
ASSERT_EQ(32*1024U + 1, stack_size);
#if defined(__BIONIC__)
- ASSERT_EQ(GetActualStackSize(attributes), 32*1024U + 1);
+ ASSERT_GT(GetActualStackSize(attributes), 32*1024U + 1);
#else // __BIONIC__
// glibc rounds down, in violation of POSIX. They document this in their BUGS section.
ASSERT_EQ(GetActualStackSize(attributes), 32*1024U);
@@ -939,6 +938,29 @@
ASSERT_EQ(6666U, stack_size);
}
+static void pthread_attr_getstack_18908062_helper(void*) {
+ char local_variable;
+ pthread_attr_t attributes;
+ pthread_getattr_np(pthread_self(), &attributes);
+ void* stack_base;
+ size_t stack_size;
+ pthread_attr_getstack(&attributes, &stack_base, &stack_size);
+
+ // Test whether &local_variable is in [stack_base, stack_base + stack_size).
+ ASSERT_LE(reinterpret_cast<char*>(stack_base), &local_variable);
+ ASSERT_LT(&local_variable, reinterpret_cast<char*>(stack_base) + stack_size);
+}
+
+// Check whether something on stack is in the range of
+// [stack_base, stack_base + stack_size). see b/18908062.
+TEST(pthread, pthread_attr_getstack_18908062) {
+ pthread_t t;
+ ASSERT_EQ(0, pthread_create(&t, NULL,
+ reinterpret_cast<void* (*)(void*)>(pthread_attr_getstack_18908062_helper),
+ NULL));
+ pthread_join(t, NULL);
+}
+
#if defined(__BIONIC__)
static void* pthread_gettid_np_helper(void* arg) {
*reinterpret_cast<pid_t*>(arg) = gettid();
diff --git a/tests/sys_personality_test.cpp b/tests/sys_personality_test.cpp
new file mode 100644
index 0000000..55a023d
--- /dev/null
+++ b/tests/sys_personality_test.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source 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 <gtest/gtest.h>
+
+#include <sys/personality.h>
+
+TEST(sys_personality, current_persona) {
+ int persona = personality(0xffffffff);
+#if defined(__BIONIC__)
+#if defined(__LP64__)
+ ASSERT_EQ(PER_LINUX, persona);
+#else
+ ASSERT_EQ(PER_LINUX32, persona);
+#endif
+#else
+ // GLIBC does not set persona prior process startup - it is always PER_LINUX;
+ ASSERT_EQ(PER_LINUX, persona);
+#endif
+}
diff --git a/tools/bionicbb/.gitignore b/tools/bionicbb/.gitignore
new file mode 100644
index 0000000..d0ff064
--- /dev/null
+++ b/tools/bionicbb/.gitignore
@@ -0,0 +1,58 @@
+config.py
+*.json
+oauth.storage
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
diff --git a/tools/bionicbb/README.md b/tools/bionicbb/README.md
new file mode 100644
index 0000000..91f64d8
--- /dev/null
+++ b/tools/bionicbb/README.md
@@ -0,0 +1,89 @@
+bionicbb
+========
+
+The bionic buildbot contains two services: a gmail polling service, and a web
+service that interacts with gerrit.
+
+Dependencies
+------------
+
+ * Python 2.7
+ * [Flask](http://flask.pocoo.org/)
+ * [Google API Client Library](https://developers.google.com/api-client-library/python/start/installation)
+ * [jenkinsapi](https://pypi.python.org/pypi/jenkinsapi)
+ * [Requests](http://docs.python-requests.org/en/latest/)
+ * [termcolor](https://pypi.python.org/pypi/termcolor)
+
+Setup
+-----
+
+Create a `config.py` in the same directory as the sources. The structure of the
+configuration file is as follows:
+
+```python
+client_secret_file = 'CLIENT_SECRET_FILE.json'
+build_listener_url = 'BUILD_LISTENER_URL'
+jenkins_url = 'JENKINS_URL'
+jenkins_credentials = {
+ 'username': 'JENKINS_USERNAME',
+ 'password': 'JENKINS_PASSWORD',
+}
+```
+
+The client secret file comes from the Gmail API page of the [Google Developers
+Console](https://console.developers.google.com/). The Jenkins credentials are
+for a Jenkins account that has the appropriate permissions to launch the jobs
+the buildbot will use.
+
+You will also need to add the HTTP password for the buildbot's Gerrit account to
+`~/.netrc`. The HTTP password can be obtained from the [Gerrit HTTP password
+settings](https://android-review.googlesource.com/#/settings/http-password).
+
+To launch the services:
+
+```bash
+$ python build_listener.py >build.log 2>&1 &
+$ python gmail_listener.py >mail.log 2>&1 &
+```
+
+The mail listener will direct your browser to an authentication page for the
+Gmail API.
+
+gmail\_listener.py
+------------------
+
+Bionicbb polls a gmail account to find changes that need to be built. The gmail
+account needs to have a gerrit account set up with project watches on anything
+it finds interesting. This is a rather ugly hack, but it seems to be the
+simplest option available.
+
+Gerrit does offer a streaming notification service that would be _far_ better,
+but it is only available over an SSH conection to gerrit, and the AOSP gerrit
+does not support this connection.
+
+Another option would be polling gerrit itself, but we'd have to process each
+change every time to see if it should be built, whereas project watches allow us
+to treat these as semi-push notifications (we still have to poll gmail).
+
+One drawback to this approach is that it's a hassle to set up the project
+watches for a large number of projects. Since bionicbb is only interested in a
+small subset of projects, this is a non-issue.
+
+If the buildbot has applied Verified-1 to a patchset, the user may add their own
+Verified+1 to the change and the buildbot will remove its rejection the next
+time the services polls (by default, every five minutes).
+
+The service will also listen for the following commands:
+
+ * `bionicbb:clean`: Something is very broken and the buildbot's output
+ directory needs to be nuked.
+ * `bionicbb:retry`: Something went wrong and the buildbot should retry the
+ build.
+
+build\_listener.py
+------------------
+
+The build listener service responds to HTTP POST events sent from Jenkins and
+updates CLs accordingly. The only other API endpoint is `/drop-rejection`, which
+will remove a Verified-1 from a previously rejected patchset. The actually
+invocation of this is handled by the gmail listener.
diff --git a/tools/bionicbb/build_listener.py b/tools/bionicbb/build_listener.py
new file mode 100644
index 0000000..f7f52ed
--- /dev/null
+++ b/tools/bionicbb/build_listener.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2015 The Android Open Source 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.
+#
+import json
+import requests
+import termcolor
+
+import gerrit
+
+from flask import Flask, request
+app = Flask(__name__)
+
+
+def gerrit_url(endpoint):
+ gerrit_base_url = 'https://android-review.googlesource.com'
+ return gerrit_base_url + endpoint
+
+
+@app.route('/', methods=['POST'])
+def handle_build_message():
+ result = json.loads(request.data)
+
+ name = result['name']
+ number = result['build']['number']
+ status = result['build']['status']
+ go_url = 'http://go/bionicbb/' + result['build']['url']
+ full_url = result['build']['full_url']
+ params = result['build']['parameters']
+ change_id = params['CHANGE_ID']
+ ref = params['REF']
+ patch_set = ref.split('/')[-1]
+
+ print '{} #{} {}: {}'.format(name, number, status, full_url)
+
+ # bionic-lint is always broken, so we don't want to reject changes for
+ # those failures until we clean things up.
+ if name == 'bionic-presubmit':
+ message_lines = ['{} #{} checkbuild {}: {}'.format(
+ name, number, status, go_url)]
+ if status == 'FAILURE':
+ message_lines += ['If you believe this Verified-1 was in error, '
+ '+1 the change and bionicbb will remove the -1 '
+ 'shortly.']
+
+ request_data = {
+ 'message': '\n'.join(message_lines)
+ }
+
+ label = 'Verified'
+ if status == 'FAILURE':
+ request_data['labels'] = {label: -1}
+ elif status == 'SUCCESS':
+ request_data['labels'] = {label: +1}
+
+ url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
+ patch_set))
+
+ headers = {'Content-Type': 'application/json;charset=UTF-8'}
+ print 'POST {}: {}'.format(url, request_data)
+ print requests.post(url, headers=headers, json=request_data)
+ elif name == 'clean-bionic-presubmit':
+ request_data = {'message': 'out/ directory removed'}
+ url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
+ patch_set))
+ headers = {'Content-Type': 'application/json;charset=UTF-8'}
+ print 'POST {}: {}'.format(url, request_data)
+ print requests.post(url, headers=headers, json=request_data)
+ elif name == 'bionic-lint':
+ print 'IGNORED'
+ else:
+ print '{}: {}'.format(termcolor.colored('red', 'UNKNOWN'), name)
+ return ''
+
+
+@app.route('/drop-rejection', methods=['POST'])
+def drop_rejection():
+ revision_info = json.loads(request.data)
+
+ change_id = revision_info['changeid']
+ patch_set = revision_info['patchset']
+
+ bb_email = 'bionicbb@android.com'
+ labels = gerrit.get_labels(change_id, patch_set)
+ if bb_email in labels['Verified']:
+ bb_review = labels['Verified'][bb_email]
+ else:
+ bb_review = 0
+
+ if bb_review >= 0:
+ print 'No rejection to drop: {} {}'.format(change_id, patch_set)
+ return ''
+
+ print 'Dropping rejection: {} {}'.format(change_id, patch_set)
+
+ request_data = {'labels': {'Verified': 0}}
+ url = gerrit_url('/a/changes/{}/revisions/{}/review'.format(change_id,
+ patch_set))
+ headers = {'Content-Type': 'application/json;charset=UTF-8'}
+ print 'POST {}: {}'.format(url, request_data)
+ print requests.post(url, headers=headers, json=request_data)
+ return ''
+
+
+if __name__ == "__main__":
+ app.run(host='0.0.0.0', debug=True)
diff --git a/tools/bionicbb/gerrit.py b/tools/bionicbb/gerrit.py
new file mode 100644
index 0000000..76e42b4
--- /dev/null
+++ b/tools/bionicbb/gerrit.py
@@ -0,0 +1,74 @@
+#
+# Copyright (C) 2015 The Android Open Source 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.
+#
+import json
+import requests
+
+
+class GerritError(RuntimeError):
+ def __init__(self, code, url):
+ self.code = code
+ self.url = url
+ super(GerritError, self).__init__('Error {}: {}'.format(code, url))
+
+
+def get_commit(change_id, revision):
+ return json.loads(
+ call('/changes/{}/revisions/{}/commit'.format(change_id, revision)))
+
+
+def call(endpoint, method='GET'):
+ if method != 'GET':
+ raise NotImplementedError('Currently only HTTP GET is supported.')
+ gerrit_url = 'https://android-review.googlesource.com'
+ url = gerrit_url + endpoint
+ response = requests.get(url)
+ if response.status_code != 200:
+ raise GerritError(response.status_code, url)
+ return response.text[5:]
+
+
+def ref_for_change(change_id):
+ endpoint = '/changes/{}/detail?o=CURRENT_REVISION'.format(change_id)
+ change = json.loads(call(endpoint))
+ commit = change['current_revision']
+ return change['revisions'][commit]['fetch']['http']['ref']
+
+
+def get_labels(change_id, patch_set):
+ """Returns labels attached to a revision.
+
+ Returned data is in the following format:
+ {
+ 'Code-Review': {
+ <email>: <value>,
+ ...
+ },
+ 'Verified': {
+ <email>: <value>,
+ ...
+ }
+ }
+ """
+ details = call('/changes/{}/revisions/{}/review'.format(
+ change_id, patch_set))
+ labels = {'Code-Review': {}, 'Verified': {}}
+ for review in details['labels']['Code-Review']['all']:
+ if 'value' in review and 'email' in review:
+ labels['Code-Review'][review['email']] = int(review['value'])
+ for review in details['labels']['Verified']['all']:
+ if 'value' in review and 'email' in review:
+ labels['Verified'][review['email']] = int(review['value'])
+ return labels
diff --git a/tools/bionicbb/gmail_listener.py b/tools/bionicbb/gmail_listener.py
new file mode 100644
index 0000000..6a8b9e6
--- /dev/null
+++ b/tools/bionicbb/gmail_listener.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python2
+#
+# Copyright (C) 2015 The Android Open Source 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.
+#
+import base64
+import httplib
+import httplib2
+import jenkinsapi
+import json
+import re
+import requests
+import termcolor
+import socket
+import sys
+import time
+
+import apiclient.errors
+
+import config
+import gerrit
+
+
+class GmailError(RuntimeError):
+ def __init__(self, message):
+ super(GmailError, self).__init__(message)
+
+
+def get_gerrit_label(labels):
+ for label in labels:
+ if label['name'] == 'gerrit':
+ return label['id']
+ return None
+
+
+def get_headers(msg):
+ headers = {}
+ for hdr in msg['payload']['headers']:
+ headers[hdr['name']] = hdr['value']
+ return headers
+
+
+def should_skip_message(info):
+ if info['MessageType'] in ('newchange', 'newpatchset', 'comment'):
+ commit = gerrit.get_commit(info['Change-Id'], info['PatchSet'])
+ committer = commit['committer']['email']
+ return not committer.endswith('@google.com')
+ else:
+ raise ValueError('should_skip_message() is only valid for new '
+ 'changes, patch sets, and commits.')
+
+
+def build_service():
+ from apiclient.discovery import build
+ from oauth2client.client import flow_from_clientsecrets
+ from oauth2client.file import Storage
+ from oauth2client.tools import run
+
+ OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.modify'
+ STORAGE = Storage('oauth.storage')
+
+ # Start the OAuth flow to retrieve credentials
+ flow = flow_from_clientsecrets(config.client_secret_file,
+ scope=OAUTH_SCOPE)
+ http = httplib2.Http()
+
+ # Try to retrieve credentials from storage or run the flow to generate them
+ credentials = STORAGE.get()
+ if credentials is None or credentials.invalid:
+ credentials = run(flow, STORAGE, http=http)
+
+ http = credentials.authorize(http)
+ return build('gmail', 'v1', http=http)
+
+
+def get_all_messages(service, label):
+ msgs = []
+ response = service.users().messages().list(
+ userId='me', labelIds=label).execute()
+ if 'messages' in response:
+ msgs.extend(response['messages'])
+ while 'nextPageToken' in response:
+ page_token = response['nextPageToken']
+ response = service.users().messages().list(
+ userId='me', pageToken=page_token).execute()
+ msgs.extend(response['messages'])
+ return msgs
+
+
+def get_body(msg):
+ if 'attachmentId' in msg['payload']['body']:
+ raise NotImplementedError('Handling of messages contained in '
+ 'attachments not yet implemented.')
+ b64_body = msg['payload']['body']['data']
+ return base64.urlsafe_b64decode(b64_body.encode('ASCII'))
+
+
+def get_gerrit_info(body):
+ info = {}
+ gerrit_pattern = r'^Gerrit-(\S+): (.+)$'
+ for match in re.finditer(gerrit_pattern, body, flags=re.MULTILINE):
+ info[match.group(1)] = match.group(2).strip()
+ return info
+
+
+def clean_project(gerrit_info, dry_run):
+ username = config.jenkins_credentials['username']
+ password = config.jenkins_credentials['password']
+ jenkins_url = config.jenkins_url
+ jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password)
+
+ build = 'clean-bionic-presubmit'
+ if build in jenkins:
+ if not dry_run:
+ job = jenkins[build].invoke()
+ url = job.get_build().baseurl
+ else:
+ url = 'DRY_RUN_URL'
+ print '{}({}): {} {}'.format(
+ termcolor.colored('CLEAN', 'green'),
+ gerrit_info['MessageType'],
+ build,
+ url)
+ else:
+ print '{}({}): {}'.format(
+ termcolor.colored('CLEAN', 'red'),
+ gerrit_info['MessageType'],
+ termcolor.colored(build, 'red'))
+ return True
+
+
+def build_project(gerrit_info, dry_run):
+ project_to_jenkins_map = {
+ 'platform/bionic': 'bionic-presubmit',
+ 'platform/build': 'bionic-presubmit',
+ 'platform/external/jemalloc': 'bionic-presubmit',
+ 'platform/external/libcxx': 'bionic-presubmit',
+ 'platform/external/libcxxabi': 'bionic-presubmit',
+ 'platform/external/compiler-rt': 'bionic-presubmit',
+ }
+
+ username = config.jenkins_credentials['username']
+ password = config.jenkins_credentials['password']
+ jenkins_url = config.jenkins_url
+ jenkins = jenkinsapi.api.Jenkins(jenkins_url, username, password)
+
+ project = gerrit_info['Project']
+ change_id = gerrit_info['Change-Id']
+ if project in project_to_jenkins_map:
+ build = project_to_jenkins_map[project]
+ else:
+ build = 'bionic-presubmit'
+
+ if build in jenkins:
+ project_path = '/'.join(project.split('/')[1:])
+ if not project_path:
+ raise RuntimeError('bogus project: {}'.format(project))
+ if project_path.startswith('platform/'):
+ print '{}({}): {} => {}'.format(
+ termcolor.colored('ERROR', 'red'),
+ 'project',
+ project,
+ project_path)
+ return False
+ try:
+ ref = gerrit.ref_for_change(change_id)
+ except gerrit.GerritError as ex:
+ print '{}({}): {} {}'.format(
+ termcolor.colored('GERRIT-ERROR', 'red'),
+ ex.code,
+ change_id,
+ ex.url)
+ return False
+ params = {
+ 'REF': ref,
+ 'CHANGE_ID': change_id,
+ 'PROJECT': project_path
+ }
+ if not dry_run:
+ job = jenkins[build].invoke(build_params=params)
+ url = job.get_build().baseurl
+ else:
+ url = 'DRY_RUN_URL'
+ print '{}({}): {} => {} {} {}'.format(
+ termcolor.colored('BUILD', 'green'),
+ gerrit_info['MessageType'],
+ project,
+ build,
+ url,
+ change_id)
+ else:
+ print '{}({}): {} => {} {}'.format(
+ termcolor.colored('BUILD', 'red'),
+ gerrit_info['MessageType'],
+ project,
+ termcolor.colored(build, 'red'),
+ change_id)
+ return True
+
+
+def handle_change(gerrit_info, _, dry_run):
+ if should_skip_message(gerrit_info):
+ return True
+ return build_project(gerrit_info, dry_run)
+handle_newchange = handle_change
+handle_newpatchset = handle_change
+
+
+def drop_rejection(gerrit_info, dry_run):
+ request_data = {
+ 'changeid': gerrit_info['Change-Id'],
+ 'patchset': gerrit_info['PatchSet']
+ }
+ url = '{}/{}'.format(config.build_listener_url, 'drop-rejection')
+ headers = {'Content-Type': 'application/json;charset=UTF-8'}
+ if not dry_run:
+ try:
+ requests.post(url, headers=headers, data=json.dumps(request_data))
+ except requests.exceptions.ConnectionError as ex:
+ print '{}(drop-rejection): {}'.format(
+ termcolor.colored('ERROR', 'red'), ex)
+ return False
+ print '{}({}): {}'.format(
+ termcolor.colored('CHECK', 'green'),
+ gerrit_info['MessageType'],
+ gerrit_info['Change-Id'])
+ return True
+
+
+def handle_comment(gerrit_info, body, dry_run):
+ if 'Verified+1' in body:
+ drop_rejection(gerrit_info, dry_run)
+
+ # TODO(danalbert): Needs to be based on the account that made the comment.
+ if should_skip_message(gerrit_info):
+ return True
+
+ command_map = {
+ 'clean': lambda: clean_project(gerrit_info, dry_run),
+ 'retry': lambda: build_project(gerrit_info, dry_run),
+ }
+
+ def handle_unknown_command():
+ pass # TODO(danalbert): should complain to the commenter.
+
+ commands = [match.group(1).strip() for match in
+ re.finditer(r'^bionicbb:\s*(.+)$', body, flags=re.MULTILINE)]
+
+ for command in commands:
+ if command in command_map:
+ command_map[command]()
+ else:
+ handle_unknown_command()
+
+ return True
+
+
+def skip_handler(gerrit_info, _, __):
+ print '{}({}): {}'.format(
+ termcolor.colored('SKIP', 'yellow'),
+ gerrit_info['MessageType'],
+ gerrit_info['Change-Id'])
+ return True
+handle_abandon = skip_handler
+handle_merged = skip_handler
+handle_restore = skip_handler
+handle_revert = skip_handler
+
+
+def process_message(msg, dry_run):
+ try:
+ body = get_body(msg)
+ gerrit_info = get_gerrit_info(body)
+ if not gerrit_info:
+ print termcolor.colored('No info found: {}'.format(msg['id']),
+ 'red')
+ msg_type = gerrit_info['MessageType']
+ handler = 'handle_{}'.format(gerrit_info['MessageType'])
+ if handler in globals():
+ return globals()[handler](gerrit_info, body, dry_run)
+ else:
+ print termcolor.colored(
+ 'MessageType {} unhandled.'.format(msg_type), 'red')
+ print
+ return False
+ except NotImplementedError as ex:
+ print ex
+ return False
+
+
+def main(argc, argv):
+ dry_run = False
+ if argc == 2 and argv[1] == '--dry-run':
+ dry_run = True
+ elif argc > 2:
+ sys.exit('usage: python {} [--dry-run]'.format(argv[0]))
+
+ gmail_service = build_service()
+ msg_service = gmail_service.users().messages()
+
+ while True:
+ try:
+ labels = gmail_service.users().labels().list(userId='me').execute()
+ if not labels['labels']:
+ raise GmailError('Could not retrieve Gmail labels')
+ label_id = get_gerrit_label(labels['labels'])
+ if not label_id:
+ raise GmailError('Could not find gerrit label')
+
+ for msg in get_all_messages(gmail_service, label_id):
+ msg = msg_service.get(userId='me', id=msg['id']).execute()
+ if process_message(msg, dry_run) and not dry_run:
+ msg_service.trash(userId='me', id=msg['id']).execute()
+ time.sleep(60 * 5)
+ except GmailError as ex:
+ print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), ex)
+ time.sleep(60 * 5)
+ except apiclient.errors.HttpError as ex:
+ print '{}: {}!'.format(termcolor.colored('ERROR', 'red'), ex)
+ time.sleep(60 * 5)
+ except httplib.BadStatusLine:
+ pass
+ except httplib2.ServerNotFoundError:
+ pass
+ except socket.error:
+ pass
+
+
+if __name__ == '__main__':
+ main(len(sys.argv), sys.argv)
diff --git a/tools/bionicbb/test_gmail_listener.py b/tools/bionicbb/test_gmail_listener.py
new file mode 100644
index 0000000..6545cdc
--- /dev/null
+++ b/tools/bionicbb/test_gmail_listener.py
@@ -0,0 +1,64 @@
+import gmail_listener
+import mock
+import unittest
+
+
+class TestShouldSkipMessage(unittest.TestCase):
+ def test_accepts_googlers(self):
+ for message_type in ('newchange', 'newpatchset', 'comment'):
+ with mock.patch('gerrit.get_commit') as mock_commit:
+ mock_commit.return_value = {
+ 'committer': {'email': 'googler@google.com'}
+ }
+
+ self.assertFalse(gmail_listener.should_skip_message({
+ 'MessageType': message_type,
+ 'Change-Id': '',
+ 'PatchSet': '',
+ }))
+
+ def test_rejects_non_googlers(self):
+ for message_type in ('newchange', 'newpatchset', 'comment'):
+ with mock.patch('gerrit.get_commit') as mock_commit:
+ mock_commit.return_value = {
+ 'committer': {'email': 'fakegoogler@google.com.fake.com'}
+ }
+
+ self.assertTrue(gmail_listener.should_skip_message({
+ 'MessageType': message_type,
+ 'Change-Id': '',
+ 'PatchSet': '',
+ }))
+
+ with mock.patch('gerrit.get_commit') as mock_commit:
+ mock_commit.return_value = {
+ 'committer': {'email': 'johndoe@example.com'}
+ }
+
+ self.assertTrue(gmail_listener.should_skip_message({
+ 'MessageType': message_type,
+ 'Change-Id': '',
+ 'PatchSet': '',
+ }))
+
+ def test_calls_gerrit_get_commit(self): # pylint: disable=no-self-use
+ for message_type in ('newchange', 'newpatchset', 'comment'):
+ with mock.patch('gerrit.get_commit') as mock_commit:
+ gmail_listener.should_skip_message({
+ 'MessageType': message_type,
+ 'Change-Id': 'foo',
+ 'PatchSet': 'bar',
+ })
+ mock_commit.assert_called_once_with('foo', 'bar')
+
+ with mock.patch('gerrit.get_commit') as mock_commit:
+ gmail_listener.should_skip_message({
+ 'MessageType': message_type,
+ 'Change-Id': 'baz',
+ 'PatchSet': 'qux',
+ })
+ mock_commit.assert_called_once_with('baz', 'qux')
+
+
+if __name__ == '__main__':
+ unittest.main()