Merge "tests/utils.h should #include <sys/sysmacros.h>"
diff --git a/libc/Android.bp b/libc/Android.bp
index b2ebe09..3877145 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -613,6 +613,13 @@
                     "upstream-openbsd/lib/libc/string/strcat.c",
                 ],
             },
+            cortex_a73: {
+                exclude_srcs: [
+                    "upstream-openbsd/lib/libc/string/memmove.c",
+                    "upstream-openbsd/lib/libc/string/stpcpy.c",
+                    "upstream-openbsd/lib/libc/string/strcat.c",
+                ],
+            },
             denver: {
                 exclude_srcs: [
                     "upstream-openbsd/lib/libc/string/memmove.c",
@@ -969,6 +976,32 @@
                     "bionic/__strcpy_chk.cpp",
                 ],
             },
+            cortex_a73: {
+                srcs: [
+                    "arch-arm/cortex-a7/bionic/memset.S",
+
+                    "arch-arm/denver/bionic/memcpy.S",
+                    "arch-arm/denver/bionic/memmove.S",
+                    "arch-arm/denver/bionic/__strcat_chk.S",
+                    "arch-arm/denver/bionic/__strcpy_chk.S",
+
+                    "arch-arm/krait/bionic/strcmp.S",
+
+                    "arch-arm/cortex-a15/bionic/stpcpy.S",
+                    "arch-arm/cortex-a15/bionic/strcat.S",
+                    "arch-arm/cortex-a15/bionic/strcpy.S",
+                    "arch-arm/cortex-a15/bionic/strlen.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm/generic/bionic/memcpy.S",
+                    "arch-arm/generic/bionic/memset.S",
+                    "arch-arm/generic/bionic/strcmp.S",
+                    "arch-arm/generic/bionic/strcpy.S",
+                    "arch-arm/generic/bionic/strlen.c",
+                    "bionic/__strcat_chk.cpp",
+                    "bionic/__strcpy_chk.cpp",
+                ],
+            },
             denver: {
                 srcs: [
                     "arch-arm/denver/bionic/memcpy.S",
@@ -1092,6 +1125,14 @@
                     "arch-arm64/generic/bionic/memmove.S",
                 ],
             },
+            cortex_a73: {
+                srcs: [
+                    "arch-arm64/cortex-a53/bionic/memmove.S",
+                ],
+                exclude_srcs: [
+                    "arch-arm64/generic/bionic/memmove.S",
+                ],
+            },
         },
 
         mips: {
diff --git a/libc/arch-arm64/generic/bionic/memset.S b/libc/arch-arm64/generic/bionic/memset.S
index 3416cfb..12fc09d 100644
--- a/libc/arch-arm64/generic/bionic/memset.S
+++ b/libc/arch-arm64/generic/bionic/memset.S
@@ -80,7 +80,7 @@
 #define tmp1w		w5
 #define tmp2		x6
 #define tmp2w		w6
-#define zva_len		x5
+#define zva_len		x7
 #define zva_lenw	w7
 
 #define L(l) .L ## l
diff --git a/libc/bionic/jemalloc.h b/libc/bionic/jemalloc.h
index fceb323..f7e8770 100644
--- a/libc/bionic/jemalloc.h
+++ b/libc/bionic/jemalloc.h
@@ -26,6 +26,7 @@
 __BEGIN_DECLS
 
 struct mallinfo je_mallinfo();
+int je_mallopt(int, int);
 int je_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
 void je_malloc_disable();
 void je_malloc_enable();
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index e33d560..266b966 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <malloc.h>
 #include <sys/param.h>
 #include <unistd.h>
 
@@ -46,3 +47,38 @@
   }
   return je_memalign(boundary, size);
 }
+
+int je_mallopt(int param, int value) {
+  // The only parameter we currently understand is M_DECAY_TIME.
+  if (param == M_DECAY_TIME) {
+    // Only support setting the value to 1 or 0.
+    ssize_t decay_time;
+    if (value) {
+      decay_time = 1;
+    } else {
+      decay_time = 0;
+    }
+    // First get the total number of arenas.
+    unsigned narenas;
+    size_t sz = sizeof(unsigned);
+    if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
+      return 0;
+    }
+
+    // Set the decay time for any arenas that will be created in the future.
+    if (je_mallctl("arenas.decay_time", nullptr, nullptr, &decay_time, sizeof(decay_time)) != 0) {
+      return 0;
+    }
+
+    // Change the decay on the already existing arenas.
+    char buffer[100];
+    for (unsigned i = 0; i < narenas; i++) {
+      snprintf(buffer, sizeof(buffer), "arena.%d.decay_time", i);
+      if (je_mallctl(buffer, nullptr, nullptr, &decay_time, sizeof(decay_time)) != 0) {
+        break;
+      }
+    }
+    return 1;
+  }
+  return 0;
+}
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 18c998d..1f201d1 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -68,6 +68,7 @@
     Malloc(iterate),
     Malloc(malloc_disable),
     Malloc(malloc_enable),
