Merge "Do a second key cleanup in pthread_exit." into lmp-dev
diff --git a/libc/Android.mk b/libc/Android.mk
index 46c9676..2007965 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -226,7 +226,6 @@
     bionic/unlink.cpp \
     bionic/utimes.cpp \
     bionic/vdso.cpp \
-    bionic/vfork.cpp \
     bionic/wait.cpp \
     bionic/wchar.cpp \
     bionic/wctype.cpp \
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 128ec6b..38ae831 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -77,6 +77,7 @@
 int     setgroups:setgroups32(int, const gid_t*)   arm,x86
 int     setgroups:setgroups(int, const gid_t*)     arm64,mips,mips64,x86_64
 int     setpgid(pid_t, pid_t)  all
+pid_t   vfork(void)  arm
 int     setregid:setregid32(gid_t, gid_t)  arm,x86
 int     setregid:setregid(gid_t, gid_t)    arm64,mips,mips64,x86_64
 int     chroot(const char*)  all
diff --git a/libc/arch-arm/syscalls/vfork.S b/libc/arch-arm/syscalls/vfork.S
new file mode 100644
index 0000000..e12fba5
--- /dev/null
+++ b/libc/arch-arm/syscalls/vfork.S
@@ -0,0 +1,14 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+ENTRY(vfork)
+    mov     ip, r7
+    ldr     r7, =__NR_vfork
+    swi     #0
+    mov     r7, ip
+    cmn     r0, #(MAX_ERRNO + 1)
+    bxls    lr
+    neg     r0, r0
+    b       __set_errno
+END(vfork)
diff --git a/libc/arch-arm64/arm64.mk b/libc/arch-arm64/arm64.mk
index 314358e..ed991ce 100644
--- a/libc/arch-arm64/arm64.mk
+++ b/libc/arch-arm64/arm64.mk
@@ -36,6 +36,7 @@
     arch-arm64/bionic/__set_tls.c \
     arch-arm64/bionic/sigsetjmp.S \
     arch-arm64/bionic/syscall.S \
+    arch-arm64/bionic/vfork.S \
 
 
 libc_crt_target_cflags_arm64 := \
diff --git a/libc/bionic/vfork.cpp b/libc/arch-arm64/bionic/vfork.S
similarity index 75%
rename from libc/bionic/vfork.cpp
rename to libc/arch-arm64/bionic/vfork.S
index b706a7f..c700623 100644
--- a/libc/bionic/vfork.cpp
+++ b/libc/arch-arm64/bionic/vfork.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,10 +26,23 @@
  * SUCH DAMAGE.
  */
 
-#include <unistd.h>
+#include <private/bionic_asm.h>
+#include <asm/signal.h>
+#include <linux/sched.h>
 
-// vfork(2) was removed from POSIX 2008, but it's common enough that we can't
-// actually remove it entirely.
-extern "C" pid_t vfork(void) {
-  return fork();
-}
+ENTRY(vfork)
+    mov     x0, #(CLONE_VM | CLONE_VFORK | SIGCHLD)
+    mov     x1, xzr
+    mov     x2, xzr
+    mov     x3, xzr
+    mov     x4, xzr
+
+    mov     x8, __NR_clone
+    svc     #0
+
+    cmn     x0, #(MAX_ERRNO + 1)
+    cneg    x0, x0, hi
+    b.hi    __set_errno
+
+    ret
+END(vfork)
diff --git a/libc/bionic/vfork.cpp b/libc/arch-mips/bionic/vfork.S
similarity index 72%
copy from libc/bionic/vfork.cpp
copy to libc/arch-mips/bionic/vfork.S
index b706a7f..96de69e 100644
--- a/libc/bionic/vfork.cpp
+++ b/libc/arch-mips/bionic/vfork.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,10 +26,33 @@
  * SUCH DAMAGE.
  */
 
-#include <unistd.h>
+#include <private/bionic_asm.h>
+#include <linux/sched.h>
 
-// vfork(2) was removed from POSIX 2008, but it's common enough that we can't
-// actually remove it entirely.
-extern "C" pid_t vfork(void) {
-  return fork();
-}
+// TODO: mips' uapi signal.h is missing #ifndef __ASSEMBLY__.
+// #include <asm/signal.h>
+#define SIGCHLD 18
+
+ENTRY(vfork)
+	.set	noreorder
+	.cpload	t9
+
+	li	a0, (CLONE_VM | CLONE_VFORK | SIGCHLD)
+	li	a1, 0
+	li	a2, 0
+	li	a3, 0
+	subu	sp, 8
+	sw	$0, 16(sp)
+	li	v0, __NR_clone
+	syscall
+	addu	sp, 8
+	bnez	a3, 1f
+	 move	a0, v0
+
+	j	ra
+	 nop
+1:
+	la	t9, __set_errno
+	j	t9
+	 nop
+END(vfork)
diff --git a/libc/arch-mips/mips.mk b/libc/arch-mips/mips.mk
index bf3b8ae..8e415f9 100644
--- a/libc/arch-mips/mips.mk
+++ b/libc/arch-mips/mips.mk
@@ -59,6 +59,7 @@
     arch-mips/bionic/setjmp.S \
     arch-mips/bionic/sigsetjmp.S \
     arch-mips/bionic/syscall.S \
