Merge "Annotate no_return and returns_twice functions."
diff --git a/libc/Android.bp b/libc/Android.bp
index fa1eab6..5554f28 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -76,6 +76,7 @@
     },
     native_coverage: false,
     recovery_available: true,
+    native_bridge_supported: true,
 
     // lld complains about duplicate symbols in libcrt and libgcc. Suppress the
     // warning since this is intended right now.
@@ -1692,6 +1693,7 @@
     host_supported: true,
     vendor_available: true,
     recovery_available: true,
+    native_bridge_supported: true,
 
     no_libcrt: true,
     no_libgcc: true,
@@ -1824,6 +1826,7 @@
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
     recovery_available: true,
+    native_bridge_supported: true,
 
     cflags: [
         "-Wno-gcc-compat",
@@ -2082,6 +2085,7 @@
 
 ndk_library {
     name: "libc",
+    native_bridge_supported: true,
     symbol_file: "libc.map.txt",
     first_version: "9",
 }
@@ -2091,6 +2095,7 @@
     symbol_file: "libc.map.txt",
     export_headers_as_system: true,
     export_preprocessed_headers: ["include"],
+    native_bridge_supported: true,
     export_include_dirs: [
         "kernel/android/uapi",
         "kernel/uapi",
diff --git a/libc/async_safe/Android.bp b/libc/async_safe/Android.bp
index fbaaad1..c28d53a 100644
--- a/libc/async_safe/Android.bp
+++ b/libc/async_safe/Android.bp
@@ -10,6 +10,7 @@
     name: "libasync_safe",
     vendor_available: true,
     recovery_available: true,
+    native_bridge_supported: true,
 
     include_dirs: ["bionic/libc"],
     header_libs: ["libc_headers", "liblog_headers"],
@@ -22,6 +23,7 @@
 cc_library_headers {
     name: "libasync_safe_headers",
     recovery_available: true,
+    native_bridge_supported: true,
     defaults: ["linux_bionic_supported"],
 
     export_include_dirs: ["include"],
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 599ac6a..8035746 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -500,3 +500,40 @@
   return HeapprofdMallopt(opcode, arg, arg_size);
 }
 // =============================================================================
+
+#if !defined(__LP64__) && defined(__arm__)
+// =============================================================================
+// Old platform only functions that some old 32 bit apps are still using.
+// See b/132175052.
+// Only compile the functions for 32 bit arm, so that new apps do not use
+// these functions.
+// =============================================================================
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size, size_t* info_size,
+                                     size_t* total_memory, size_t* backtrace_size) {
+  if (info == nullptr || overall_size == nullptr || info_size == nullptr ||
+      total_memory == nullptr || backtrace_size == nullptr) {
+    return;
+  }
+
+  *info = nullptr;
+  *overall_size = 0;
+  *info_size = 0;
+  *total_memory = 0;
+  *backtrace_size = 0;
+
+  android_mallopt_leak_info_t leak_info = {};
+  if (android_mallopt(M_GET_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info))) {
+    *info = leak_info.buffer;
+    *overall_size = leak_info.overall_size;
+    *info_size = leak_info.info_size;
+    *total_memory = leak_info.total_memory;
+    *backtrace_size = leak_info.backtrace_size;
+  }
+}
+
+extern "C" void free_malloc_leak_info(uint8_t* info) {
+  android_mallopt_leak_info_t leak_info = { .buffer = info };
+  android_mallopt(M_FREE_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info));
+}
+// =============================================================================
+#endif
diff --git a/libc/bionic/malloc_heapprofd.cpp b/libc/bionic/malloc_heapprofd.cpp
index 2aeb9bf..5d3735d 100644
--- a/libc/bionic/malloc_heapprofd.cpp
+++ b/libc/bionic/malloc_heapprofd.cpp
@@ -143,22 +143,23 @@
   }
 }
 
+constexpr char kHeapprofdProgramPropertyPrefix[] = "heapprofd.enable.";
+constexpr size_t kHeapprofdProgramPropertyPrefixSize = sizeof(kHeapprofdProgramPropertyPrefix) - 1;
+constexpr size_t kMaxCmdlineSize = 512;
+
 static bool GetHeapprofdProgramProperty(char* data, size_t size) {
-  constexpr char prefix[] = "heapprofd.enable.";
-  // - 1 to skip nullbyte, which we will write later.
-  constexpr size_t prefix_size = sizeof(prefix) - 1;
-  if (size < prefix_size) {
+  if (size < kHeapprofdProgramPropertyPrefixSize) {
     error_log("%s: Overflow constructing heapprofd property", getprogname());
     return false;
   }
-  memcpy(data, prefix, prefix_size);
+  memcpy(data, kHeapprofdProgramPropertyPrefix, kHeapprofdProgramPropertyPrefixSize);
 
   int fd = open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC);
   if (fd == -1) {
     error_log("%s: Failed to open /proc/self/cmdline", getprogname());
     return false;
   }
-  char cmdline[128];
+  char cmdline[kMaxCmdlineSize];
   ssize_t rd = read(fd, cmdline, sizeof(cmdline) - 1);
   close(fd);
   if (rd == -1) {
@@ -167,7 +168,7 @@
   }
   cmdline[rd] = '\0';
   char* first_arg = static_cast<char*>(memchr(cmdline, '\0', rd));
-  if (first_arg == nullptr || first_arg == cmdline + size - 1) {
+  if (first_arg == nullptr) {
     error_log("%s: Overflow reading cmdline", getprogname());
     return false;
   }
@@ -192,12 +193,12 @@
   }
 
   size_t name_size = static_cast<size_t>(first_arg - start);
-  if (name_size >= size - prefix_size) {
+  if (name_size >= size - kHeapprofdProgramPropertyPrefixSize) {
     error_log("%s: overflow constructing heapprofd property.", getprogname());
     return false;
   }
   // + 1 to also copy the trailing null byte.
-  memcpy(data + prefix_size, start, name_size + 1);
+  memcpy(data + kHeapprofdProgramPropertyPrefixSize, start, name_size + 1);
   return true;
 }
 
@@ -213,7 +214,7 @@
     return true;
   }
 
