Merge "Update kernel headers to v3.18.3."
diff --git a/benchmarks/Android.mk b/benchmarks/Android.mk
index 8f5fd41..989e9e6 100644
--- a/benchmarks/Android.mk
+++ b/benchmarks/Android.mk
@@ -14,8 +14,6 @@
 # limitations under the License.
 #
 
-ifneq ($(BUILD_TINY_ANDROID), true)
-
 LOCAL_PATH := $(call my-dir)
 
 # -----------------------------------------------------------------------------
@@ -108,5 +106,3 @@
 endif
 
 endif
-
-endif # !BUILD_TINY_ANDROID
diff --git a/libc/Android.mk b/libc/Android.mk
index 71ddbd3..a2a3108 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -112,6 +112,7 @@
     bionic/epoll_pwait.cpp \
     bionic/epoll_wait.cpp \
     bionic/__errno.cpp \
+    bionic/error.cpp \
     bionic/eventfd_read.cpp \
     bionic/eventfd_write.cpp \
     bionic/ffs.cpp \
@@ -896,7 +897,14 @@
 LOCAL_CXX_STL := none
 LOCAL_SYSTEM_SHARED_LIBRARIES :=
 LOCAL_ADDRESS_SANITIZER := false
-LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
+# b/17574078: Need to disable coverage until we have a prebuilt libprofile_rt.
+# Since this is a static library built with clang, it needs to link
+# libprofile_rt when it is linked into the final binary. Since the final binary
+# is built with GCC, it won't link libprofile_rt. We can't very easily just add
+# libprofile_rt to all link lines the way we've done for libgcov because
+# libprofile_rt isn't prebuilt, and it would be tricky to write a rule that
+# would make sure libprofile_rt is built.
+LOCAL_NATIVE_COVERAGE := false
 
 include $(BUILD_STATIC_LIBRARY)
 
diff --git a/libc/bionic/error.cpp b/libc/bionic/error.cpp
new file mode 100644
index 0000000..9f4d0ee
--- /dev/null
+++ b/libc/bionic/error.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <error.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+unsigned int error_message_count = 0;
+void (*error_print_progname)(void) = NULL;
+int error_one_per_line = 0;
+
+static void __error_head() {
+  ++error_message_count;
+
+  if (error_print_progname != NULL) {
+    error_print_progname();
+  } else {
+    fflush(stdout);
+    fprintf(stderr, "%s:", getprogname());
+  }
+}
+
+static void __error_tail(int status, int error) {
+  if (error != 0) {
+    fprintf(stderr, ": %s", strerror(error));
+  }
+
+  putc('\n', stderr);
+  fflush(stderr);
+
+  if (status != 0) {
+    exit(status);
+  }
+}
+
+void error(int status, int error, const char* fmt, ...) {
+  __error_head();
+  putc(' ', stderr);
+
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+
+  __error_tail(status, error);
+}
+
+void error_at_line(int status, int error, const char* file, unsigned int line, const char* fmt, ...) {
+  if (error_one_per_line) {
+    static const char* last_file;
+    static unsigned int last_line;
+    if (last_line == line && last_file != NULL && strcmp(last_file, file) == 0) {
+      return;
+    }
+    last_file = file;
+    last_line = line;
+  }
+
+  __error_head();
+  fprintf(stderr, "%s:%d: ", file, line);
+
+  va_list ap;
+  va_start(ap, fmt);
+  vfprintf(stderr, fmt, ap);
+  va_end(ap);
+
+  __error_tail(status, error);
+}
diff --git a/libc/bionic/sigemptyset.cpp b/libc/bionic/sigemptyset.cpp
index 8d8033f..58166a1 100644
--- a/libc/bionic/sigemptyset.cpp
+++ b/libc/bionic/sigemptyset.cpp
@@ -28,6 +28,7 @@
 
 #include <errno.h>
 #include <signal.h>