+    arch-mips/bionic/vfork.S \
 
 ifndef ARCH_MIPS_REV6
 libc_bionic_src_files_mips += \
diff --git a/libc/bionic/vfork.cpp b/libc/arch-mips64/bionic/vfork.S
similarity index 62%
copy from libc/bionic/vfork.cpp
copy to libc/arch-mips64/bionic/vfork.S
index b706a7f..911a264 100644
--- a/libc/bionic/vfork.cpp
+++ b/libc/arch-mips64/bionic/vfork.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,10 +26,46 @@
  * SUCH DAMAGE.
  */
 
-#include <unistd.h>
+#include <private/bionic_asm.h>
+#include <linux/sched.h>
 
-// vfork(2) was removed from POSIX 2008, but it's common enough that we can't
-// actually remove it entirely.
-extern "C" pid_t vfork(void) {
-  return fork();
-}
+// TODO: mips' uapi signal.h is missing #ifndef __ASSEMBLY__.
+// #include <asm/signal.h>
+#define SIGCHLD 18
+
+	.text
+
+#if (_MIPS_SIM == _ABIO32) || (_MIPS_SIM == _ABI32)
+FRAMESZ		=	MKFSIZ(5,0)
+#else
+FRAMESZ		=	MKFSIZ(0,0)
+#endif
+
+LEAF(vfork,FRAMESZ)
+#if FRAMESZ!=0
+	PTR_SUBU sp, FRAMESZ
+#endif
+	SETUP_GP64(a5, vfork)
+	LI	a0, (CLONE_VM | CLONE_VFORK | SIGCHLD)
+	move	a1, $0
+	move	a2, $0
+	move	a3, $0
+#if (_MIPS_SIM == _ABIO32) || (_MIPS_SIM == _ABI32)
+	REG_S	$0, 4*REGSZ(sp)
+#else
+	move	a4, $0
+#endif
+	LI	v0, __NR_clone
+	syscall
+#if FRAMESZ!=0
+	PTR_ADDU sp,FRAMESZ
+#endif
+	move	a0, v0
+	bnez	a3, 1f
+	RESTORE_GP64
+	j	ra
+1:
+	LA	t9,__set_errno
+	RESTORE_GP64
+	j	t9
+	END(vfork)
diff --git a/libc/arch-mips64/mips64.mk b/libc/arch-mips64/mips64.mk
index 6f7a928..230cb26 100644
--- a/libc/arch-mips64/mips64.mk
+++ b/libc/arch-mips64/mips64.mk
@@ -46,6 +46,7 @@
     arch-mips64/bionic/setjmp.S \
     arch-mips64/bionic/sigsetjmp.S \
     arch-mips64/bionic/syscall.S \
+    arch-mips64/bionic/vfork.S \
 
 # FIXME TODO
 ## libc_bionic_src_files_mips64 += arch-mips64/string/memcpy.S
diff --git a/libc/bionic/vfork.cpp b/libc/arch-x86/bionic/vfork.S
similarity index 75%
copy from libc/bionic/vfork.cpp
copy to libc/arch-x86/bionic/vfork.S
index b706a7f..ffa6b16 100644
--- a/libc/bionic/vfork.cpp
+++ b/libc/arch-x86/bionic/vfork.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,10 +26,19 @@
  * SUCH DAMAGE.
  */
 
-#include <unistd.h>
+#include <private/bionic_asm.h>
 
-// vfork(2) was removed from POSIX 2008, but it's common enough that we can't
-// actually remove it entirely.
-extern "C" pid_t vfork(void) {
-  return fork();
-}
+// This custom code preserves the return address across the system call.
+
+ENTRY(vfork)
+  popl    %ecx  // Grab the return address.
+  movl    $__NR_vfork, %eax
+  int     $0x80
+  cmpl    $-MAX_ERRNO, %eax
+  jb      1f
+  negl    %eax
+  pushl   %eax
+  call    __set_errno
+1:
+  jmp     *%ecx  // Jump to the stored return address.
+END(vfork)
diff --git a/libc/arch-x86/x86.mk b/libc/arch-x86/x86.mk
index 8aa2645..019dc8e 100644
--- a/libc/arch-x86/x86.mk
+++ b/libc/arch-x86/x86.mk
@@ -31,6 +31,7 @@
     arch-x86/bionic/__set_tls.c \
     arch-x86/bionic/sigsetjmp.S \
     arch-x86/bionic/syscall.S \
+    arch-x86/bionic/vfork.S \
 
 ## ARCH variant specific source files
 arch_variant_mk := $(LOCAL_PATH)/arch-x86/$(TARGET_ARCH_VARIANT)/$(TARGET_ARCH_VARIANT).mk
diff --git a/libc/bionic/vfork.cpp b/libc/arch-x86_64/bionic/vfork.S
similarity index 75%
copy from libc/bionic/vfork.cpp
copy to libc/arch-x86_64/bionic/vfork.S
index b706a7f..7c14cc0 100644
--- a/libc/bionic/vfork.cpp
+++ b/libc/arch-x86_64/bionic/vfork.S
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,10 +26,20 @@
  * SUCH DAMAGE.
  */
 
-#include <unistd.h>
+#include <private/bionic_asm.h>
 