+    Malloc(mallopt),
   };
 
 // In a VM process, this is set to 1 after fork()ing out of zygote.
@@ -101,6 +102,14 @@
   return Malloc(mallinfo)();
 }
 
+extern "C" int mallopt(int param, int value) {
+  auto _mallopt = __libc_globals->malloc_dispatch.mallopt;
+  if (__predict_false(_mallopt != nullptr)) {
+    return _mallopt(param, value);
+  }
+  return Malloc(mallopt)(param, value);
+}
+
 extern "C" void* malloc(size_t bytes) {
   auto _malloc = __libc_globals->malloc_dispatch.malloc;
   if (__predict_false(_malloc != nullptr)) {
@@ -247,6 +256,10 @@
                                           prefix, "mallinfo")) {
     return false;
   }
+  if (!InitMallocFunction<MallocMallopt>(malloc_impl_handler, &table->mallopt,
+                                         prefix, "mallopt")) {
+    return false;
+  }
   if (!InitMallocFunction<MallocMalloc>(malloc_impl_handler, &table->malloc,
                                         prefix, "malloc")) {
     return false;
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index 412e3f0..db5da04 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -77,6 +77,11 @@
  */
 int malloc_info(int, FILE*) __INTRODUCED_IN(23);
 
+/* mallopt options */
+#define M_DECAY_TIME -100
+
+int mallopt(int, int) __INTRODUCED_IN(26);
+
 __END_DECLS
 
 #endif  /* LIBC_INCLUDE_MALLOC_H_ */
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 7500742..4654f99 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1551,4 +1551,5 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
+    mallopt;
 } LIBC_O;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 9875eda..2c04e4d 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1268,4 +1268,5 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
+    mallopt;
 } LIBC_O;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 93d31a7..f68f1c5 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1392,4 +1392,5 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
+    mallopt;
 } LIBC_O;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 9875eda..2c04e4d 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1268,4 +1268,5 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
+    mallopt;
 } LIBC_O;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 08ef8be..bea2fe9 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1391,4 +1391,5 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
+    mallopt;
 } LIBC_O;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 9875eda..2c04e4d 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1268,4 +1268,5 @@
     malloc_disable;
     malloc_enable;
     malloc_iterate;
+    mallopt;
 } LIBC_O;
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index addb5d4..014d385 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -77,6 +77,7 @@
 void* debug_realloc(void* pointer, size_t bytes);
 void* debug_calloc(size_t nmemb, size_t bytes);
 struct mallinfo debug_mallinfo();
+int debug_mallopt(int param, int value);
 int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
 int debug_iterate(uintptr_t base, size_t size,
     void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
@@ -639,6 +640,10 @@
   return g_dispatch->mallinfo();
 }
 
