Merge "Implement malloc_info(3)."
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 38ae831..bfa13b7 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -315,7 +315,7 @@
 int     cacheflush:__ARM_NR_cacheflush(long start, long end, long flags)  arm
 
 # MIPS-specific
-int     _flush_cache:cacheflush(char* addr, const int nbytes, const int op) mips,mips64
+int     _flush_cache:cacheflush(char* addr, const int nbytes, const int op) mips
 int     __set_tls:set_thread_area(void*) mips,mips64
 
 # x86-specific
diff --git a/libc/arch-mips64/syscalls/_flush_cache.S b/libc/arch-mips64/syscalls/_flush_cache.S
deleted file mode 100644
index 997ccec..0000000
--- a/libc/arch-mips64/syscalls/_flush_cache.S
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Generated by gensyscalls.py. Do not edit. */
-
-#include <private/bionic_asm.h>
-
-    .hidden __set_errno
-
-ENTRY(_flush_cache)
-    .set push
-    .set noreorder
-    li v0, __NR_cacheflush
-    syscall
-    bnez a3, 1f
-    move a0, v0
-    j ra
-    nop
-1:
-    move t0, ra
-    bal     2f
-    nop
-2:
-    .cpsetup ra, t1, 2b
-    LA t9,__set_errno
-    .cpreturn
-    j t9
-    move ra, t0
-    .set pop
-END(_flush_cache)
diff --git a/libc/include/elf.h b/libc/include/elf.h
index faae73e..7a9485a 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -80,6 +80,4 @@
 #define STT_LOPROC    13
 #define STT_HIPROC    15
 
-#define R_386_IRELATIVE  42
-
 #endif /* _ELF_H */
diff --git a/libc/include/sys/cachectl.h b/libc/include/sys/cachectl.h
index 57e6ae7..a302ff8 100644
--- a/libc/include/sys/cachectl.h
+++ b/libc/include/sys/cachectl.h
@@ -31,6 +31,5 @@
 #ifdef __mips__
 #include <asm/cachectl.h>
 extern int __cachectl (void *addr, __const int nbytes, __const int op);
-extern int _flush_cache (char *addr, __const int nbytes, __const int op);
 #endif
 #endif /* sys/cachectl.h */
diff --git a/libc/tools/check-symbols-glibc.py b/libc/tools/check-symbols-glibc.py
index 8326730..8bcf7fc 100755
--- a/libc/tools/check-symbols-glibc.py
+++ b/libc/tools/check-symbols-glibc.py
@@ -70,6 +70,7 @@
   '__res_mkquery': 'res_mkquery',
   '__res_query': 'res_query',
   '__res_search': 'res_search',
+  '__xpg_basename': '__gnu_basename',
 }
 
 glibc = GetSymbolsFromSystemSo('libc.so.*', 'librt.so.*', 'libpthread.so.*', 'libresolv.so.*', 'libm.so.*')
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 8a7c073..e88195f 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -466,14 +466,17 @@
   return NULL;
 }
 