-// vfork(2) was removed from POSIX 2008, but it's common enough that we can't
-// actually remove it entirely.
-extern "C" pid_t vfork(void) {
-  return fork();
-}
+// This custom code preserves the return address across the system call.
+
+ENTRY(vfork)
+  popq    %rdi  // Grab the return address.
+  movl    $__NR_vfork, %eax
+  syscall
+  pushq   %rdi  // Restore the return address.
+  cmpq    $-MAX_ERRNO, %rax
+  jb      1f
+  negl    %eax
+  movl    %eax, %edi
+  call    __set_errno
+1:
+  ret
+END(vfork)
diff --git a/libc/arch-x86_64/x86_64.mk b/libc/arch-x86_64/x86_64.mk
index 234cf67..7887c51 100644
--- a/libc/arch-x86_64/x86_64.mk
+++ b/libc/arch-x86_64/x86_64.mk
@@ -37,6 +37,7 @@
     arch-x86_64/bionic/__set_tls.c \
     arch-x86_64/bionic/sigsetjmp.S \
     arch-x86_64/bionic/syscall.S \
+    arch-x86_64/bionic/vfork.S \
 
 libc_bionic_src_files_x86_64 += \
     arch-x86_64/string/sse2-memcpy-slm.S \
diff --git a/libc/bionic/locale.cpp b/libc/bionic/locale.cpp
index 363140e..4c3fd7f 100644
--- a/libc/bionic/locale.cpp
+++ b/libc/bionic/locale.cpp
@@ -30,10 +30,27 @@
 #include <pthread.h>
 #include <stdlib.h>
 
+#include "private/bionic_macros.h"
+
 // We currently support a single locale, the "C" locale (also known as "POSIX").
 
+static bool __bionic_current_locale_is_utf8 = true;
+
 struct __locale_t {
-  // Because we only support one locale, these are just tokens with no data.
+  size_t mb_cur_max;
+
+  __locale_t(size_t mb_cur_max) : mb_cur_max(mb_cur_max) {
+  }
+
+  __locale_t(const __locale_t* other) {
+    if (other == LC_GLOBAL_LOCALE) {
+      mb_cur_max = __bionic_current_locale_is_utf8 ? 4 : 1;
+    } else {
+      mb_cur_max = other->mb_cur_max;
+    }
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(__locale_t);
 };
 
 static pthread_once_t g_locale_once = PTHREAD_ONCE_INIT;
@@ -75,7 +92,14 @@
   g_locale.int_n_sign_posn = CHAR_MAX;
 }
 
-static bool __bionic_current_locale_is_utf8 = false;
+size_t __mb_cur_max() {
+  locale_t l = reinterpret_cast<locale_t>(pthread_getspecific(g_uselocale_key));
+  if (l == nullptr || l == LC_GLOBAL_LOCALE) {
+    return __bionic_current_locale_is_utf8 ? 4 : 1;
+  } else {
+    return l->mb_cur_max;
+  }
+}
 
 static bool __is_supported_locale(const char* locale) {
   return (strcmp(locale, "") == 0 ||
@@ -85,25 +109,17 @@
           strcmp(locale, "POSIX") == 0);
 }
 
-static locale_t __new_locale() {
-  return reinterpret_cast<locale_t>(malloc(sizeof(__locale_t)));
-}
-
 lconv* localeconv() {
   pthread_once(&g_locale_once, __locale_init);
   return &g_locale;
 }
 
 locale_t duplocale(locale_t l) {
-  locale_t clone = __new_locale();
-  if (clone != NULL && l != LC_GLOBAL_LOCALE) {
-    *clone = *l;
-  }
-  return clone;
+  return new __locale_t(l);
 }
 
 void freelocale(locale_t l) {
-  free(l);
+  delete l;
 }
 
 locale_t newlocale(int category_mask, const char* locale_name, locale_t /*base*/) {
@@ -118,7 +134,7 @@
     return NULL;
   }
 
-  return __new_locale();
+  return new __locale_t(strstr(locale_name, "UTF-8") != NULL ? 4 : 1);
 }
 
 char* setlocale(int category, const char* locale_name) {
diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp
index e3e640a..0c72019 100644
--- a/libc/bionic/ndk_cruft.cpp
+++ b/libc/bionic/ndk_cruft.cpp
@@ -225,6 +225,7 @@
   return syscall(__NR_tkill, tid, sig);
 }
 
+// This was removed from POSIX 2008.
 extern "C" wchar_t* wcswcs(wchar_t* haystack, wchar_t* needle) {
   return wcsstr(haystack, needle);
 }
@@ -267,7 +268,7 @@
   return r.rlim_cur;
 }
 
-// Only used by ftime, which was removed from POSIX.
+// Only used by ftime, which was removed from POSIX 2008.
 struct timeb {
   time_t          time;
   unsigned short  millitm;
diff --git a/libc/bionic/wchar.cpp b/libc/bionic/wchar.cpp
index 438ce03..524ba07 100644
--- a/libc/bionic/wchar.cpp
+++ b/libc/bionic/wchar.cpp
@@ -84,6 +84,7 @@
       if (static_cast<uint8_t>((*src)[i]) < 0x80) {
         // Fast path for plain ASCII characters.
         if ((*src)[i] == '\0') {
+          *src = nullptr;
           return reset_and_return(o, state);
         }
         r = 1;
@@ -96,6 +97,7 @@
           return reset_and_return_illegal(EILSEQ, state);
         }
         if (r == 0) {
+          *src = nullptr;
           return reset_and_return(o, state);
         }
       }