+int debug_mallopt(int param, int value) {
+  return g_dispatch->mallopt(param, value);
+}
+
 int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
   if (DebugCallsDisabled()) {
     return g_dispatch->posix_memalign(memptr, alignment, size);
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 219c21e..4fdba2e 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -58,6 +58,7 @@
 void debug_free_malloc_leak_info(uint8_t*);
 
 struct mallinfo debug_mallinfo();
+int debug_mallopt(int, int);
 
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 void* debug_pvalloc(size_t);
@@ -128,6 +129,7 @@
   nullptr,
   nullptr,
   nullptr,
+  mallopt,
 };
 
 void VerifyAllocCalls() {
@@ -1474,6 +1476,20 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
+TEST_F(MallocDebugTest, debug_mallopt) {
+  Init("guard");
+
+  void* pointer = debug_malloc(150);
+  ASSERT_TRUE(pointer != nullptr);
+
+  EXPECT_EQ(0, debug_mallopt(-1000, 1));
+
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
 TEST_F(MallocDebugTest, debug_posix_memalign) {
   Init("guard");
 
diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h
index 02a092f..cdae466 100644
--- a/libc/private/bionic_malloc_dispatch.h
+++ b/libc/private/bionic_malloc_dispatch.h
@@ -45,6 +45,7 @@
 typedef int (*MallocIterate)(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
 typedef void (*MallocMallocDisable)();
 typedef void (*MallocMallocEnable)();
+typedef int (*MallocMallopt)(int, int);
 
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 typedef void* (*MallocPvalloc)(size_t);
@@ -69,6 +70,7 @@
   MallocIterate iterate;
   MallocMallocDisable malloc_disable;
   MallocMallocEnable malloc_enable;
+  MallocMallopt mallopt;
 } __attribute__((aligned(32)));
 
 #endif
diff --git a/tests/leak_test.cpp b/tests/leak_test.cpp
index 9ddb2ff..a356640 100644
--- a/tests/leak_test.cpp
+++ b/tests/leak_test.cpp
@@ -49,28 +49,61 @@
   return result;
 }
 
-#define LEAK_TEST(test_case_name, test_name)                                                 \
-  static void __leak_test__##test_case_name##__##test_name();                                \
-  TEST(test_case_name, test_name) {                                                          \
-    auto previous_size = GetMappingSize();                                                   \
-    __leak_test__##test_case_name##__##test_name();                                          \
-    auto current_size = GetMappingSize();                                                    \
-    if (current_size > previous_size) {                                                      \
-      FAIL() << "increase in process map size: " << previous_size << " -> " << current_size; \
-    }                                                                                        \
-  }                                                                                          \
-  static void __leak_test__##test_case_name##__##test_name()
-
-LEAK_TEST(leak, smoke) {
-  // Do nothing.
+static void WaitUntilAllExited(pid_t* pids, size_t pid_count) {
+  // Wait until all children have exited.
+  bool alive = true;
+  while (alive) {
+    alive = false;
+    for (size_t i = 0; i < pid_count; ++i) {
+      if (pids[i] != 0) {
+        if (kill(pids[i], 0) == 0) {
+          alive = true;
+        } else {
+          EXPECT_EQ(errno, ESRCH);
+          pids[i] = 0;  // Skip in next loop.
+        }
+      }
+    }
+  }
 }
 
-LEAK_TEST(leak, xfail) {
-  UNUSED(mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+class LeakChecker {
+ public:
+  LeakChecker() {
+    Reset();
+  }
+
+  ~LeakChecker() {
+    Check();
+  }
+
+  void Reset() {
+    previous_size_ = GetMappingSize();
+  }
+
+  void DumpTo(std::ostream& os) const {
+    os << previous_size_;
+  }
+
+ private:
+  size_t previous_size_;
+
+  void Check() {
+    auto current_size = GetMappingSize();
+    if (current_size > previous_size_) {
+      FAIL() << "increase in process map size: " << previous_size_ << " -> " << current_size;
+    }
+  }
+};
+
+std::ostream& operator<<(std::ostream& os, const LeakChecker& lc) {
+  lc.DumpTo(os);
+  return os;
 }
 
 // http://b/36045112
-LEAK_TEST(pthread_leak, join) {
+TEST(pthread_leak, join) {
+  LeakChecker lc;
   for (int i = 0; i < 100; ++i) {
     pthread_t thread;
     ASSERT_EQ(0, pthread_create(&thread, nullptr, [](void*) -> void* { return nullptr; }, nullptr));
@@ -79,22 +112,42 @@
 }
 
 // http://b/36045112
-LEAK_TEST(pthread_leak, detach) {
-  pthread_barrier_t barrier;
-  constexpr int thread_count = 100;
-  ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, thread_count + 1));
-  for (int i = 0; i < thread_count; ++i) {
-    pthread_t thread;
-    const auto thread_function = +[](void* barrier) -> void* {
-      pthread_barrier_wait(static_cast<pthread_barrier_t*>(barrier));
-      return nullptr;
-    };
-    ASSERT_EQ(0, pthread_create(&thread, nullptr, thread_function, &barrier));
-    ASSERT_EQ(0, pthread_detach(thread));
+TEST(pthread_leak, detach) {
+  LeakChecker lc;
+
+  for (size_t pass = 0; pass < 2; ++pass) {
+    pthread_barrier_t barrier;
+    constexpr int thread_count = 100;
+    ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, thread_count + 1), 0);
+
+    // Start child threads.
+    struct thread_data { pthread_barrier_t* barrier; pid_t* tid; };
+    pid_t tids[thread_count];
+    for (int i = 0; i < thread_count; ++i) {
+      thread_data* td = new thread_data{&barrier, &tids[i]};
+      const auto thread_function = +[](void* ptr) -> void* {
+        thread_data* data = static_cast<thread_data*>(ptr);
+        *data->tid = gettid();
+        pthread_barrier_wait(data->barrier);
+        // Doing this delete allocates new VMAs for jemalloc bookkeeping,
+        // but the two-pass nature of this test means we can check that
+        // it's a pool rather than an unbounded leak.
+        delete data;
+        return nullptr;
+      };
+      pthread_t thread;
+      ASSERT_EQ(0, pthread_create(&thread, nullptr, thread_function, td));
+      ASSERT_EQ(0, pthread_detach(thread));
+    }
+
+    pthread_barrier_wait(&barrier);
+    ASSERT_EQ(pthread_barrier_destroy(&barrier), 0);
+
+    WaitUntilAllExited(tids, arraysize(tids));
+
+    // houdini keeps a thread pool, so we ignore the first pass while the
+    // pool fills, but then on the second pass require that the "pool" isn't
+    // actually an unbounded leak. https://issuetracker.google.com/37920774.
+    if (pass == 0) lc.Reset();
   }
-
-  pthread_barrier_wait(&barrier);
-
-  // Give the threads some time to exit.
-  std::this_thread::sleep_for(100ms);
 }
diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk
index 37499ba..0220ae2 100644
--- a/tests/libs/Android.build.dlext_testzip.mk
+++ b/tests/libs/Android.build.dlext_testzip.mk
@@ -22,55 +22,47 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE := libdlext_test_zip_zipaligned
 LOCAL_MODULE_SUFFIX := .zip
-LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test_zip
 LOCAL_2ND_ARCH_VAR_PREFIX := $(bionic_2nd_arch_prefix)
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
 my_shared_libs := \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libdlext_test_zip.so \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libatest_simple_zip.so
+  $(call intermediates-dir-for,SHARED_LIBRARIES,libdlext_test_zip,,,$(bionic_2nd_arch_prefix))/libdlext_test_zip.so \
+  $(call intermediates-dir-for,SHARED_LIBRARIES,libatest_simple_zip,,,$(bionic_2nd_arch_prefix))/libatest_simple_zip.so
 
-$(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(BIONIC_TESTS_ZIPALIGN)
+$(LOCAL_BUILT_MODULE): PRIVATE_SHARED_LIBS := $(my_shared_libs)
+$(LOCAL_BUILT_MODULE): $(my_shared_libs) $(BIONIC_TESTS_ZIPALIGN)
 	@echo "Aligning zip: $@"
 	$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir
-	$(hide) cp $^ $(dir $@)/libdir
+	$(hide) cp $(PRIVATE_SHARED_LIBS) $(dir $@)/libdir
 	$(hide) (cd $(dir $@) && touch empty_file.txt && zip -qrD0 $(notdir $@).unaligned empty_file.txt libdir/*.so)
 	$(hide) $(BIONIC_TESTS_ZIPALIGN) 4096 $@.unaligned $@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE := libdlext_test_runpath_zip_zipaligned
 LOCAL_MODULE_SUFFIX := .zip
-LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test_runpath_zip
 LOCAL_2ND_ARCH_VAR_PREFIX := $(bionic_2nd_arch_prefix)
 
 include $(BUILD_SYSTEM)/base_rules.mk
-my_shared_libs := \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_d_zip.so \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_b.so \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_a.so \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_c.so \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_x.so
+lib_d := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_d_zip,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_d_zip.so
+lib_a := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_a,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_a.so
+lib_b := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_b,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_b.so
+lib_c := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_c,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_c.so
+lib_x := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_x,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_x.so
 
-
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_D := \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_d_zip.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_A := \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_a.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_B := \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_b.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_C := \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_c.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_X := \
-  $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_x.so
-$(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(BIONIC_TESTS_ZIPALIGN)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_D := $(lib_d)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_A := $(lib_a)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_B := $(lib_b)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_C := $(lib_c)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_X := $(lib_x)
+$(LOCAL_BUILT_MODULE) : $(lib_d) $(lib_a) $(lib_b) $(lib_c) $(lib_x) $(BIONIC_TESTS_ZIPALIGN)
 	@echo "Aligning zip: $@"
 	$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir && \
     mkdir -p $(dir $@)/libdir/dt_runpath_a && mkdir -p $(dir $@)/libdir/dt_runpath_b_c_x
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 8fba1c4..a7b9d52 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -500,3 +500,10 @@
   delete[] values_64;
   delete[] values_ldouble;
 }
+
+TEST(malloc, mallopt_smoke) {
+  errno = 0;
+  ASSERT_EQ(0, mallopt(-1000, 1));
+  // mallopt doesn't set errno.
+  ASSERT_EQ(0, errno);
+}