Merge "Revert "Added a bionic systrace class and tracing to pthread_mutex.cpp."" into lmp-dev
diff --git a/libc/Android.mk b/libc/Android.mk
index 1fb5e84..d641d89 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -45,7 +45,6 @@
     bionic/fts.c \
     bionic/gethostname.c \
     bionic/getpriority.c \
-    bionic/getpt.c \
     bionic/if_indextoname.c \
     bionic/if_nametoindex.c \
     bionic/initgroups.c \
@@ -53,8 +52,6 @@
     bionic/isatty.c \
     bionic/memmem.c \
     bionic/pathconf.c \
-    bionic/ptsname.c \
-    bionic/ptsname_r.c \
     bionic/pututline.c \
     bionic/sched_cpualloc.c \
     bionic/sched_cpucount.c \
@@ -63,7 +60,6 @@
     bionic/siginterrupt.c \
     bionic/sigsetmask.c \
     bionic/system_properties_compat.c \
-    bionic/unlockpt.c \
     stdio/snprintf.c\
     stdio/sprintf.c \
 
@@ -174,6 +170,7 @@
     bionic/pthread_setschedparam.cpp \
     bionic/pthread_sigmask.cpp \
     bionic/ptrace.cpp \
+    bionic/pty.cpp \
     bionic/raise.cpp \
     bionic/rand.cpp \
     bionic/readlink.cpp \
diff --git a/libc/bionic/getpt.c b/libc/bionic/getpt.c
deleted file mode 100644
index 8bb5c11..0000000
--- a/libc/bionic/getpt.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2008 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 <unistd.h>
-#include <fcntl.h>
-
-int getpt(void)
-{
-  return open("/dev/ptmx", O_RDWR|O_NOCTTY);
-}
diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp
index e656a12..d0172ed 100644
--- a/libc/bionic/libc_logging.cpp
+++ b/libc/bionic/libc_logging.cpp
@@ -620,7 +620,11 @@
   out_vformat(os, format, args);
 
   // log to stderr for the benefit of "adb shell" users.
-  write(2, msg, strlen(msg));
+  struct iovec iov[2] = {
+    {msg, strlen(msg)},
+    {const_cast<void*>(static_cast<const void*>("\n")), 1},
+  };
+  writev(2, iov, 2);
 
   // Log to the log for the benefit of regular app developers (whose stdout and stderr are closed).
   __libc_write_log(ANDROID_LOG_FATAL, "libc", msg);
diff --git a/libc/bionic/locale.cpp b/libc/bionic/locale.cpp
index 5a1da43..363140e 100644
--- a/libc/bionic/locale.cpp
+++ b/libc/bionic/locale.cpp
@@ -79,7 +79,9 @@
 
 static bool __is_supported_locale(const char* locale) {
   return (strcmp(locale, "") == 0 ||
-          strcmp(locale, "C") == 0 || strcmp(locale, "C.UTF-8") == 0 ||
+          strcmp(locale, "C") == 0 ||
+          strcmp(locale, "C.UTF-8") == 0 ||
+          strcmp(locale, "en_US.UTF-8") == 0 ||
           strcmp(locale, "POSIX") == 0);
 }
 
diff --git a/libc/bionic/ptsname.c b/libc/bionic/ptsname.c
deleted file mode 100644
index 24d5d30..0000000
--- a/libc/bionic/ptsname.c
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2008 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 <stdio.h>
-#include <unistd.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-
-/* not thread-safe */
-char*  ptsname( int fd )
-{
-    unsigned int  pty_num;
-    static char   buff[64];
-
-    if ( ioctl( fd, TIOCGPTN, &pty_num ) != 0 )
-        return NULL;
-
-    snprintf( buff, sizeof(buff), "/dev/pts/%u", pty_num );
-    return buff;
-}
diff --git a/libc/bionic/ptsname_r.c b/libc/bionic/ptsname_r.c
deleted file mode 100644
index 2fa4c3d..0000000
--- a/libc/bionic/ptsname_r.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2008 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 <stdio.h>
-#include <unistd.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-#include <string.h>
-
-int    ptsname_r( int  fd, char*  buf, size_t  buflen)
-{
-    unsigned int  pty_num;
-    char          buff[64];
-    int           len;
-
-    if (buf == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    if ( ioctl( fd, TIOCGPTN, &pty_num ) != 0 ) {
-        errno = ENOTTY;
-        return -1;
-    }
-
-    len = snprintf( buff, sizeof(buff), "/dev/pts/%u", pty_num );
-    if (len+1 > (int)buflen) {
-        errno = ERANGE;
-        return -1;
-    }
-    memcpy( buf, buff, len+1 );
-    return 0;
-}
diff --git a/libc/bionic/pty.cpp b/libc/bionic/pty.cpp
new file mode 100644
index 0000000..995e006
--- /dev/null
+++ b/libc/bionic/pty.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+int getpt(void) {
+  return posix_openpt(O_RDWR|O_NOCTTY);
+}
+
+int grantpt(int) {
+  return 0;
+}
+
+int posix_openpt(int flags) {
+  return open("/dev/ptmx", flags);
+}
+
+char* ptsname(int fd) {
+  static char buf[64];
+  return ptsname_r(fd, buf, sizeof(buf)) == 0 ? buf : NULL;
+}
+
+int ptsname_r(int fd, char* buf, size_t len) {
+  if (buf == NULL) {
+    errno = EINVAL;
+    return errno;
+  }
+
+  unsigned int pty_num;
+  if (ioctl(fd, TIOCGPTN, &pty_num) != 0) {
+    errno = ENOTTY;
+    return errno;
+  }
+
+  if (snprintf(buf, len, "/dev/pts/%u", pty_num) >= static_cast<int>(len)) {
+    errno = ERANGE;
+    return errno;
+  }
+
+  return 0;
+}
+
+char* ttyname(int fd) {
+  static char buf[64];
+  return ttyname_r(fd, buf, sizeof(buf)) == 0 ? buf : NULL;
+}
+
+int ttyname_r(int fd, char* buf, size_t len) {
+  if (buf == NULL) {
+    errno = EINVAL;
+    return errno;
+  }
+
+  if (!isatty(fd)) {
+    return errno;
+  }
+
+  char path[64];
+  snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+
+  ssize_t count = readlink(path, buf, len);
+  if (count == -1) {
+    return errno;
+  }
+  if (static_cast<size_t>(count) == len) {
+    errno = ERANGE;
+    return errno;
+  }
+  buf[count] = '\0';
+  return 0;
+}
+
+int unlockpt(int fd) {
+  int unlock = 0;
+  return ioctl(fd, TIOCSPTLCK, &unlock);
+}
diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp
index 0937e9c..b1e38be 100644
--- a/libc/bionic/stubs.cpp
+++ b/libc/bionic/stubs.cpp
@@ -444,16 +444,6 @@
   UNIMPLEMENTED;
 }
 