@@ -118,7 +120,8 @@
       dst[o] = (*src)[i];
       r = 1;
       if ((*src)[i] == '\0') {
-        break;
+        *src = nullptr;
+        return reset_and_return(o, state);
       }
     } else {
       r = mbrtowc(dst + o, *src + i, nmc - i, state);
diff --git a/libc/dns/include/resolv_netid.h b/libc/dns/include/resolv_netid.h
index f1b8892..e5521b8 100644
--- a/libc/dns/include/resolv_netid.h
+++ b/libc/dns/include/resolv_netid.h
@@ -61,6 +61,9 @@
 extern void _resolv_set_nameservers_for_net(unsigned netid,
     const char** servers, int numservers, const char *domains) __used_in_netd;
 
+/* flush the cache associated with a certain network */
+extern void _resolv_flush_cache_for_net(unsigned netid) __used_in_netd;
+
 /* delete the cache associated with a certain network */
 extern void _resolv_delete_cache_for_net(unsigned netid) __used_in_netd;
 
diff --git a/libc/dns/resolv/res_cache.c b/libc/dns/resolv/res_cache.c
index c934b4e..419b748 100644
--- a/libc/dns/resolv/res_cache.c
+++ b/libc/dns/resolv/res_cache.c
@@ -1831,6 +1831,17 @@
     return cache;
 }
 
+void
+_resolv_flush_cache_for_net(unsigned netid)
+{
+    pthread_once(&_res_cache_once, _res_cache_init);
+    pthread_mutex_lock(&_res_cache_list_lock);
+
+    _flush_cache_for_net_locked(netid);
+
+    pthread_mutex_unlock(&_res_cache_list_lock);
+}
+
 static void
 _flush_cache_for_net_locked(unsigned netid)
 {
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index e29fdba..a5eb3d1 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -161,7 +161,8 @@
 extern int	wctomb(char *, wchar_t);
 extern size_t	wcstombs(char *, const wchar_t *, size_t);
 
-#define MB_CUR_MAX 4U
+extern size_t __mb_cur_max(void);
+#define MB_CUR_MAX __mb_cur_max()
 
 __END_DECLS
 
diff --git a/libc/include/string.h b/libc/include/string.h
index af1c0c1..8df68e3 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -94,9 +94,6 @@
 
 #if defined(__BIONIC_FORTIFY)
 
-__errordecl(__memcpy_dest_size_error, "memcpy: prevented write past end of buffer");
-__errordecl(__memcpy_src_size_error, "memcpy: prevented read past end of buffer");
-
 __BIONIC_FORTIFY_INLINE
 void* memcpy(void* __restrict dest, const void* __restrict src, size_t copy_amount) {
     char *d = (char *) dest;
@@ -104,14 +101,6 @@
     size_t s_len = __bos0(s);
     size_t d_len = __bos0(d);
 
-    if (__builtin_constant_p(copy_amount) && (copy_amount > d_len)) {
-        __memcpy_dest_size_error();
-    }
-
-    if (__builtin_constant_p(copy_amount) && (copy_amount > s_len)) {
-        __memcpy_src_size_error();
-    }
-
     return __builtin___memcpy_chk(dest, src, copy_amount, d_len);
 }
 
@@ -130,16 +119,12 @@
     return __builtin___strcpy_chk(dest, src, __bos(dest));
 }
 