+#include <string.h>
 
 int sigemptyset(sigset_t* set) {
   if (set == NULL) {
diff --git a/libc/bionic/sigfillset.cpp b/libc/bionic/sigfillset.cpp
index 37792e5..cb67394 100644
--- a/libc/bionic/sigfillset.cpp
+++ b/libc/bionic/sigfillset.cpp
@@ -28,6 +28,7 @@
 
 #include <errno.h>
 #include <signal.h>
+#include <string.h>
 
 int sigfillset(sigset_t* set) {
   if (set == NULL) {
diff --git a/libc/include/error.h b/libc/include/error.h
new file mode 100644
index 0000000..dd12884
--- /dev/null
+++ b/libc/include/error.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _ERROR_H
+#define _ERROR_H 1
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+void error(int, int, const char*, ...) __printflike(3, 4);
+void error_at_line(int, int, const char*, unsigned int, const char*, ...) __printflike(5, 6);
+
+extern void (*error_print_progname)(void);
+extern unsigned int error_message_count;
+extern int error_one_per_line;
+
+__END_DECLS
+
+#endif
diff --git a/libc/include/signal.h b/libc/include/signal.h
index 8988888..39d37e9 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -33,7 +33,6 @@
 #include <limits.h>
 #include <machine/pthread_types.h>
 #include <machine/timespec.h>
-#include <string.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index dfb788e..0060ea3 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -31,7 +31,6 @@
 #include <sys/cdefs.h>
 
 #include <stddef.h>
-#include <string.h>
 #include <alloca.h>
 #include <strings.h>
 #include <memory.h>
diff --git a/libc/include/sys/_errdefs.h b/libc/include/sys/_errdefs.h
index 3eeadf4..3c3c9d7 100644
--- a/libc/include/sys/_errdefs.h
+++ b/libc/include/sys/_errdefs.h
@@ -45,7 +45,7 @@
 __BIONIC_ERRDEF( ENXIO          ,   6, "No such device or address" )
 __BIONIC_ERRDEF( E2BIG          ,   7, "Argument list too long" )
 __BIONIC_ERRDEF( ENOEXEC        ,   8, "Exec format error" )
-__BIONIC_ERRDEF( EBADF          ,   9, "Bad file number" )
+__BIONIC_ERRDEF( EBADF          ,   9, "Bad file descriptor" )
 __BIONIC_ERRDEF( ECHILD         ,  10, "No child processes" )
 __BIONIC_ERRDEF( EAGAIN         ,  11, "Try again" )
 __BIONIC_ERRDEF( ENOMEM         ,  12, "Out of memory" )
diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c
index baf62b9..bac8dad 100644
--- a/libc/stdio/fread.c
+++ b/libc/stdio/fread.c
@@ -120,7 +120,7 @@
 	while (total > 0) {
 		ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total);
 		if (bytes_read <= 0) {
-			fp->_flags = (fp->_r == 0) ? __SEOF : __SERR;
+			fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR;
 			break;
 		}
 		dst += bytes_read;
diff --git a/linker/Android.mk b/linker/Android.mk
index cc7fadf..54535fc 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -19,6 +19,9 @@
 LOCAL_SRC_FILES_mips    := arch/mips/begin.S linker_mips.cpp
 LOCAL_SRC_FILES_mips64  := arch/mips64/begin.S linker_mips.cpp
 
+# -shared is used to overwrite the -Bstatic and -static
+# flags triggered by LOCAL_FORCE_STATIC_EXECUTABLE.
+# This dynamic linker is actually a shared object linked with static libraries.
 LOCAL_LDFLAGS := \
     -shared \
     -Wl,-Bsymbolic \
diff --git a/linker/linker.cpp b/linker/linker.cpp
index df8e52e..f7bcd27 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -262,11 +262,6 @@
   g_soinfo_links_allocator.free(entry);
 }
 
-static void protect_data(int protection) {
-  g_soinfo_allocator.protect_all(protection);
-  g_soinfo_links_allocator.protect_all(protection);
-}
-
 static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, off64_t file_offset, uint32_t rtld_flags) {
   if (strlen(name) >= SOINFO_NAME_LEN) {
     DL_ERR("library name \"%s\" too long", name);
@@ -589,6 +584,34 @@
   return s;
 }
 
+class ProtectedDataGuard {
+ public:
+  ProtectedDataGuard() {
+    if (ref_count_++ == 0) {
+      protect_data(PROT_READ | PROT_WRITE);
+    }
+  }
+
+  ~ProtectedDataGuard() {
+    if (ref_count_ == 0) { // overflow
+      __libc_fatal("Too many nested calls to dlopen()");
+    }
+
+    if (--ref_count_ == 0) {
+      protect_data(PROT_READ);
+    }
+  }
+ private:
+  void protect_data(int protection) {
+    g_soinfo_allocator.protect_all(protection);
+    g_soinfo_links_allocator.protect_all(protection);
+  }
+
+  static size_t ref_count_;
+};
+
+size_t ProtectedDataGuard::ref_count_ = 0;
+
 // Each size has it's own allocator.
 template<size_t size>
 class SizeBasedAllocator {
@@ -1237,19 +1260,18 @@
       return nullptr;
     }
   }
-  protect_data(PROT_READ | PROT_WRITE);
+
+  ProtectedDataGuard guard;
   soinfo* si = find_library(name, flags, extinfo);
   if (si != nullptr) {
     si->call_constructors();
   }
-  protect_data(PROT_READ);
   return si;
 }
 
 void do_dlclose(soinfo* si) {
-  protect_data(PROT_READ | PROT_WRITE);
+  ProtectedDataGuard guard;
   soinfo_unload(si);
-  protect_data(PROT_READ);
 }
 
 static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
@@ -1595,10 +1617,6 @@
   TRACE("[ Calling %s @ %p for '%s' ]", function_name, function, name);
   function();
   TRACE("[ Done calling %s @ %p for '%s' ]", function_name, function, name);
-
-  // The function may have called dlopen(3) or dlclose(3), so we need to ensure our data structures
-  // are still writable. This happens with our debug malloc (see http://b/7941716).
-  protect_data(PROT_READ | PROT_WRITE);
 }
 
 void soinfo::call_pre_init_constructors() {
@@ -2522,15 +2540,19 @@
 
   add_vdso(args);
 
-  si->call_pre_init_constructors();
+  {
+    ProtectedDataGuard guard;
 
-  /* After the prelink_image, the si->load_bias is initialized.
-   * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
-   * We need to update this value for so exe here. So Unwind_Backtrace
-   * for some arch like x86 could work correctly within so exe.
-   */
-  map->l_addr = si->load_bias;
-  si->call_constructors();
+    si->call_pre_init_constructors();
+
+    /* After the prelink_image, the si->load_bias is initialized.
+     * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
+     * We need to update this value for so exe here. So Unwind_Backtrace
+     * for some arch like x86 could work correctly within so exe.
+     */
+    map->l_addr = si->load_bias;
+    si->call_constructors();
+  }
 
 #if TIMING
   gettimeofday(&t1, nullptr);
@@ -2673,8 +2695,6 @@
   args.abort_message_ptr = &g_abort_message;
   ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr);
 
