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()