-__errordecl(__stpncpy_error, "stpncpy: prevented write past end of buffer");
 extern char* __stpncpy_chk2(char* __restrict, const char* __restrict, size_t, size_t, size_t);
 
 __BIONIC_FORTIFY_INLINE
 char* stpncpy(char* __restrict dest, const char* __restrict src, size_t n) {
     size_t bos_dest = __bos(dest);
     size_t bos_src = __bos(src);
-    if (__builtin_constant_p(n) && (n > bos_dest)) {
-        __stpncpy_error();
-    }
 
     if (bos_src == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
         return __builtin___stpncpy_chk(dest, src, n, bos_dest);
@@ -157,16 +142,12 @@
     return __stpncpy_chk2(dest, src, n, bos_dest, bos_src);
 }
 
-__errordecl(__strncpy_error, "strncpy: prevented write past end of buffer");
 extern char* __strncpy_chk2(char* __restrict, const char* __restrict, size_t, size_t, size_t);
 
 __BIONIC_FORTIFY_INLINE
 char* strncpy(char* __restrict dest, const char* __restrict src, size_t n) {
     size_t bos_dest = __bos(dest);
     size_t bos_src = __bos(src);
-    if (__builtin_constant_p(n) && (n > bos_dest)) {
-        __strncpy_error();
-    }
 
     if (bos_src == __BIONIC_FORTIFY_UNKNOWN_SIZE) {
         return __builtin___strncpy_chk(dest, src, n, bos_dest);
@@ -201,7 +182,6 @@
 
 extern size_t __strlcpy_real(char* __restrict, const char* __restrict, size_t)
     __asm__(__USER_LABEL_PREFIX__ "strlcpy");
-__errordecl(__strlcpy_error, "strlcpy: prevented write past end of buffer");
 extern size_t __strlcpy_chk(char *, const char *, size_t, size_t);
 
 __BIONIC_FORTIFY_INLINE
@@ -219,12 +199,6 @@
     if (__builtin_constant_p(size) && (size <= bos)) {
         return __strlcpy_real(dest, src, size);
     }
-
-    // Compiler can prove, at compile time, that the passed in size
-    // is always > the actual object size. Force a compiler error.
-    if (__builtin_constant_p(size) && (size > bos)) {
-        __strlcpy_error();
-    }
 #endif /* !defined(__clang__) */
 
     return __strlcpy_chk(dest, src, size, bos);
@@ -232,7 +206,6 @@
 
 extern size_t __strlcat_real(char* __restrict, const char* __restrict, size_t)
     __asm__(__USER_LABEL_PREFIX__ "strlcat");
-__errordecl(__strlcat_error, "strlcat: prevented write past end of buffer");
 extern size_t __strlcat_chk(char* __restrict, const char* __restrict, size_t, size_t);
 
 
@@ -251,12 +224,6 @@
     if (__builtin_constant_p(size) && (size <= bos)) {
         return __strlcat_real(dest, src, size);
     }
-
-    // Compiler can prove, at compile time, that the passed in size
-    // is always > the actual object size. Force a compiler error.
-    if (__builtin_constant_p(size) && (size > bos)) {
-        __strlcat_error();
-    }
 #endif /* !defined(__clang__) */
 
     return __strlcat_chk(dest, src, size, bos);
diff --git a/libc/tools/zoneinfo/ZoneCompactor.java b/libc/tools/zoneinfo/ZoneCompactor.java
index f47afd1..bf3153e 100644
--- a/libc/tools/zoneinfo/ZoneCompactor.java
+++ b/libc/tools/zoneinfo/ZoneCompactor.java
@@ -1,9 +1,6 @@
 
 import java.io.*;
-import java.nio.ByteOrder;
 import java.util.*;
-import libcore.io.BufferIterator;
-import libcore.util.ZoneInfo;
 
 // usage: java ZoneCompiler <setup file> <data directory> <output directory> <tzdata version>
 //
@@ -27,66 +24,20 @@
 //
 
 public class ZoneCompactor {
-  public static class ByteArrayBufferIteratorBE extends BufferIterator {
-    private final byte[] bytes;
-    private int offset = 0;
-
-    public ByteArrayBufferIteratorBE(byte[] bytes) {
-      this.bytes = bytes;
-      this.offset = 0;
-    }
-
-    public void seek(int offset) {
-      this.offset = offset;
-    }
-
-    public void skip(int byteCount) {
-      this.offset += byteCount;
-    }
-
-    public void readByteArray(byte[] dst, int dstOffset, int byteCount) {
-      System.arraycopy(bytes, offset, dst, dstOffset, byteCount);
-      offset += byteCount;
-    }
-
-    public byte readByte() {
-      return bytes[offset++];
-    }
-
-    public int readInt() {
-      return ((readByte() & 0xff) << 24) | ((readByte() & 0xff) << 16) | ((readByte() & 0xff) << 8) | (readByte() & 0xff);
-    }
-
-    public void readIntArray(int[] dst, int dstOffset, int intCount) {
-      for (int i = 0; i < intCount; ++i) {
-        dst[dstOffset++] = readInt();
-      }
-    }
-
-    public short readShort() {
-      throw new UnsupportedOperationException();
-    }
-  }
-
-  // Maximum number of characters in a zone name, including '\0' terminator
+  // Maximum number of characters in a zone name, including '\0' terminator.
   private static final int MAXNAME = 40;
 
-  // Zone name synonyms
+  // Zone name synonyms.
   private Map<String,String> links = new HashMap<String,String>();
 
-  // File starting bytes by zone name
-  private Map<String,Integer> starts = new HashMap<String,Integer>();
+  // File offsets by zone name.
+  private Map<String,Integer> offsets = new HashMap<String,Integer>();
 
-  // File lengths by zone name
+  // File lengths by zone name.
   private Map<String,Integer> lengths = new HashMap<String,Integer>();
 
-  // Raw GMT offsets by zone name
-  private Map<String,Integer> offsets = new HashMap<String,Integer>();
-  private int start = 0;
-
-  // Concatenate the contents of 'inFile' onto 'out'
-  // and return the contents as a byte array.
-  private static byte[] copyFile(File inFile, OutputStream out) throws Exception {
+  // Concatenate the contents of 'inFile' onto 'out'.
+  private static void copyFile(File inFile, OutputStream out) throws Exception {
     byte[] ret = new byte[0];
 
     InputStream in = new FileInputStream(inFile);
@@ -104,14 +55,14 @@
       ret = nret;
     }
     out.flush();
-    return ret;
   }
 
   public ZoneCompactor(String setupFile, String dataDirectory, String zoneTabFile, String outputDirectory, String version) throws Exception {
-    // Read the setup file, and concatenate all the data.
+    // Read the setup file and concatenate all the data.
     ByteArrayOutputStream allData = new ByteArrayOutputStream();
     BufferedReader reader = new BufferedReader(new FileReader(setupFile));
     String s;
+    int offset = 0;
     while ((s = reader.readLine()) != null) {
       s = s.trim();
       if (s.startsWith("Link")) {
@@ -125,16 +76,11 @@
         if (link == null) {
           File sourceFile = new File(dataDirectory, s);
           long length = sourceFile.length();
-          starts.put(s, start);
+          offsets.put(s, offset);
           lengths.put(s, (int) length);
 
-          start += length;
-          byte[] data = copyFile(sourceFile, allData);
-
-          BufferIterator it = new ByteArrayBufferIteratorBE(data);
-          TimeZone tz = ZoneInfo.makeTimeZone(s, it);
-          int gmtOffset = tz.getRawOffset();
-          offsets.put(s, gmtOffset);
+          offset += length;
+          copyFile(sourceFile, allData);
         }
       }
     }
@@ -146,9 +92,8 @@
       String from = it.next();
       String to = links.get(from);
 
-      starts.put(from, starts.get(to));
-      lengths.put(from, lengths.get(to));
       offsets.put(from, offsets.get(to));
+      lengths.put(from, lengths.get(to));
     }
 
     // Create/truncate the destination file.
@@ -178,7 +123,7 @@
 
     // Write the index.
     ArrayList<String> sortedOlsonIds = new ArrayList<String>();
-    sortedOlsonIds.addAll(starts.keySet());
+    sortedOlsonIds.addAll(offsets.keySet());
     Collections.sort(sortedOlsonIds);
     it = sortedOlsonIds.iterator();
     while (it.hasNext()) {
@@ -188,9 +133,9 @@
       }
 
       f.write(toAscii(new byte[MAXNAME], zoneName));
-      f.writeInt(starts.get(zoneName));
-      f.writeInt(lengths.get(zoneName));
       f.writeInt(offsets.get(zoneName));
+      f.writeInt(lengths.get(zoneName));
+      f.writeInt(0); // Used to be raw GMT offset. No longer used.
     }
 
     int data_offset = (int) f.getFilePointer();
diff --git a/libc/tools/zoneinfo/update-tzdata.py b/libc/tools/zoneinfo/update-tzdata.py
index 6b69a5a..e800e8f 100755
--- a/libc/tools/zoneinfo/update-tzdata.py
+++ b/libc/tools/zoneinfo/update-tzdata.py
@@ -115,7 +115,7 @@
   print 'Configuring ICU tools...'
   subprocess.check_call(['%s/runConfigureICU' % icu_dir, 'Linux'])
   print 'Making ICU tools...'
-  subprocess.check_call(['make', '-j6'])
+  subprocess.check_call(['make', '-j32'])
 
   # Run the ICU tools.
   os.chdir('tools/tzcode')
@@ -169,11 +169,8 @@
   WriteSetupFile()
 
   print 'Calling ZoneCompactor to update bionic to %s...' % new_version
-  libcore_src_dir = '%s/../libcore/luni/src/main/java/' % bionic_dir
   subprocess.check_call(['javac', '-d', '.',
-                         '%s/ZoneCompactor.java' % bionic_libc_tools_zoneinfo_dir,
-                         '%s/libcore/util/ZoneInfo.java' % libcore_src_dir,
-                         '%s/libcore/io/BufferIterator.java' % libcore_src_dir])
+                         '%s/ZoneCompactor.java' % bionic_libc_tools_zoneinfo_dir])
   subprocess.check_call(['java', 'ZoneCompactor',
                          'setup', 'data', 'extracted/zone.tab',
                          bionic_libc_zoneinfo_dir, new_version])
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 5e4e962..3bbed90 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -2210,7 +2210,7 @@
     char buf[NAME_LENGTH];
     int32_t start;
     int32_t length;
-    int32_t raw_gmt_offset;
+    int32_t unused; // Was raw GMT offset; always 0 since tzdata2014f (L).
   };
 
   size_t id_count = (ntohl(header.data_offset) - ntohl(header.index_offset)) / sizeof(struct index_entry_t);