-  protect_data(PROT_READ);
-
   INFO("[ jumping to _start ]");
 
   // Return the address that the calling assembly stub should jump to.
diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk
index fe64e77..aa9491e 100644
--- a/linker/tests/Android.mk
+++ b/linker/tests/Android.mk
@@ -14,8 +14,6 @@
 # limitations under the License.
 #
 
-ifneq ($(BUILD_TINY_ANDROID),true)
-
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
@@ -34,5 +32,3 @@
   ../linker_allocator.cpp
 
 include $(BUILD_NATIVE_TEST)
-
-endif # !BUILD_TINY_ANDROID
diff --git a/tests/Android.mk b/tests/Android.mk
index 11fb2c8..82a92f0 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -14,8 +14,6 @@
 # limitations under the License.
 #
 
-ifneq ($(BUILD_TINY_ANDROID),true)
-
 LOCAL_PATH := $(call my-dir)
 
 # -----------------------------------------------------------------------------
@@ -57,6 +55,7 @@
     complex_test.cpp \
     ctype_test.cpp \
     dirent_test.cpp \
+    error_test.cpp \
     eventfd_test.cpp \
     fcntl_test.cpp \
     fenv_test.cpp \
@@ -413,11 +412,15 @@
 include $(LOCAL_PATH)/../build/run-on-host.mk
 
 ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),x86 x86_64))