-  char program_property[128];
+  char program_property[kHeapprofdProgramPropertyPrefixSize + kMaxCmdlineSize];
   if (!GetHeapprofdProgramProperty(program_property,
                                    sizeof(program_property))) {
     return false;
diff --git a/libc/include/bits/seek_constants.h b/libc/include/bits/seek_constants.h
index 6b88606..6f3f22d 100644
--- a/libc/include/bits/seek_constants.h
+++ b/libc/include/bits/seek_constants.h
@@ -39,3 +39,23 @@
 #define SEEK_CUR 1
 /** Seek relative to the end of the file. */
 #define SEEK_END 2
+
+#if defined(__USE_GNU)
+
+/**
+ * Seek to the first data (non-hole) location in the file
+ * greater than or equal to the given offset.
+ *
+ * See [lseek(2)](http://man7.org/linux/man-pages/man2/lseek.2.html).
+ */
+#define SEEK_DATA 3
+
+/**
+ * Seek to the first hole (non-data) location in the file
+ * greater than or equal to the given offset.
+ *
+ * See [lseek(2)](http://man7.org/linux/man-pages/man2/lseek.2.html).
+ */
+#define SEEK_HOLE 4
+
+#endif
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 4a734fc..bc26d2a 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1742,6 +1742,8 @@
 LIBC_DEPRECATED {
   global:
     __system_property_wait_any;
+    free_malloc_leak_info; # arm
+    get_malloc_leak_info; # arm
 };
 
 LIBC_PLATFORM {
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 91e1d26..53fcead 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <malloc.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -103,6 +104,32 @@
 __END_DECLS
 // ------------------------------------------------------------------------
 
+class ScopedConcurrentLock {
+ public:
+  ScopedConcurrentLock() {
+    pthread_rwlock_rdlock(&lock_);
+  }
+  ~ScopedConcurrentLock() {
+    pthread_rwlock_unlock(&lock_);
+  }
+
+  static void Init() {
+    pthread_rwlockattr_t attr;
+    // Set the attribute so that when a write lock is pending, read locks are no
+    // longer granted.
+    pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
+    pthread_rwlock_init(&lock_, &attr);
+  }
+
+  static void BlockAllOperations() {
+    pthread_rwlock_wrlock(&lock_);
+  }
+
+ private:
+  static pthread_rwlock_t lock_;
+};
+pthread_rwlock_t ScopedConcurrentLock::lock_;
+
 static void InitAtfork() {
   static pthread_once_t atfork_init = PTHREAD_ONCE_INIT;
   pthread_once(&atfork_init, []() {
@@ -257,6 +284,8 @@
     info_log("%s: malloc debug enabled", getprogname());
   }
 
+  ScopedConcurrentLock::Init();
+
   return true;
 }
 
@@ -265,6 +294,10 @@
     return;
   }
 
+  // Make sure that there are no other threads doing debug allocations
+  // before we kill everything.
+  ScopedConcurrentLock::BlockAllOperations();
+
   // Turn off capturing allocations calls.
   DebugDisableSet(true);
 
@@ -292,6 +325,8 @@
 
 void debug_get_malloc_leak_info(uint8_t** info, size_t* overall_size, size_t* info_size,
                                 size_t* total_memory, size_t* backtrace_size) {
+  ScopedConcurrentLock lock;
+
   ScopedDisableDebugCalls disable;
 
   // Verify the arguments.
@@ -325,6 +360,7 @@
   if (DebugCallsDisabled() || pointer == nullptr) {
     return g_dispatch->malloc_usable_size(pointer);
   }
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   if (!VerifyPointer(pointer, "malloc_usable_size")) {
@@ -388,6 +424,7 @@
   if (DebugCallsDisabled()) {
     return g_dispatch->malloc(size);
   }
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   void* pointer = InternalMalloc(size);
@@ -463,6 +500,7 @@
   if (DebugCallsDisabled() || pointer == nullptr) {
     return g_dispatch->free(pointer);
   }
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   if (g_debug->config().options() & RECORD_ALLOCS) {
@@ -480,6 +518,7 @@
   if (DebugCallsDisabled()) {
     return g_dispatch->memalign(alignment, bytes);
   }
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   if (bytes == 0) {
@@ -558,6 +597,7 @@
   if (DebugCallsDisabled()) {
     return g_dispatch->realloc(pointer, bytes);
   }
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   if (pointer == nullptr) {
@@ -676,6 +716,7 @@
   if (DebugCallsDisabled()) {
     return g_dispatch->calloc(nmemb, bytes);
   }
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   size_t size;
@@ -737,6 +778,8 @@
   if (DebugCallsDisabled() || !g_debug->TrackPointers()) {
     return g_dispatch->malloc_info(options, fp);
   }
+  ScopedConcurrentLock lock;
+  ScopedDisableDebugCalls disable;
 
   MallocXmlElem root(fp, "malloc", "version=\"debug-malloc-1\"");
   std::vector<ListInfoType> list;
@@ -786,6 +829,7 @@
 
 int debug_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*),
                   void* arg) {
+  ScopedConcurrentLock lock;
   if (g_debug->TrackPointers()) {
     // Since malloc is disabled, don't bother acquiring any locks.
     for (auto it = PointerData::begin(); it != PointerData::end(); ++it) {
@@ -800,6 +844,7 @@
 }
 
 void debug_malloc_disable() {
+  ScopedConcurrentLock lock;
   g_dispatch->malloc_disable();
   if (g_debug->pointer) {
     g_debug->pointer->PrepareFork();
@@ -807,6 +852,7 @@
 }
 
 void debug_malloc_enable() {
+  ScopedConcurrentLock lock;
   if (g_debug->pointer) {
     g_debug->pointer->PostForkParent();
   }
@@ -817,6 +863,7 @@
   if (DebugCallsDisabled() || pointer == nullptr) {
     return 0;
   }
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   if (!(g_debug->config().options() & BACKTRACE)) {
@@ -870,6 +917,7 @@
 }
 
 bool debug_write_malloc_leak_info(FILE* fp) {
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   std::lock_guard<std::mutex> guard(g_dump_lock);
@@ -883,6 +931,7 @@
 }
 
 void debug_dump_heap(const char* file_name) {
+  ScopedConcurrentLock lock;
   ScopedDisableDebugCalls disable;
 
   std::lock_guard<std::mutex> guard(g_dump_lock);
diff --git a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
index 71e8ebf..f85c45b 100644
--- a/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_system_tests.cpp
@@ -42,13 +42,15 @@
 #include <log/log.h>
 
 #include <string>
+#include <thread>
 #include <vector>
 
 #include "private/bionic_malloc.h"
 
-static constexpr time_t kTimeoutSeconds = 5;
+static constexpr time_t kTimeoutSeconds = 10;
 
-static void Exec(const char* test_name, const char* debug_options, pid_t* pid) {
+static void Exec(const char* test_name, const char* debug_options, pid_t* pid, int exit_code = 0,
+                 time_t timeout_seconds = kTimeoutSeconds) {
   int fds[2];
   ASSERT_NE(-1, pipe(fds));
   ASSERT_NE(-1, fcntl(fds[0], F_SETFL, O_NONBLOCK));
@@ -94,7 +96,8 @@
       output.append(buffer.data(), bytes);
     }
 
-    if ((time(nullptr) - start_time) > kTimeoutSeconds) {
+    if ((time(nullptr) - start_time) > timeout_seconds) {
+      kill(*pid, SIGINT);
       break;
     }
   }
@@ -109,7 +112,7 @@
       done = true;
       break;
     }
-    if ((time(nullptr) - start_time) > kTimeoutSeconds) {
+    if ((time(nullptr) - start_time) > timeout_seconds) {
       break;
     }
   }
@@ -119,21 +122,23 @@
     while (true) {
       int kill_status;
       int wait_pid = waitpid(*pid, &kill_status, WNOHANG);
-      if (wait_pid == *pid || (time(nullptr) - start_time) > kTimeoutSeconds) {
+      if (wait_pid == *pid || (time(nullptr) - start_time) > timeout_seconds) {
         break;
       }
     }
   }
 
   ASSERT_TRUE(done) << "Timed out waiting for waitpid, output:\n" << output;
-  ASSERT_EQ(0, WEXITSTATUS(status)) << "Output:\n" << output;
+  ASSERT_FALSE(WIFSIGNALED(status))
+      << "Failed with signal " << WTERMSIG(status) << "\nOutput:\n" << output;
+  ASSERT_EQ(exit_code, WEXITSTATUS(status)) << "Output:\n" << output;
 }
 
-static void GetLogStr(pid_t pid, std::string* log_str) {
+static void GetLogStr(pid_t pid, std::string* log_str, log_id log = LOG_ID_MAIN) {
   log_str->clear();
 
   logger_list* list;
-  list = android_logger_list_open(LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid);
+  list = android_logger_list_open(log, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid);
   ASSERT_TRUE(list != nullptr);
 
   while (true) {
@@ -168,7 +173,8 @@
   android_logger_list_close(list);
 }
 
-static void FindStrings(pid_t pid, std::vector<const char*> match_strings) {
+static void FindStrings(pid_t pid, std::vector<const char*> match_strings,
+                        time_t timeout_seconds = kTimeoutSeconds) {
   std::string log_str;
   time_t start = time(nullptr);
   bool found_all;
@@ -184,7 +190,7 @@
     if (found_all) {
       return;
     }
-    if ((time(nullptr) - start) > kTimeoutSeconds) {
+    if ((time(nullptr) - start) > timeout_seconds) {
       break;
     }
   }
@@ -414,3 +420,47 @@
 TEST(MallocDebugSystemTest, verify_leak_allocation_limit) {
   VerifyLeak("leak_memory_limit_");
 }
+
+static constexpr int kExpectedExitCode = 30;
+
+TEST(MallocTests, DISABLED_exit_while_threads_allocating) {
+  std::atomic_uint32_t thread_mask;
+  thread_mask = 0;
+
+  for (size_t i = 0; i < 32; i++) {
+    std::thread malloc_thread([&thread_mask, i] {
+      while (true) {
+        void* ptr = malloc(100);
+        if (ptr == nullptr) {
+          exit(1000);
+        }
+        free(ptr);
+        thread_mask.fetch_or(1 << i);
+      }
+    });
+    malloc_thread.detach();
+  }
+
+  // Wait until each thread has done at least one allocation.
+  while (thread_mask.load() != 0xffffffff)
+    ;
+  exit(kExpectedExitCode);
+}
+
+// Verify that exiting while other threads are doing malloc operations,
+// that there are no crashes.
+TEST(MallocDebugSystemTest, exit_while_threads_allocating) {
+  for (size_t i = 0; i < 100; i++) {
+    SCOPED_TRACE(::testing::Message() << "Run " << i);
+    pid_t pid;
+    ASSERT_NO_FATAL_FAILURE(Exec("MallocTests.DISABLED_exit_while_threads_allocating",
+                                 "verbose backtrace", &pid, kExpectedExitCode));
+
+    ASSERT_NO_FATAL_FAILURE(FindStrings(pid, std::vector<const char*>{"malloc debug enabled"}));
+
+    std::string log_str;
+    GetLogStr(pid, &log_str, LOG_ID_CRASH);
+    ASSERT_TRUE(log_str.find("Fatal signal") == std::string::npos)
+        << "Found crash in log.\nLog message: " << log_str;
+  }
+}
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index d306a21..1ecf122 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -204,6 +204,7 @@
 __LIBC32_LEGACY_PUBLIC__ int _fwalk(int (*)(FILE*));
 
 off64_t __sseek64(void*, off64_t, int);
+int __sflush_locked(FILE*);
 int __swhatbuf(FILE*, size_t*, int*);
 wint_t __fgetwc_unlock(FILE*);
 wint_t __ungetwc(wint_t, FILE*);
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index cfa2bfd..1df4191 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -40,7 +40,7 @@
 lflush(FILE *fp)
 {
 	if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
-		return (__sflush(fp));	/* ignored... */
+		return (__sflush_locked(fp));	/* ignored... */
 	return (0);
 }
 
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index 4cec757..91c7689 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -106,7 +106,7 @@
 FILE* stdout = &__sF[1];
 FILE* stderr = &__sF[2];
 
-static pthread_mutex_t __stdio_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t __stdio_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static uint64_t __get_file_tag(FILE* fp) {
   // Don't use a tag for the standard streams.
@@ -211,21 +211,23 @@
 }
 
 int _fwalk(int (*callback)(FILE*)) {
-  pthread_mutex_lock(&__stdio_mutex);
   int result = 0;
   for (glue* g = &__sglue; g != nullptr; g = g->next) {
     FILE* fp = g->iobs;
     for (int n = g->niobs; --n >= 0; ++fp) {
-      ScopedFileLock sfl(fp);
       if (fp->_flags != 0 && (fp->_flags & __SIGN) == 0) {
         result |= (*callback)(fp);
       }
     }
   }
-  pthread_mutex_unlock(&__stdio_mutex);
   return result;
 }
 
+extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
+  // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway.
+  _fwalk(__sflush);
+}
+
 static FILE* __fopen(int fd, int flags) {
 #if !defined(__LP64__)
   if (fd > SHRT_MAX) {
@@ -520,6 +522,11 @@
   return 0;
 }
 
+int __sflush_locked(FILE* fp) {
+  ScopedFileLock sfl(fp);
+  return __sflush(fp);
+}
+
 int __sread(void* cookie, char* buf, int n) {
   FILE* fp = reinterpret_cast<FILE*>(cookie);
   return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
@@ -1061,7 +1068,7 @@
 }
 
 static int fflush_all() {
-  return _fwalk(__sflush);
+  return _fwalk(__sflush_locked);
 }
 
 int fflush(FILE* fp) {
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index 692e0c0..0efb118 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -188,7 +188,8 @@
 
 	/* If called via exit(), flush output of all open files. */
 	if (dso == NULL) {
-		fflush(NULL);
+		extern void __libc_stdio_cleanup(void);
+		__libc_stdio_cleanup();
 	}
 
   /* BEGIN android-changed: call __unregister_atfork if dso is not null */
diff --git a/libc/system_properties/Android.bp b/libc/system_properties/Android.bp
index 911afb1..8780dda 100644
--- a/libc/system_properties/Android.bp
+++ b/libc/system_properties/Android.bp
@@ -1,6 +1,7 @@
 cc_library_static {
     name: "libsystemproperties",
     defaults: ["libc_defaults"],
+    native_bridge_supported: true,
     srcs: [
         "context_node.cpp",
         "contexts_split.cpp",
diff --git a/libdl/Android.bp b/libdl/Android.bp
index b1ee5ab..e36ddc5 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -5,6 +5,7 @@
     name: "libdl_static",
     defaults: ["linux_bionic_supported"],
     recovery_available: true,
+    native_bridge_supported: true,
 
     srcs: [
         "libdl.cpp",
@@ -32,6 +33,7 @@
 cc_library {
     name: "libdl",
     recovery_available: true,
+    native_bridge_supported: true,
     static_ndk_lib: true,
 
     defaults: ["linux_bionic_supported"],
@@ -47,6 +49,7 @@
 
     ldflags: [
         "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-x86-android.a",
@@ -115,6 +118,7 @@
 
     defaults: ["linux_bionic_supported"],
     recovery_available: true,
+    native_bridge_supported: true,
 
     // NOTE: --exclude-libs=libgcc.a makes sure that any symbols libdl.so pulls from
     // libgcc.a are made static to libdl.so.  This in turn ensures that libraries that
@@ -127,6 +131,7 @@
 
     ldflags: [
         "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-x86-android.a",
@@ -179,12 +184,14 @@
 
 ndk_library {
     name: "libdl",
+    native_bridge_supported: true,
     symbol_file: "libdl.map.txt",
     first_version: "9",
 }
 
 llndk_library {
     name: "libdl",
+    native_bridge_supported: true,
     symbol_file: "libdl.map.txt",
 }
 
diff --git a/libm/Android.bp b/libm/Android.bp
index 8c32810..48b9a5f 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -507,6 +507,7 @@
         integer_overflow: false,
     },
     stl: "none",
+    native_bridge_supported: true,
 
     stubs: {
         symbol_file: "libm.map.txt",
@@ -516,12 +517,14 @@
 
 ndk_library {
     name: "libm",
+    native_bridge_supported: true,
     symbol_file: "libm.map.txt",
     first_version: "9",
 }
 
 llndk_library {
     name: "libm",
+    native_bridge_supported: true,
     symbol_file: "libm.map.txt",
 }
 
diff --git a/linker/Android.bp b/linker/Android.bp
index 95fd4f7..5e7a921 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -2,6 +2,7 @@
     name: "liblinker_malloc",
     defaults: ["linux_bionic_supported"],
     recovery_available: true,
+    native_bridge_supported: true,
 
     srcs: [
         "linker_memory.cpp",
@@ -306,6 +307,11 @@
     xom: false,
 }
 
+sh_binary {
+    name: "ldd",
+    src: "ldd",
+}
+
 cc_library {
     // NOTE: --exclude-libs=libgcc.a makes sure that any symbols ld-android.so pulls from
     // libgcc.a are made static to ld-android.so.  This in turn ensures that libraries that
@@ -318,6 +324,7 @@
 
     ldflags: [
         "-Wl,--exclude-libs=libgcc.a",
+        "-Wl,--exclude-libs=libgcc_stripped.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-aarch64-android.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-x86-android.a",
@@ -360,6 +367,7 @@
     name: "ld-android",
     defaults: ["linux_bionic_supported"],
     recovery_available: true,
+    native_bridge_supported: true,
 
     nocrt: true,
     system_shared_libs: [],
diff --git a/linker/ldd b/linker/ldd
new file mode 100644
index 0000000..3a0aff9
--- /dev/null
+++ b/linker/ldd
@@ -0,0 +1,23 @@
+#!/system/bin/sh
+
+# Rather than have ldd and ldd64, this script does the right thing depending
+# on the argument.
+
+function error() {
+  echo "$1"
+  exit 1
+}
+
+[ $# -eq 1 ] || error "usage: ldd FILE"
+
+case `file -L "$1"` in
+  *32-bit*)
+    linker --list "$1"
+    ;;
+  *64-bit*)
+    linker64 --list "$1"
+    ;;
+  *)
+    error "$1: not an ELF file"
+    ;;
+esac
diff --git a/linker/linker.cpp b/linker/linker.cpp
index b59df73..f68775c 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1039,7 +1039,7 @@
 
   ZipEntry entry;
 
-  if (FindEntry(handle, ZipString(file_path), &entry) != 0) {
+  if (FindEntry(handle, file_path, &entry) != 0) {
     // Entry was not found.
     close(fd);
     return -1;
@@ -2596,6 +2596,8 @@
 }
 
 ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
+  if (g_is_ldd) return 0;
+
   typedef ElfW(Addr) (*ifunc_resolver_t)(void);
   ifunc_resolver_t ifunc_resolver = reinterpret_cast<ifunc_resolver_t>(resolver_addr);
   ElfW(Addr) ifunc_addr = ifunc_resolver();
@@ -3876,6 +3878,11 @@
     return true;
   }
 
+  if (g_is_ldd && !is_main_executable()) {
+    async_safe_format_fd(STDOUT_FILENO, "\t%s => %s (%p)\n", get_soname(),
+                         get_realpath(), reinterpret_cast<void*>(base));
+  }
+
   local_group_root_ = local_group.front();
   if (local_group_root_ == nullptr) {
     local_group_root_ = this;
diff --git a/linker/linker_globals.h b/linker/linker_globals.h
index 32aa09d..de05238 100644
--- a/linker/linker_globals.h
+++ b/linker/linker_globals.h
@@ -87,3 +87,5 @@
  private:
   std::string saved_error_msg_;
 };
+
+__LIBC_HIDDEN__ extern bool g_is_ldd;
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index f6e4f67..f576023 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -117,6 +117,7 @@
   return vdso;
 }
 
+bool g_is_ldd;
 int g_ld_debug_verbosity;
 
 static std::vector<std::string> g_ld_preload_names;
@@ -397,7 +398,7 @@
                          "\"%s\": error: Android 5.0 and later only support "
                          "position-independent executables (-fPIE).\n",
                          g_argv[0]);
-    exit(EXIT_FAILURE);
+    _exit(EXIT_FAILURE);
   }
 
   // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
@@ -660,22 +661,29 @@
   // linker's _start.
   const char* exe_to_load = nullptr;
   if (getauxval(AT_ENTRY) == reinterpret_cast<uintptr_t>(&_start)) {
-    if (args.argc <= 1 || !strcmp(args.argv[1], "--help")) {
+    if (args.argc == 3 && !strcmp(args.argv[1], "--list")) {
+      // We're being asked to behave like ldd(1).
+      g_is_ldd = true;
+      exe_to_load = args.argv[2];
+    } else if (args.argc <= 1 || !strcmp(args.argv[1], "--help")) {
       async_safe_format_fd(STDOUT_FILENO,
-         "Usage: %s program [arguments...]\n"
-         "       %s path.zip!/program [arguments...]\n"
+         "Usage: %s [--list] PROGRAM [ARGS-FOR-PROGRAM...]\n"
+         "       %s [--list] path.zip!/PROGRAM [ARGS-FOR-PROGRAM...]\n"
          "\n"
          "A helper program for linking dynamic executables. Typically, the kernel loads\n"
          "this program because it's the PT_INTERP of a dynamic executable.\n"
          "\n"
          "This program can also be run directly to load and run a dynamic executable. The\n"
          "executable can be inside a zip file if it's stored uncompressed and at a\n"
-         "page-aligned offset.\n",
+         "page-aligned offset.\n"
+         "\n"
+         "The --list option gives behavior equivalent to ldd(1) on other systems.\n",
          args.argv[0], args.argv[0]);
-      exit(0);
+      _exit(EXIT_SUCCESS);
+    } else {
+      exe_to_load = args.argv[1];
+      __libc_shared_globals()->initial_linker_arg_count = 1;
     }
-    exe_to_load = args.argv[1];
-    __libc_shared_globals()->initial_linker_arg_count = 1;
   }
 
   // store argc/argv/envp to use them for calling constructors
@@ -693,6 +701,8 @@
 
   ElfW(Addr) start_address = linker_main(args, exe_to_load);
 
+  if (g_is_ldd) _exit(EXIT_SUCCESS);
+
   INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
 
   // Return the address that the calling assembly stub should jump to.
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 31ee74c..5f40528 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -391,13 +391,15 @@
 }
 
 void soinfo::call_pre_init_constructors() {
+  if (g_is_ldd) return;
+
   // DT_PREINIT_ARRAY functions are called before any other constructors for executables,
   // but ignored in a shared library.
   call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath());
 }
 
 void soinfo::call_constructors() {
-  if (constructors_called) {
+  if (constructors_called || g_is_ldd) {
     return;
   }
 
diff --git a/tests/Android.bp b/tests/Android.bp
index 85bb29a..d65780c 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -288,6 +288,7 @@
         // enabled. Since the intent is just to build this, we can get away with
         // passing this flag on its own.
         "-fsanitize=address",
+        "-Wno-memset-transposed-args",
     ],
     // Ignore that we don't have ASAN symbols linked in.
     allow_undefined_symbols: true,
@@ -304,6 +305,7 @@
         "-Werror",
         "-D_FORTIFY_SOURCE=2",
         "-D__clang_analyzer__",
+        "-Wno-memset-transposed-args",
     ],
     srcs: ["fortify_filecheck_diagnostics_test.cpp"],
 }
diff --git a/tests/Android.mk b/tests/Android.mk
index fc7b940..848d291 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -65,18 +65,15 @@
 include $(CLEAR_VARS)
 LOCAL_ADDITIONAL_DEPENDENCIES := \
     $(LOCAL_PATH)/Android.mk \
-    $(LOCAL_PATH)/file-check-cxx \
-    | $(HOST_OUT_EXECUTABLES)/FileCheck$(HOST_EXECUTABLE_SUFFIX) \
+    $(LOCAL_PATH)/touch-obj-on-success
 
-LOCAL_CXX := $(LOCAL_PATH)/file-check-cxx \
-    $(HOST_OUT_EXECUTABLES)/FileCheck \
+LOCAL_CXX := $(LOCAL_PATH)/touch-obj-on-success \
     $(LLVM_PREBUILTS_PATH)/clang++ \
-    CLANG \
 
 LOCAL_CLANG := true
 LOCAL_MODULE := bionic-compile-time-tests-clang++
-LOCAL_CPPFLAGS := -Wall -Werror
-LOCAL_CPPFLAGS += -fno-color-diagnostics -ferror-limit=10000
+LOCAL_CPPFLAGS := -Wall -Wno-error
+LOCAL_CPPFLAGS += -fno-color-diagnostics -ferror-limit=10000 -Xclang -verify
 LOCAL_SRC_FILES := fortify_filecheck_diagnostics_test.cpp
 include $(BUILD_STATIC_LIBRARY)
 
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index eed84a4..67ebf37 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -134,10 +134,7 @@
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchive(lib_path.c_str(), &handle));
   ZipEntry zip_entry;
-  ZipString zip_name;
-  zip_name.name = reinterpret_cast<const uint8_t*>(kLibZipSimpleZip);
-  zip_name.name_length = strlen(kLibZipSimpleZip);
-  ASSERT_EQ(0, FindEntry(handle, zip_name, &zip_entry));
+  ASSERT_EQ(0, FindEntry(handle, kLibZipSimpleZip, &zip_entry));
   extinfo.library_fd_offset = zip_entry.offset;
   CloseArchive(handle);
 
diff --git a/tests/file-check-cxx b/tests/file-check-cxx
deleted file mode 100755
index d3bc5f7..0000000
--- a/tests/file-check-cxx
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-FILECHECK=$1
-CXX=$2
-PREFIX=$3
-shift 3
-SOURCE=$(echo "$@" | grep -oP '\S+\.cpp\b')
-OBJ=$(echo "$@" | grep -oP '\S+\.o\b')
-$CXX "$@" -Wno-error 2>&1 | $FILECHECK -check-prefix=$PREFIX $SOURCE
-if [ "$?" -eq 0 ]; then
-  touch $OBJ
-else
-  exit 1
-fi
diff --git a/tests/fortify_filecheck_diagnostics_test.cpp b/tests/fortify_filecheck_diagnostics_test.cpp
index e79a9a6..ac9853c 100644
--- a/tests/fortify_filecheck_diagnostics_test.cpp
+++ b/tests/fortify_filecheck_diagnostics_test.cpp
@@ -15,19 +15,14 @@
  */
 
 /*
- * If this test fails, you can see the compiler's output by erasing a few args from the failing
- * command. Specifically, delete everything before the path/to/the/compiler, then delete the first
- * arg after the path/to/the/compiler. For example, given the following command:
- *
- * bionic/tests/file-check-cxx out/host/linux-x86/bin/FileCheck \
- * prebuilts/clang/host/linux-x86/clang-4053586/bin/clang++ CLANG    -I bionic/tests -I ...
- *
- * If you delete everything before clang++ and delete "CLANG", then you'll end up with:
- *
- * prebuilts/clang/host/linux-x86/clang-4053586/bin/clang++ -I bionic/tests -I ...
- *
- * Which is the command that FileCheck executes.
+ * Silence all notes about enable_if-related 'candidates'; they're nice to know
+ * about for users, but this test doesn't care.
  */
+// expected-note@* 0+{{candidate function}}
+
+/* Similarly, ignore all "from 'diagnose_if'"s. */
+// expected-note@* 0+{{from 'diagnose_if'}}
+
 
 #undef _FORTIFY_SOURCE
 #define _FORTIFY_SOURCE 2
@@ -43,11 +38,15 @@
 #include <time.h>
 #include <unistd.h>
 
+#if !defined(__BIONIC__)
+#  error "This only works with Bionic."
+#endif
+
 void test_sprintf() {
   char buf[4];
 
   // NOLINTNEXTLINE(whitespace/line_length)
-  // CLANG: error: call to unavailable function 'sprintf': format string will always overflow destination buffer
+  // expected-error@+1{{call to unavailable function 'sprintf': format string will always overflow destination buffer}}
   sprintf(buf, "foobar");  // NOLINT(runtime/printf)
 
   // TODO: clang should emit a warning, but doesn't
@@ -58,7 +57,7 @@
   char buf[4];
 
   // NOLINTNEXTLINE(whitespace/line_length)
-  // CLANG: error: call to unavailable function 'snprintf': format string will always overflow destination buffer
+  // expected-error@+1{{call to unavailable function 'snprintf': format string will always overflow destination buffer}}
   snprintf(buf, 5, "foobar");  // NOLINT(runtime/printf)
 
   // TODO: clang should emit a warning, but doesn't
@@ -74,41 +73,41 @@
 void test_memcpy() {
   char buf[4];
 
-  // CLANG: error: 'memcpy' called with size bigger than buffer
+  // expected-error@+1{{'memcpy' called with size bigger than buffer}}
   memcpy(buf, "foobar", sizeof("foobar") + 100);
 }
 
 void test_memmove() {
   char buf[4];
 
-  // CLANG: error: 'memmove' called with size bigger than buffer
+  // expected-error@+1{{'memmove' called with size bigger than buffer}}
   memmove(buf, "foobar", sizeof("foobar"));
 }
 
 void test_memset() {
   char buf[4];
 
-  // CLANG: error: 'memset' called with size bigger than buffer
+  // expected-error@+1{{'memset' called with size bigger than buffer}}
   memset(buf, 0, 6);
 }
 
 void test_strcpy() {
   char buf[4];
 
-  // CLANG: error: 'strcpy' called with string bigger than buffer
+  // expected-error@+1{{'strcpy' called with string bigger than buffer}}
   strcpy(buf, "foobar");  // NOLINT(runtime/printf)
 
-  // CLANG: error: 'strcpy' called with string bigger than buffer
+  // expected-error@+1{{'strcpy' called with string bigger than buffer}}
   strcpy(buf, "quux");
 }
 
 void test_stpcpy() {
   char buf[4];
 
-  // CLANG: error: 'stpcpy' called with string bigger than buffer
+  // expected-error@+1{{'stpcpy' called with string bigger than buffer}}
   stpcpy(buf, "foobar");
 
-  // CLANG: error: 'stpcpy' called with string bigger than buffer
+  // expected-error@+1{{'stpcpy' called with string bigger than buffer}}
   stpcpy(buf, "quux");
 }
 
@@ -157,10 +156,10 @@
 void test_fgets() {
   char buf[4];
 
-  // CLANG: error: in call to 'fgets', size should not be negative
+  // expected-error@+1{{in call to 'fgets', size should not be negative}}
   fgets(buf, -1, stdin);
 
-  // CLANG: error: in call to 'fgets', size is larger than the destination buffer
+  // expected-error@+1{{in call to 'fgets', size is larger than the destination buffer}}
   fgets(buf, 6, stdin);
 }
 
@@ -168,58 +167,58 @@
   char buf[4];
   sockaddr_in addr;
 
-  // CLANG: error: 'recvfrom' called with size bigger than buffer
+  // expected-error@+1{{'recvfrom' called with size bigger than buffer}}
   recvfrom(0, buf, 6, 0, reinterpret_cast<sockaddr*>(&addr), nullptr);
 }
 
 void test_recv() {
   char buf[4] = {0};
 
-  // CLANG: error: 'recv' called with size bigger than buffer
+  // expected-error@+1{{'recv' called with size bigger than buffer}}
   recv(0, buf, 6, 0);
 }
 
 void test_umask() {
-  // CLANG: error: 'umask' called with invalid mode
+  // expected-error@+1{{'umask' called with invalid mode}}
   umask(01777);
 }
 
 void test_read() {
   char buf[4];
-  // CLANG: error: in call to 'read', 'count' bytes overflows the given object
+  // expected-error@+1{{in call to 'read', 'count' bytes overflows the given object}}
   read(0, buf, 6);
 }
 
 void test_open() {
-  // CLANG: error: 'open' called with O_CREAT or O_TMPFILE, but missing mode
+  // expected-error@+1{{'open' called with O_CREAT or O_TMPFILE, but missing mode}}
   open("/dev/null", O_CREAT);
 
-  // CLANG: error: 'open' called with O_CREAT or O_TMPFILE, but missing mode
+  // expected-error@+1{{'open' called with O_CREAT or O_TMPFILE, but missing mode}}
   open("/dev/null", O_TMPFILE);
 
-  // CLANG: error: call to unavailable function 'open': too many arguments
+  // expected-error@+1{{call to unavailable function 'open': too many arguments}}
   open("/dev/null", O_CREAT, 0, 0);
 
-  // CLANG: error: call to unavailable function 'open': too many arguments
+  // expected-error@+1{{call to unavailable function 'open': too many arguments}}
   open("/dev/null", O_TMPFILE, 0, 0);
 
-  // CLANG: warning: 'open' has superfluous mode bits; missing O_CREAT?
+  // expected-warning@+1{{'open' has superfluous mode bits; missing O_CREAT?}}
   open("/dev/null", O_RDONLY, 0644);
 
-  // CLANG: warning: 'open' has superfluous mode bits; missing O_CREAT?
+  // expected-warning@+1{{'open' has superfluous mode bits; missing O_CREAT?}}
   open("/dev/null", O_DIRECTORY, 0644);
 }
 
 void test_poll() {
   pollfd fds[1];
-  // CLANG: error: in call to 'poll', fd_count is larger than the given buffer
+  // expected-error@+1{{in call to 'poll', fd_count is larger than the given buffer}}
   poll(fds, 2, 0);
 }
 
 void test_ppoll() {
   pollfd fds[1];
   timespec timeout;
-  // CLANG: error: in call to 'ppoll', fd_count is larger than the given buffer
+  // expected-error@+1{{in call to 'ppoll', fd_count is larger than the given buffer}}
   ppoll(fds, 2, &timeout, nullptr);
 }
 
@@ -227,101 +226,98 @@
   pollfd fds[1];
   timespec timeout;
   // NOLINTNEXTLINE(whitespace/line_length)
-  // CLANG: error: in call to 'ppoll64', fd_count is larger than the given buffer
+  // expected-error@+1{{in call to 'ppoll64', fd_count is larger than the given buffer}}
   ppoll64(fds, 2, &timeout, nullptr);
 }
 
 void test_fread_overflow() {
   char buf[4];
-  // CLANG: error: in call to 'fread', size * count overflows
+  // expected-error@+1{{in call to 'fread', size * count overflows}}
   fread(buf, 2, (size_t)-1, stdin);
 }
 
 void test_fread_too_big() {
   char buf[4];
   // NOLINTNEXTLINE(whitespace/line_length)
-  // CLANG: error: in call to 'fread', size * count is too large for the given buffer
+  // expected-error@+1{{in call to 'fread', size * count is too large for the given buffer}}
   fread(buf, 1, 5, stdin);
 }
 
 void test_fwrite_overflow() {
   char buf[4] = {0};
-  // CLANG: error: in call to 'fwrite', size * count overflows
+  // expected-error@+1{{in call to 'fwrite', size * count overflows}}
   fwrite(buf, 2, (size_t)-1, stdout);
 }
 
 void test_fwrite_too_big() {
   char buf[4] = {0};
   // NOLINTNEXTLINE(whitespace/line_length)
-  // CLANG: error: in call to 'fwrite', size * count is too large for the given buffer
+  // expected-error@+1{{in call to 'fwrite', size * count is too large for the given buffer}}
   fwrite(buf, 1, 5, stdout);
 }
 
 void test_getcwd() {
   char buf[4];
-  // CLANG: error: in call to 'getcwd', 'size' bytes overflows the given object
+  // expected-error@+1{{in call to 'getcwd', 'size' bytes overflows the given object}}
   getcwd(buf, 5);
 }
 
 void test_pwrite64_size() {
   char buf[4] = {0};
-  // CLANG: error: in call to 'pwrite64', 'count' bytes overflows the given object
+  // expected-error@+1{{in call to 'pwrite64', 'count' bytes overflows the given object}}
   pwrite64(STDOUT_FILENO, buf, 5, 0);
 }
 
 void test_pwrite64_too_big_malloc() {
   void *buf = calloc(atoi("5"), 1);
-  // clang should emit a warning, but probably never will.
+  // expected-error@+1{{in call to 'pwrite64', 'count' must be <= SSIZE_MAX}}
   pwrite64(STDOUT_FILENO, buf, SIZE_MAX, 0);
 }
 
 void test_pwrite64_too_big() {
   char buf[4] = {0};
-  // CLANG: error: in call to 'pwrite64', 'count' must be <= SSIZE_MAX
+  // expected-error@+1{{in call to 'pwrite64', 'count' must be <= SSIZE_MAX}}
   pwrite64(STDOUT_FILENO, buf, SIZE_MAX, 0);
 }
 
 void test_write_size() {
   char buf[4] = {0};
-  // CLANG: error: in call to 'write', 'count' bytes overflows the given object
+  // expected-error@+1{{in call to 'write', 'count' bytes overflows the given object}}
   write(STDOUT_FILENO, buf, 5);
 }
 
 void test_memset_args_flipped() {
   char from[4] = {0};
   // NOLINTNEXTLINE(whitespace/line_length)
-  // CLANG: 'memset' will set 0 bytes; maybe the arguments got flipped?
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wmemset-transposed-args"
+  // expected-warning@+1{{'memset' will set 0 bytes; maybe the arguments got flipped?}}
   memset(from, sizeof(from), 0);
-#pragma clang diagnostic pop
 }
 
 void test_sendto() {
   char buf[4] = {0};
   sockaddr_in addr;
 
-  // CLANG: error: 'sendto' called with size bigger than buffer
+  // expected-error@+1{{'sendto' called with size bigger than buffer}}
   sendto(0, buf, 6, 0, reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr_in));
 }
 
 void test_send() {
   char buf[4] = {0};
 
-  // CLANG: error: 'send' called with size bigger than buffer
+  // expected-error@+1{{'send' called with size bigger than buffer}}
   send(0, buf, 6, 0);
 }
 
 void test_realpath() {
   char buf[4] = {0};
   // NOLINTNEXTLINE(whitespace/line_length)
-  // CLANG: error: 'realpath' output parameter must be NULL or a pointer to a buffer with >= PATH_MAX bytes
+  // expected-error@+1{{'realpath' output parameter must be NULL or a pointer to a buffer with >= PATH_MAX bytes}}
   realpath(".", buf);
 
   // This is fine.
   realpath(".", nullptr);
 
   char bigbuf[PATH_MAX];
-  // CLANG: error: 'realpath': NULL path is never correct; flipped arguments?
+  // expected-error@+1{{'realpath': NULL path is never correct; flipped arguments?}}
   realpath(nullptr, bigbuf);
 }
diff --git a/tests/libs/bionic_tests_zipalign.cpp b/tests/libs/bionic_tests_zipalign.cpp
index 56183ba..ec500d4 100644
--- a/tests/libs/bionic_tests_zipalign.cpp
+++ b/tests/libs/bionic_tests_zipalign.cpp
@@ -41,7 +41,7 @@
 
 static bool GetEntries(ZipArchiveHandle handle, std::vector<ZipData>* entries) {
   void* cookie;
-  int32_t return_value = StartIteration(handle, &cookie, nullptr, nullptr);
+  int32_t return_value = StartIteration(handle, &cookie);
   if (return_value != 0) {
     fprintf(stderr, "Unable to iterate over entries: %s\n", ErrorCodeString(return_value));
     return false;
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 65a942c..01b4dba 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <linux/fs.h>
 #include <math.h>
 #include <stdio.h>
 #include <sys/types.h>
@@ -29,6 +30,7 @@
 #include <locale.h>
 
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
@@ -2577,3 +2579,34 @@
   ASSERT_LT(0, length);
   ASSERT_EQ("/proc/self/fd/2", std::string(path, length));
 }
+
+TEST(STDIO_TEST, fread_with_locked_file) {
+  // Reading an unbuffered/line-buffered file from one thread shouldn't block on
+  // files locked on other threads, even if it flushes some line-buffered files.
+  FILE* fp1 = fopen("/dev/zero", "r");
+  ASSERT_TRUE(fp1 != nullptr);
+  flockfile(fp1);
+
+  std::thread([] {
+    for (int mode : { _IONBF, _IOLBF }) {
+      FILE* fp2 = fopen("/dev/zero", "r");
+      ASSERT_TRUE(fp2 != nullptr);
+      setvbuf(fp2, nullptr, mode, 0);
+      ASSERT_EQ('\0', fgetc(fp2));
+      fclose(fp2);
+    }
+  }).join();
+
+  funlockfile(fp1);
+  fclose(fp1);
+}
+
+TEST(STDIO_TEST, SEEK_macros) {
+  ASSERT_EQ(0, SEEK_SET);
+  ASSERT_EQ(1, SEEK_CUR);
+  ASSERT_EQ(2, SEEK_END);
+  ASSERT_EQ(3, SEEK_DATA);
+  ASSERT_EQ(4, SEEK_HOLE);
+  // So we'll notice if Linux grows another constant in <linux/fs.h>...
+  ASSERT_EQ(SEEK_MAX, SEEK_HOLE);
+}
diff --git a/tests/touch-obj-on-success b/tests/touch-obj-on-success
new file mode 100755
index 0000000..df08a49
--- /dev/null
+++ b/tests/touch-obj-on-success
@@ -0,0 +1,8 @@
+#!/bin/bash -eu
+#
+# Runs the given C/C++ compile-ish command. On success, scrapes an object file
+# from that command line and touches it.
+
+"$@"
+obj="$(echo "$@" | grep -oP '\S+\.o\b')"
+touch "${obj}"