diff --git a/libc/zoneinfo/tzdata b/libc/zoneinfo/tzdata
index a5ae1f6..9547f58 100644
--- a/libc/zoneinfo/tzdata
+++ b/libc/zoneinfo/tzdata
Binary files differ
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index efb829e..8ebf357 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -111,8 +111,7 @@
       sym = dlsym_linear_lookup(symbol, &found, caller_si->next, caller_si);
     }
   } else {
-    found = reinterpret_cast<soinfo*>(handle);
-    sym = dlsym_handle_lookup(found, symbol, caller_si);
+    sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, symbol, caller_si);
   }
 
   if (sym != NULL && sym->st_shndx != 0) {
diff --git a/linker/linked_list.h b/linker/linked_list.h
index 52af0f1..7f8c901 100644
--- a/linker/linked_list.h
+++ b/linker/linked_list.h
@@ -31,13 +31,45 @@
 template<typename T, typename Allocator>
 class LinkedList {
  public:
-  LinkedList() : head_(nullptr) {}
+  LinkedList() : head_(nullptr), tail_(nullptr) {}
 
   void push_front(T* const element) {
     LinkedListEntry<T>* new_entry = Allocator::alloc();
     new_entry->next = head_;
     new_entry->element = element;
     head_ = new_entry;
+    if (tail_ == nullptr) {
+      tail_ = new_entry;
+    }
+  }
+
+  void push_back(T* const element) {
+    LinkedListEntry<T>* new_entry = Allocator::alloc();
+    new_entry->next = nullptr;
+    new_entry->element = element;
+    if (tail_ == nullptr) {
+      tail_ = head_ = new_entry;
+    } else {
+      tail_->next = new_entry;
+      tail_ = new_entry;
+    }
+  }
+
+  T* pop_front() {
+    if (head_ == nullptr) {
+      return nullptr;
+    }
+
+    LinkedListEntry<T>* entry = head_;
+    T* element = entry->element;
+    head_ = entry->next;
+    Allocator::free(entry);
+
+    if (head_ == nullptr) {
+      tail_ = nullptr;
+    }
+
+    return element;
   }
 
   void clear() {
@@ -46,6 +78,8 @@
       head_ = head_->next;
       Allocator::free(p);
     }
+
+    tail_ = nullptr;
   }
 
   template<typename F>
@@ -68,6 +102,7 @@
 
  private:
   LinkedListEntry<T>* head_;
+  LinkedListEntry<T>* tail_;
   DISALLOW_COPY_AND_ASSIGN(LinkedList);
 };
 
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 59b9938..f8b35d7 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -469,6 +469,10 @@
     }
   }
 