-static void resolve_ifunc_symbols(soinfo* si) {
+void soinfo::resolve_ifunc_symbols() {
+  if (!get_has_ifuncs()) {
+    return;
+  }
 
-  phdr_table_unprotect_segments(si->phdr, si->phnum, si->load_bias);
+  phdr_table_unprotect_segments(phdr, phnum, load_bias);
 
   TRACE_TYPE(IFUNC, "CHECKING FOR IFUNCS AND PERFORMING SYMBOL UPDATES");
 
-  for (size_t i = 0; i < si->nchain; ++i) {
-    ElfW(Sym)* s = &si->symtab[i];
+  for (size_t i = 0; i < nchain; ++i) {
+    ElfW(Sym)* s = &symtab[i];
     if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
       // The address of the ifunc in the symbol table is the address of the
       // function that chooses the function to which the ifunc will refer.
@@ -481,12 +484,12 @@
       // in the linker and then return its result (minus the base offset).
       TRACE_TYPE(IFUNC, "FOUND IFUNC");
       ElfW(Addr) (*ifunc_ptr)();
-      ifunc_ptr = reinterpret_cast<ElfW(Addr)(*)()>(s->st_value + si->base);
-      s->st_value = (ifunc_ptr() - si->base);
+      ifunc_ptr = reinterpret_cast<ElfW(Addr)(*)()>(s->st_value + base);
+      s->st_value = (ifunc_ptr() - base);
       TRACE_TYPE(IFUNC, "NEW VALUE IS %p", (void*)s->st_value);
     }
   }
-  phdr_table_protect_segments(si->phdr, si->phnum, si->load_bias);
+  phdr_table_protect_segments(phdr, phnum, load_bias);
 }
 
 static unsigned elfhash(const char* _name) {
@@ -809,10 +812,6 @@
       return NULL;
     }
 
-    // if the library has any ifuncs, we will need to resolve them so that dlsym
-    // can handle them properly
-    resolve_ifunc_symbols(si);
-
     return si;
 }
 
@@ -1567,6 +1566,8 @@
   // DT_INIT should be called before DT_INIT_ARRAY if both are present.
   CallFunction("DT_INIT", init_func);
   CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);
+
+  resolve_ifunc_symbols();
 }
 
 void soinfo::CallDestructors() {
diff --git a/linker/linker.h b/linker/linker.h
index d7cf24b..684561a 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -216,6 +216,7 @@
  private:
   void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
   void CallFunction(const char* function_name, linker_function_t function);
+  void resolve_ifunc_symbols();
 
  private:
   // This part of the structure is only available
diff --git a/tests/Android.mk b/tests/Android.mk
index d5f82ba..4f1661a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -362,6 +362,22 @@
 		$(TARGET_OUT_DATA_NATIVE_TESTS)/bionic-unit-tests/bionic-unit-tests$(NATIVE_TEST_SUFFIX) $(BIONIC_TEST_FLAGS)
 endif
 
+ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),x86_64))
+# add target to run lp32 tests
+bionic-unit-tests-run-on-host32: bionic-unit-tests $(TARGET_OUT_EXECUTABLES)/$(LINKER) $(TARGET_OUT_EXECUTABLES)/sh
+	if [ ! -d /system -o ! -d /system/bin ]; then \
+	  echo "Attempting to create /system/bin"; \
+	  sudo mkdir -p -m 0777 /system/bin; \
+	fi
+	mkdir -p $(TARGET_OUT_DATA)/local/tmp
+	cp $(TARGET_OUT_EXECUTABLES)/linker /system/bin
+	cp $(TARGET_OUT_EXECUTABLES)/sh /system/bin
+	ANDROID_DATA=$(TARGET_OUT_DATA) \
+	ANDROID_ROOT=$(TARGET_OUT) \
+	LD_LIBRARY_PATH=$(2ND_TARGET_OUT_SHARED_LIBRARIES) \
+		$(2ND_TARGET_OUT_DATA_NATIVE_TESTS)/bionic-unit-tests/bionic-unit-tests32 $(BIONIC_TEST_FLAGS)
+endif
+
 endif # linux-x86
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index fc788ac..a731748 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -90,32 +90,42 @@
 // ifuncs are only supported on intel for now
 #if defined(__i386__) || defined(__x86_64__)
 TEST(dlfcn, ifunc) {
-  const char* (*foo_ptr)();
-  const char* (*foo_library_ptr)();
+  typedef const char* (*fn_ptr)();
 
   // ifunc's choice depends on whether IFUNC_CHOICE has a value
   // first check the set case
   setenv("IFUNC_CHOICE", "set", 1);
   void* handle = dlopen("libtest_ifunc.so", RTLD_NOW);
   ASSERT_TRUE(handle != NULL);
-  *(void **)(&foo_ptr) = dlsym(handle, "foo");
-  *(void **)(&foo_library_ptr) = dlsym(handle, "foo_library");
+  fn_ptr foo_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo"));
+  fn_ptr foo_library_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo_library"));
   ASSERT_TRUE(foo_ptr != NULL);
   ASSERT_TRUE(foo_library_ptr != NULL);
-  ASSERT_EQ(strncmp("set", (*foo_ptr)(), 3), 0);
-  ASSERT_EQ(strncmp("set", (*foo_library_ptr)(), 3), 0);
+  ASSERT_EQ(strncmp("set", foo_ptr(), 3), 0);
+  ASSERT_EQ(strncmp("set", foo_library_ptr(), 3), 0);
   dlclose(handle);
 
   // then check the unset case
   unsetenv("IFUNC_CHOICE");
   handle = dlopen("libtest_ifunc.so", RTLD_NOW);
   ASSERT_TRUE(handle != NULL);
-  *(void **)(&foo_ptr) = dlsym(handle, "foo");
-  *(void **)(&foo_library_ptr) = dlsym(handle, "foo_library");
+  foo_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo"));
+  foo_library_ptr = reinterpret_cast<fn_ptr>(dlsym(handle, "foo_library"));
   ASSERT_TRUE(foo_ptr != NULL);
   ASSERT_TRUE(foo_library_ptr != NULL);
-  ASSERT_EQ(strncmp("unset", (*foo_ptr)(), 5), 0);
-  ASSERT_EQ(strncmp("unset", (*foo_library_ptr)(), 3), 0);
+  ASSERT_EQ(strncmp("unset", foo_ptr(), 5), 0);
+  ASSERT_EQ(strncmp("unset", foo_library_ptr(), 3), 0);
+  dlclose(handle);
+}
+
+TEST(dlfcn, ifunc_ctor_call) {
+  typedef const char* (*fn_ptr)();
+
+  void* handle = dlopen("libtest_ifunc.so", RTLD_NOW);
+  ASSERT_TRUE(handle != NULL) << dlerror();
+  fn_ptr is_ctor_called =  reinterpret_cast<fn_ptr>(dlsym(handle, "is_ctor_called"));
+  ASSERT_TRUE(is_ctor_called != NULL) << dlerror();
+  ASSERT_STREQ("true", is_ctor_called());
   dlclose(handle);
 }
 #endif