-char* ttyname(int /*fd*/) { // NOLINT: implementing bad function.
-  UNIMPLEMENTED;
-  return NULL;
-}
-
-int ttyname_r(int /*fd*/, char* /*buf*/, size_t /*buflen*/) {
-  UNIMPLEMENTED;
-  return -ERANGE;
-}
-
 char* getusershell() {
   UNIMPLEMENTED;
   return NULL;
diff --git a/libc/bionic/unlockpt.c b/libc/bionic/unlockpt.c
deleted file mode 100644
index 998b7a3..0000000
--- a/libc/bionic/unlockpt.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2008 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 <unistd.h>
-#include <sys/ioctl.h>
-
-int  unlockpt( int  fd )
-{
-    int  unlock = 0;
-
-    return ioctl( fd, TIOCSPTLCK, &unlock );
-}
diff --git a/libc/bionic/wchar.cpp b/libc/bionic/wchar.cpp
index ecb8b33..438ce03 100644
--- a/libc/bionic/wchar.cpp
+++ b/libc/bionic/wchar.cpp
@@ -116,11 +116,10 @@
     if (static_cast<uint8_t>((*src)[i]) < 0x80) {
       // Fast path for plain ASCII characters.
       dst[o] = (*src)[i];
-      if ((*src)[i] == '\0') {
-        *src = NULL;
-        return reset_and_return_illegal(EILSEQ, state);
-      }
       r = 1;
+      if ((*src)[i] == '\0') {
+        break;
+      }
     } else {
       r = mbrtowc(dst + o, *src + i, nmc - i, state);
       if (r == __MB_ERR_ILLEGAL_SEQUENCE) {
diff --git a/libc/include/stdint.h b/libc/include/stdint.h
index f34843c..a6f8505 100644
--- a/libc/include/stdint.h
+++ b/libc/include/stdint.h
@@ -203,7 +203,7 @@
 
 #if defined(__WINT_UNSIGNED__)
 #  define WINT_MAX       UINT32_MAX
-#  define WINT_MIN       UINT32_MIN
+#  define WINT_MIN       0
 #else
 #  define WINT_MAX       INT32_MAX
 #  define WINT_MIN       INT32_MIN
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index 195765a..857d631 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -120,18 +120,12 @@
 char* setstate(char*);
 void srandom(unsigned int);
 
-/* Basic PTY functions.  These only work if devpts is mounted! */
-
-extern int    unlockpt(int);
-extern char*  ptsname(int);
-extern int    ptsname_r(int, char*, size_t);
-extern int    getpt(void);
-
-static __inline__ int grantpt(int __fd __attribute((unused)))
-{
-  (void)__fd;
-  return 0;     /* devpts does this all for us! */
-}
+int getpt(void);
+int grantpt(int);
+int posix_openpt(int);
+char* ptsname(int) __warnattr("ptsname is not thread-safe; use ptsname_r instead");
+int ptsname_r(int, char*, size_t);
+int unlockpt(int);
 
 typedef struct {
     int  quot;
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index 12e6257..82c53e8 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -170,7 +170,7 @@
 extern int optind, opterr, optopt;
 
 extern int isatty(int);
-extern char* ttyname(int);
+extern char* ttyname(int) __warnattr("ttyname is not thread-safe; use ttyname_r instead");
 extern int ttyname_r(int, char*, size_t);
 
 extern int  acct(const char*  filepath);
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/stdlib_test.cpp b/tests/stdlib_test.cpp
index 6d29421..553f018 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -262,3 +262,102 @@
   ASSERT_TRUE(WIFEXITED(status));
   ASSERT_EQ(99, WEXITSTATUS(status));
 }
+
+TEST(stdlib, pty_smoke) {
+  // getpt returns a pty with O_RDWR|O_NOCTTY.
+  int fd = getpt();
+  ASSERT_NE(-1, fd);
+
+  // grantpt is a no-op.
+  ASSERT_EQ(0, grantpt(fd));
+
+  // ptsname_r should start "/dev/pts/".
+  char name_r[128];
+  ASSERT_EQ(0, ptsname_r(fd, name_r, sizeof(name_r)));
+  name_r[9] = 0;
+  ASSERT_STREQ("/dev/pts/", name_r);
+
+  close(fd);
+}
+
+TEST(stdlib, posix_openpt) {
+  int fd = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC);
+  ASSERT_NE(-1, fd);
+  close(fd);
+}
+
+TEST(stdlib, ptsname_r_ENOTTY) {
+  errno = 0;
+  char buf[128];
+  ASSERT_EQ(ENOTTY, ptsname_r(STDOUT_FILENO, buf, sizeof(buf)));
+  ASSERT_EQ(ENOTTY, errno);
+}
+
+TEST(stdlib, ptsname_r_EINVAL) {
+  int fd = getpt();
+  ASSERT_NE(-1, fd);
+  errno = 0;
+  char* buf = NULL;
+  ASSERT_EQ(EINVAL, ptsname_r(fd, buf, 128));
+  ASSERT_EQ(EINVAL, errno);
+  close(fd);
+}
+
+TEST(stdlib, ptsname_r_ERANGE) {
+  int fd = getpt();
+  ASSERT_NE(-1, fd);
+  errno = 0;
+  char buf[1];
+  ASSERT_EQ(ERANGE, ptsname_r(fd, buf, sizeof(buf)));
+  ASSERT_EQ(ERANGE, errno);
+  close(fd);
+}
+
+TEST(stdlib, ttyname_r) {
+  int fd = getpt();
+  ASSERT_NE(-1, fd);
+
+  // ttyname_r returns "/dev/ptmx" for a pty.
+  char name_r[128];
+  ASSERT_EQ(0, ttyname_r(fd, name_r, sizeof(name_r)));
+  ASSERT_STREQ("/dev/ptmx", name_r);
+
+  close(fd);
+}
+
+TEST(stdlib, ttyname_r_ENOTTY) {
+  int fd = open("/dev/null", O_WRONLY);
+  errno = 0;
+  char buf[128];
+  ASSERT_EQ(ENOTTY, ttyname_r(fd, buf, sizeof(buf)));
+  ASSERT_EQ(ENOTTY, errno);
+  close(fd);
+}
+
+TEST(stdlib, ttyname_r_EINVAL) {
+  int fd = getpt();
+  ASSERT_NE(-1, fd);
+  errno = 0;
+  char* buf = NULL;
+  ASSERT_EQ(EINVAL, ttyname_r(fd, buf, 128));
+  ASSERT_EQ(EINVAL, errno);
+  close(fd);
+}
+
+TEST(stdlib, ttyname_r_ERANGE) {
+  int fd = getpt();
+  ASSERT_NE(-1, fd);
+  errno = 0;
+  char buf[1];
+  ASSERT_EQ(ERANGE, ttyname_r(fd, buf, sizeof(buf)));
+  ASSERT_EQ(ERANGE, errno);
+  close(fd);
+}
+
+TEST(stdlib, unlockpt_ENOTTY) {
+  int fd = open("/dev/null", O_WRONLY);
+  errno = 0;
+  ASSERT_EQ(-1, unlockpt(fd));
+  ASSERT_EQ(ENOTTY, errno);
+  close(fd);
+}
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index a5f5f63..f052ce6 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -340,8 +340,19 @@
   ASSERT_EQ(static_cast<wchar_t>(0x00a2), out[1]);
   ASSERT_EQ(static_cast<wchar_t>(0x20ac), out[2]);
   ASSERT_EQ(static_cast<wchar_t>(0x24b62), out[3]);
+  // Check that valid has advanced to the next unread character.
   ASSERT_EQ('e', *valid);
 
+  wmemset(out, L'x', sizeof(out) / sizeof(wchar_t));
+  ASSERT_EQ(2U, mbsrtowcs(out, &valid, 4, ps));
+  ASSERT_EQ(L'e', out[0]);
+  ASSERT_EQ(L'f', out[1]);
+  ASSERT_EQ(L'\0', out[2]);
+  // 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);
+
   const char* invalid = "A" "\xc2\x20" "ef";
   ASSERT_EQ(static_cast<size_t>(-1), mbsrtowcs(out, &invalid, 4, ps));
   EXPECT_EQ(EILSEQ, errno);