+  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
+             name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
+
+
   return NULL;
 }
 
@@ -585,18 +589,43 @@
     return NULL;
 }
 
-/* This is used by dlsym(3).  It performs symbol lookup only within the
-   specified soinfo object and not in any of its dependencies.
+// Another soinfo list allocator to use in dlsym. We don't reuse
+// SoinfoListAllocator because it is write-protected most of the time.
+static LinkerAllocator<LinkedListEntry<soinfo>> g_soinfo_list_allocator_rw;
+class SoinfoListAllocatorRW {
+ public:
+  static LinkedListEntry<soinfo>* alloc() {
+    return g_soinfo_list_allocator_rw.alloc();
+  }
 
-   TODO: Only looking in the specified soinfo seems wrong. dlsym(3) says
-   that it should do a breadth first search through the dependency
-   tree. This agrees with the ELF spec (aka System V Application
-   Binary Interface) where in Chapter 5 it discuss resolving "Shared
-   Object Dependencies" in breadth first search order.
- */
-ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name, soinfo* caller) {
-    return soinfo_elf_lookup(si, elfhash(name), name,
-        caller == si ? SymbolLookupScope::kAllowLocal : SymbolLookupScope::kExcludeLocal);
+  static void free(LinkedListEntry<soinfo>* ptr) {
+    g_soinfo_list_allocator_rw.free(ptr);
+  }
+};
+
+// This is used by dlsym(3).  It performs symbol lookup only within the
+// specified soinfo object and its dependencies in breadth first order.
+ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name, soinfo* caller) {
+  LinkedList<soinfo, SoinfoListAllocatorRW> visit_list;
+  visit_list.push_back(si);
+  soinfo* current_soinfo;
+  while ((current_soinfo = visit_list.pop_front()) != nullptr) {
+    ElfW(Sym)* result = soinfo_elf_lookup(current_soinfo, elfhash(name), name,
+        caller == current_soinfo ? SymbolLookupScope::kAllowLocal : SymbolLookupScope::kExcludeLocal);
+
+    if (result != nullptr) {
+      *found = current_soinfo;
+      visit_list.clear();
+      return result;
+    }
+
+    current_soinfo->get_children().for_each([&](soinfo* child) {
+      visit_list.push_back(child);
+    });
+  }
+
+  visit_list.clear();
+  return nullptr;
 }
 
 /* This is used by dlsym(3) to performs a global symbol lookup. If the
diff --git a/linker/linker.h b/linker/linker.h
index e1112e6..03672b2 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -238,7 +238,7 @@
 soinfo* find_containing_library(const void* addr);
 
 ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr);
-ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name, soinfo* caller_si);
+ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name, soinfo* caller_si);
 
 void debuggerd_init();
 extern "C" abort_msg_t* g_abort_message;
diff --git a/linker/tests/linked_list_test.cpp b/linker/tests/linked_list_test.cpp
index 31ec7d5..b9816fa 100644
--- a/linker/tests/linked_list_test.cpp
+++ b/linker/tests/linked_list_test.cpp
@@ -95,3 +95,23 @@
   ASSERT_TRUE(free_called);
   ASSERT_EQ("", test_list_to_string(list));
 }
+
+TEST(linked_list, push_pop) {
+  test_list_t list;
+  list.push_front("b");
+  list.push_front("a");
+  ASSERT_EQ("ab", test_list_to_string(list));
+  list.push_back("c");
+  ASSERT_EQ("abc", test_list_to_string(list));
+  ASSERT_EQ("a", list.pop_front());
+  ASSERT_EQ("bc", test_list_to_string(list));
+  ASSERT_EQ("b", list.pop_front());
+  ASSERT_EQ("c", test_list_to_string(list));
+  ASSERT_EQ("c", list.pop_front());
+  ASSERT_EQ("", test_list_to_string(list));
+  ASSERT_TRUE(list.pop_front() == nullptr);
+  list.push_back("r");
+  ASSERT_EQ("r", test_list_to_string(list));
+  ASSERT_EQ("r", list.pop_front());
+  ASSERT_TRUE(list.pop_front() == nullptr);
+}
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index f056fb6..9bc2557 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -62,10 +62,9 @@
   ASSERT_EQ(0, dlclose(self));
 }
 
-#if !defined(__LP64__)
-// Current compiler/static linker used for aarch64
-// platform optimizes LOCAL PROTECTED symbol
-// in libtest_local_symbol.so out of existence
+#if defined(__arm__)
+// This seems to be working only for arm.
+// Others platforms optimize LOCAL PROTECTED symbols.
 TEST(dlfcn, dlsym_local_symbol) {
   void* handle = dlopen("libtest_local_symbol.so", RTLD_NOW);
   ASSERT_TRUE(handle != NULL);
@@ -78,9 +77,23 @@
   f = reinterpret_cast<uint32_t (*)(void)>(dlsym(handle, "dlsym_local_symbol_get_taxicab_number_using_dlsym"));
   ASSERT_TRUE(f != NULL);
   ASSERT_EQ(1729U, f());
+  dlclose(handle);
 }
 #endif
 
+TEST(dlfcn, dlsym_with_dependencies) {
+  void* handle = dlopen("libtest_with_dependency.so", RTLD_NOW);
+  ASSERT_TRUE(handle != NULL);
+  dlerror();
+  // This symbol is in DT_NEEDED library.
+  void* sym = dlsym(handle, "getRandomNumber");
+  ASSERT_TRUE(sym != NULL);
+  int (*fn)(void);
+  fn = reinterpret_cast<int (*)(void)>(sym);
+  EXPECT_EQ(4, fn());
+  dlclose(handle);
+}
+
 TEST(dlfcn, dlopen_noload) {
   void* handle = dlopen("libtest_simple.so", RTLD_NOW | RTLD_NOLOAD);
   ASSERT_TRUE(handle == NULL);
diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk
index a374e48..7ed3e7b 100644
--- a/tests/libs/Android.mk
+++ b/tests/libs/Android.mk
@@ -102,6 +102,19 @@
 include $(TEST_PATH)/Android.build.mk
 
 # -----------------------------------------------------------------------------
+# Library with dependency used by dlfcn tests
+# -----------------------------------------------------------------------------
+libtest_with_dependency_src_files := \
+    dlopen_testlib_simple.cpp
+
+libtest_with_dependency_shared_libraries := libdlext_test
+
+module := libtest_with_dependency
+build_type := target
+build_target := SHARED_LIBRARY
+include $(TEST_PATH)/Android.build.mk
+
+# -----------------------------------------------------------------------------
 # Library used to test local symbol lookup
 # -----------------------------------------------------------------------------
 libtest_local_symbol_src_files := \
diff --git a/tests/locale_test.cpp b/tests/locale_test.cpp
index 7d063f9..325f6ce 100644
--- a/tests/locale_test.cpp
+++ b/tests/locale_test.cpp
@@ -48,8 +48,8 @@
 }
 
 TEST(locale, setlocale) {
-  EXPECT_STREQ("C", setlocale(LC_ALL, NULL));
-  EXPECT_STREQ("C", setlocale(LC_CTYPE, NULL));
+  EXPECT_STREQ("C.UTF-8", setlocale(LC_ALL, NULL));
+  EXPECT_STREQ("C.UTF-8", setlocale(LC_CTYPE, NULL));
 
   errno = 0;
   EXPECT_EQ(NULL, setlocale(-1, NULL));
@@ -105,3 +105,20 @@
 
   EXPECT_EQ(n, uselocale(NULL));
 }
+
+TEST(locale, mb_cur_max) {
+  // We can't reliably test the behavior with setlocale(3) or the behavior for
+  // initial program conditions because (unless we're the only test that was
+  // run), another test has almost certainly called uselocale(3) in this thread.
+  // See b/16685652.
+  locale_t cloc = newlocale(LC_ALL, "C", 0);
+  locale_t cloc_utf8 = newlocale(LC_ALL, "C.UTF-8", 0);
+
+  uselocale(cloc);
+  ASSERT_EQ(1U, MB_CUR_MAX);
+  uselocale(cloc_utf8);
+  ASSERT_EQ(4U, MB_CUR_MAX);
+
+  freelocale(cloc);
+  freelocale(cloc_utf8);
+}
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 18dae9c..bb86509 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -427,6 +427,9 @@
 }
 
 TEST(stdio, snprintf_utf8_15439554) {
+  locale_t cloc = newlocale(LC_ALL, "C.UTF-8", 0);
+  uselocale(cloc);
+
   // http://b/15439554
   char buf[BUFSIZ];
 
@@ -442,6 +445,8 @@
   // 4-byte character.
   snprintf(buf, sizeof(buf), "%d\xf0\xa4\xad\xa2%d", 1, 2);
   EXPECT_STREQ("1𤭢2", buf);
+
+  freelocale(cloc);
 }
 
 TEST(stdio, fprintf_failures_7229520) {
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index f052ce6..d02c4bf 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -351,7 +351,7 @@
   // Check that we didn't clobber the rest of out.
   ASSERT_EQ(L'x', out[3]);
   // Check that valid has advanced to the end of the string.
-  ASSERT_EQ(L'\0', *valid);
+  ASSERT_EQ(nullptr, valid);
 
   const char* invalid = "A" "\xc2\x20" "ef";
   ASSERT_EQ(static_cast<size_t>(-1), mbsrtowcs(out, &invalid, 4, ps));