+
+TEST_TIMEOUT := 0
+
 # BIONIC_TEST_FLAGS is either empty or it comes from the user.
 bionic-unit-tests-run-on-host32: bionic-unit-tests bionic-prepare-run-on-host
 	ANDROID_DATA=$(TARGET_OUT_DATA) \
 	ANDROID_DNS_MODE=local \
 	ANDROID_ROOT=$(TARGET_OUT) \
+		timeout $(TEST_TIMEOUT) \
 		$(TARGET_OUT_DATA)/nativetest/bionic-unit-tests/bionic-unit-tests32 $(BIONIC_TEST_FLAGS)
 
 ifeq ($(TARGET_IS_64_BIT),true)
@@ -426,6 +429,7 @@
 	ANDROID_DATA=$(TARGET_OUT_DATA) \
 	ANDROID_DNS_MODE=local \
 	ANDROID_ROOT=$(TARGET_OUT) \
+		timeout $(TEST_TIMEOUT) \
 		$(TARGET_OUT_DATA)/nativetest64/bionic-unit-tests/bionic-unit-tests64 $(BIONIC_TEST_FLAGS)
 endif
 
@@ -433,4 +437,3 @@
 endif # linux-x86
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
-endif # !BUILD_TINY_ANDROID
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 6fdfdc7..3b1001a 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -850,3 +850,17 @@
   dlclose(handle1);
   dlclose(handle2);
 }
+
+// libtest_dlopen_from_ctor_main.so depends on
+// libtest_dlopen_from_ctor.so which has a constructor
+// that calls dlopen(libc...). This is to test the situation
+// described in b/7941716.
+TEST(dlfcn, dlopen_dlopen_from_ctor) {
+#if defined(__BIONIC__)
+  void* handle = dlopen("libtest_dlopen_from_ctor_main.so", RTLD_NOW);
+  ASSERT_TRUE(handle != nullptr) << dlerror();
+  dlclose(handle);
+#else
+  GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n";
+#endif
+}
diff --git a/tests/error_test.cpp b/tests/error_test.cpp
new file mode 100644
index 0000000..5fee16f
--- /dev/null
+++ b/tests/error_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <gtest/gtest.h>
+
+#include <error.h>
+
+#include "BionicDeathTest.h"
+
+static size_t g_test_error_print_progname_invocation_count;
+
+static void test_error_print_progname() {
+  ++g_test_error_print_progname_invocation_count;
+}
+
+TEST(error, smoke) {
+  error_message_count = 0;
+  error(0, 0, "oops");
+  ASSERT_EQ(1U, error_message_count);
+  error(0, ENOENT, "couldn't open file '%s'", "blah");
+  ASSERT_EQ(2U, error_message_count);
+
+  error_print_progname = test_error_print_progname;
+  g_test_error_print_progname_invocation_count = 0;
+  error(0, 0, "oops");
+  ASSERT_EQ(1U, g_test_error_print_progname_invocation_count);
+
+  error_at_line(0, 0, "blah.c", 123, "hello %s", "world");
+
+  error_print_progname = NULL;
+}
+
+TEST(error_DeathTest, error_exit) {
+  ASSERT_EXIT(error(22, 0, "x%c", 'y'), ::testing::ExitedWithCode(22), "xy");
+}
+
+TEST(error_DeathTest, error_exit_with_errno) {
+  ASSERT_EXIT(error(22, EBADF, "x%c", 'y'), ::testing::ExitedWithCode(22), ": xy: Bad file descriptor");
+}
+
+TEST(error_DeathTest, error_at_line_exit) {
+  ASSERT_EXIT(error_at_line(22, 0, "a.c", 123, "x%c", 'y'), ::testing::ExitedWithCode(22), ":a.c:123: xy");
+}
+
+TEST(error_DeathTest, error_at_line_exit_with_errno) {
+  ASSERT_EXIT(error_at_line(22, EBADF, "a.c", 123, "x%c", 'y'), ::testing::ExitedWithCode(22), ":a.c:123: xy: Bad file descriptor");
+}
diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk
index 50d96b2..7ca856c 100644
--- a/tests/libs/Android.mk
+++ b/tests/libs/Android.mk
@@ -367,3 +367,29 @@
 
 module := libtest_dlopen_weak_undefined_func
 include $(LOCAL_PATH)/Android.build.testlib.mk