diff --git a/tests/libs/dlopen_testlib_ifunc.c b/tests/libs/dlopen_testlib_ifunc.c
index 1c4bafa..4874841 100644
--- a/tests/libs/dlopen_testlib_ifunc.c
+++ b/tests/libs/dlopen_testlib_ifunc.c
@@ -17,7 +17,22 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+static int g_flag = 0;
+
+static void __attribute__((constructor)) init_flag() {
+  g_flag = 1;
+}
+
 const char* foo() __attribute__ ((ifunc ("foo_ifunc")));
+const char* is_ctor_called() __attribute__ ((ifunc("is_ctor_called_ifun")));
+
+const char* return_true() {
+  return "true";
+}
+
+const char* return_false() {
+  return "false";
+}
 
 const char* f1() {
   return "unset";
@@ -27,6 +42,10 @@
   return "set";
 }
 
+void* is_ctor_called_ifun() {
+  return g_flag == 0 ? return_false : return_true;
+}
+
 void* foo_ifunc() {
    char* choice = getenv("IFUNC_CHOICE");
    return choice == NULL ? f1 : f2;
@@ -34,4 +53,4 @@
 
 const char* foo_library() {
    return foo();
-}
\ No newline at end of file
+}
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 2a65657..8195ea8 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -431,6 +431,10 @@
 
   void* child_stack[1024];
   int clone_result = clone(GetPidCachingCloneStartRoutine, &child_stack[1024], CLONE_NEWNS | SIGCHLD, NULL);
+  if (clone_result == -1 && errno == EPERM && getuid() != 0) {
+    GTEST_LOG_(INFO) << "This test only works if you have permission to CLONE_NEWNS; try running as root.\n";
+    return;
+  }
   ASSERT_NE(clone_result, -1);
 
   ASSERT_EQ(parent_pid, getpid());