+
+# -----------------------------------------------------------------------------
+# Library with constructor that calls dlopen() b/7941716
+# -----------------------------------------------------------------------------
+libtest_dlopen_from_ctor_src_files := \
+   dlopen_testlib_dlopen_from_ctor.cpp
+
+module := libtest_dlopen_from_ctor
+
+build_target := SHARED_LIBRARY
+build_type := host
+include $(TEST_PATH)/Android.build.mk
+
+libtest_dlopen_from_ctor_shared_libraries := libdl
+build_type := target
+include $(TEST_PATH)/Android.build.mk
+
+# -----------------------------------------------------------------------------
+# Library that depends on the library with constructor that calls dlopen() b/7941716
+# -----------------------------------------------------------------------------
+
+libtest_dlopen_from_ctor_main_src_files := empty.cpp
+libtest_dlopen_from_ctor_main_shared_libraries := libtest_dlopen_from_ctor
+
+module := libtest_dlopen_from_ctor_main
+include $(LOCAL_PATH)/Android.build.testlib.mk
diff --git a/tests/libs/dlopen_testlib_dlopen_from_ctor.cpp b/tests/libs/dlopen_testlib_dlopen_from_ctor.cpp
new file mode 100644
index 0000000..95233f7
--- /dev/null
+++ b/tests/libs/dlopen_testlib_dlopen_from_ctor.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+
+static void __attribute__((constructor)) call_dlopen_from_ctor() {
+  void* handle = dlopen("libc.so", RTLD_NOW);
+  dlclose(handle);
+}
+
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 0e24325..890e86e 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -27,6 +27,8 @@
 #include <wchar.h>
 #include <locale.h>
 
+#include <vector>
+
 #include "TemporaryFile.h"
 
 TEST(stdio, flockfile_18208568_stderr) {
@@ -872,7 +874,7 @@
 
   time_t t0 = time(NULL);
   for (size_t i = 0; i < 1024; ++i) {
-    fread(buf, 64*1024, 1, fp);
+    ASSERT_EQ(1U, fread(buf, 64*1024, 1, fp));
   }
   time_t t1 = time(NULL);
 
@@ -911,3 +913,55 @@
 
   fclose(fp);
 }
+
+static void test_fread_from_write_only_stream(size_t n) {
+  FILE* fp = fopen("/dev/null", "w");
+  std::vector<char> buf(n, 0);
+  errno = 0;
+  ASSERT_EQ(0U, fread(&buf[0], n, 1, fp));
+  ASSERT_EQ(EBADF, errno);
+  ASSERT_TRUE(ferror(fp));
+  ASSERT_FALSE(feof(fp));
+  fclose(fp);
+}
+
+TEST(stdio, fread_from_write_only_stream_slow_path) {
+  test_fread_from_write_only_stream(1);
+}
+
+TEST(stdio, fread_from_write_only_stream_fast_path) {
+  test_fread_from_write_only_stream(64*1024);
+}
+
+static void test_fwrite_after_fread(size_t n) {
+  TemporaryFile tf;
+
+  FILE* fp = fdopen(tf.fd, "w+");
+  ASSERT_EQ(1U, fwrite("1", 1, 1, fp));
+  fflush(fp);
+
+  // We've flushed but not rewound, so there's nothing to read.
+  std::vector<char> buf(n, 0);
+  ASSERT_EQ(0U, fread(&buf[0], 1, buf.size(), fp));
+  ASSERT_TRUE(feof(fp));
+
+  // But hitting EOF doesn't prevent us from writing...
+  errno = 0;
+  ASSERT_EQ(1U, fwrite("2", 1, 1, fp)) << errno;
+
+  // And if we rewind, everything's there.
+  rewind(fp);
+  ASSERT_EQ(2U, fread(&buf[0], 1, buf.size(), fp));
+  ASSERT_EQ('1', buf[0]);
+  ASSERT_EQ('2', buf[1]);
+
+  fclose(fp);
+}
+
+TEST(stdio, fwrite_after_fread_slow_path) {
+  test_fwrite_after_fread(16);
+}
+
+TEST(stdio, fwrite_after_fread_fast_path) {
+  test_fwrite_after_fread(64*1024);
+}