am ab4e2ed1: am db4fdf1a: Merge "Fix dlerror(3)."

* commit 'ab4e2ed182011998751ff5d6c321ff5bae20c6cd':
  Fix dlerror(3).
diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp
index 9e6d92e..4190a1d 100644
--- a/libc/bionic/malloc_debug_check.cpp
+++ b/libc/bionic/malloc_debug_check.cpp
@@ -26,31 +26,30 @@
  * SUCH DAMAGE.
  */
 
-#include <errno.h>
-#include <pthread.h>
-#include <time.h>
-#include <stdio.h>
 #include <arpa/inet.h>
-#include <sys/socket.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <fcntl.h>
-#include <unwind.h>
-#include <dlfcn.h>
-#include <stdbool.h>
-
-#include <sys/types.h>
+#include <sys/socket.h>
 #include <sys/system_properties.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <unwind.h>
 
 #include "dlmalloc.h"
 #include "logd.h"
-
-#include "malloc_debug_common.h"
 #include "malloc_debug_check_mapinfo.h"
+#include "malloc_debug_common.h"
+#include "ScopedPthreadMutexLocker.h"
 
 static mapinfo *milist;
 
diff --git a/libc/bionic/malloc_debug_common.cpp b/libc/bionic/malloc_debug_common.cpp
index adcc238..0594c0a 100644
--- a/libc/bionic/malloc_debug_common.cpp
+++ b/libc/bionic/malloc_debug_common.cpp
@@ -40,12 +40,15 @@
  * or static (libc.a) linking.
  */
 
-#include <stdlib.h>
-#include <pthread.h>
-#include <unistd.h>
-#include "dlmalloc.h"
 #include "malloc_debug_common.h"
 
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "dlmalloc.h"
+#include "ScopedPthreadMutexLocker.h"
+
 /*
  * In a VM process, this is set to 1 after fork()ing out of zygote.
  */
diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h
index 45b4e36..78ad5e5 100644
--- a/libc/bionic/malloc_debug_common.h
+++ b/libc/bionic/malloc_debug_common.h
@@ -33,6 +33,8 @@
 #ifndef MALLOC_DEBUG_COMMON_H
 #define MALLOC_DEBUG_COMMON_H
 
+#include <stdlib.h>
+
 #define HASHTABLE_SIZE      1543
 #define BACKTRACE_SIZE      32
 /* flag definitions, currently sharing storage with "size" */
@@ -101,18 +103,4 @@
 #define info_log(format, ...)  \
     __libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ )
 
-class ScopedPthreadMutexLocker {
- public:
-  explicit ScopedPthreadMutexLocker(pthread_mutex_t* mu) : mu_(mu) {
-    pthread_mutex_lock(mu_);
-  }
-
-  ~ScopedPthreadMutexLocker() {
-    pthread_mutex_unlock(mu_);
-  }
-
- private:
-  pthread_mutex_t* mu_;
-};
-
 #endif  // MALLOC_DEBUG_COMMON_H
diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp
index ca00d4d..090a981 100644
--- a/libc/bionic/malloc_debug_leak.cpp
+++ b/libc/bionic/malloc_debug_leak.cpp
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <arpa/inet.h>
 #include <dlfcn.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -36,19 +37,18 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
-#include <unwind.h>
-
-#include <arpa/inet.h>
 #include <sys/select.h>
 #include <sys/socket.h>
 #include <sys/system_properties.h>
 #include <sys/types.h>
 #include <sys/un.h>
+#include <unistd.h>
+#include <unwind.h>
 
 #include "dlmalloc.h"
 #include "logd.h"
 #include "malloc_debug_common.h"
+#include "ScopedPthreadMutexLocker.h"
 
 // This file should be included into the build only when
 // MALLOC_LEAK_CHECK, or MALLOC_QEMU_INSTRUMENT, or both
diff --git a/libc/private/ScopedPthreadMutexLocker.h b/libc/private/ScopedPthreadMutexLocker.h
new file mode 100644
index 0000000..06b8e37
--- /dev/null
+++ b/libc/private/ScopedPthreadMutexLocker.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef SCOPED_PTHREAD_MUTEX_LOCKER_H
+#define SCOPED_PTHREAD_MUTEX_LOCKER_H
+
+#include <pthread.h>
+
+class ScopedPthreadMutexLocker {
+ public:
+  explicit ScopedPthreadMutexLocker(pthread_mutex_t* mu) : mu_(mu) {
+    pthread_mutex_lock(mu_);
+  }
+
+  ~ScopedPthreadMutexLocker() {
+    pthread_mutex_unlock(mu_);
+  }
+
+ private:
+  pthread_mutex_t* mu_;
+
+  // Disallow copy and assignment.
+  ScopedPthreadMutexLocker(const ScopedPthreadMutexLocker&);
+  void operator=(const ScopedPthreadMutexLocker&);
+};
+
+#endif // SCOPED_PTHREAD_MUTEX_LOCKER_H
diff --git a/linker/Android.mk b/linker/Android.mk
index 7b5da62..2d19827 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -4,7 +4,7 @@
 LOCAL_SRC_FILES:= \
 	arch/$(TARGET_ARCH)/begin.S \
 	debugger.c \
-	dlfcn.c \
+	dlfcn.cpp \
 	linker.cpp \
 	linker_environ.c \
 	linker_format.c \
diff --git a/linker/dlfcn.c b/linker/dlfcn.c
deleted file mode 100644
index 920137e..0000000
--- a/linker/dlfcn.c
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2007 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>
-#include <pthread.h>
-#include <stdio.h>
-#include "linker.h"
-#include "linker_format.h"
-
-/* This file hijacks the symbols stubbed out in libdl.so. */
-
-#define DL_SUCCESS                    0
-#define DL_ERR_CANNOT_LOAD_LIBRARY    1
-#define DL_ERR_INVALID_LIBRARY_HANDLE 2
-#define DL_ERR_BAD_SYMBOL_NAME        3
-#define DL_ERR_SYMBOL_NOT_FOUND       4
-#define DL_ERR_SYMBOL_NOT_GLOBAL      5
-
-static char dl_err_buf[1024];
-static const char *dl_err_str;
-
-static const char *dl_errors[] = {
-    [DL_ERR_CANNOT_LOAD_LIBRARY] = "Cannot load library",
-    [DL_ERR_INVALID_LIBRARY_HANDLE] = "Invalid library handle",
-    [DL_ERR_BAD_SYMBOL_NAME] = "Invalid symbol name",
-    [DL_ERR_SYMBOL_NOT_FOUND] = "Symbol not found",
-    [DL_ERR_SYMBOL_NOT_GLOBAL] = "Symbol is not global",
-};
-
-#define likely(expr)   __builtin_expect (expr, 1)
-#define unlikely(expr) __builtin_expect (expr, 0)
-
-pthread_mutex_t dl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
-
-static void set_dlerror(int err)
-{
-    format_buffer(dl_err_buf, sizeof(dl_err_buf), "%s: %s", dl_errors[err],
-             linker_get_error());
-    dl_err_str = (const char *)&dl_err_buf[0];
-};
-
-void *dlopen(const char *filename, int flag)
-{
-    soinfo *ret;
-
-    pthread_mutex_lock(&dl_lock);
-    ret = find_library(filename);
-    if (unlikely(ret == NULL)) {
-        set_dlerror(DL_ERR_CANNOT_LOAD_LIBRARY);
-    } else {
-        soinfo_call_constructors(ret);
-        ret->refcount++;
-    }
-    pthread_mutex_unlock(&dl_lock);
-    return ret;
-}
-
-const char *dlerror(void)
-{
-    const char *tmp = dl_err_str;
-    dl_err_str = NULL;
-    return (const char *)tmp;
-}
-
-void *dlsym(void *handle, const char *symbol)
-{
-    soinfo *found;
-    Elf32_Sym *sym;
-    unsigned bind;
-
-    pthread_mutex_lock(&dl_lock);
-
-    if(unlikely(handle == 0)) {
-        set_dlerror(DL_ERR_INVALID_LIBRARY_HANDLE);
-        goto err;
-    }
-    if(unlikely(symbol == 0)) {
-        set_dlerror(DL_ERR_BAD_SYMBOL_NAME);
-        goto err;
-    }
-
-    if(handle == RTLD_DEFAULT) {
-        sym = lookup(symbol, &found, NULL);
-    } else if(handle == RTLD_NEXT) {
-        void *ret_addr = __builtin_return_address(0);
-        soinfo *si = find_containing_library(ret_addr);
-
-        sym = NULL;
-        if(si && si->next) {
-            sym = lookup(symbol, &found, si->next);
-        }
-    } else {
-        found = (soinfo*)handle;
-        sym = soinfo_lookup(found, symbol);
-    }
-
-    if(likely(sym != 0)) {
-        bind = ELF32_ST_BIND(sym->st_info);
-
-        if(likely((bind == STB_GLOBAL) && (sym->st_shndx != 0))) {
-            unsigned ret = sym->st_value + found->load_bias;
-            pthread_mutex_unlock(&dl_lock);
-            return (void*)ret;
-        }
-
-        set_dlerror(DL_ERR_SYMBOL_NOT_GLOBAL);
-    }
-    else
-        set_dlerror(DL_ERR_SYMBOL_NOT_FOUND);
-
-err:
-    pthread_mutex_unlock(&dl_lock);
-    return 0;
-}
-
-int dladdr(const void *addr, Dl_info *info)
-{
-    int ret = 0;
-
-    pthread_mutex_lock(&dl_lock);
-
-    /* Determine if this address can be found in any library currently mapped */
-    soinfo *si = find_containing_library(addr);
-
-    if(si) {
-        memset(info, 0, sizeof(Dl_info));
-
-        info->dli_fname = si->name;
-        /* Address at which the shared object is loaded */
-        info->dli_fbase = (void*)si->base;
-
-        /* Determine if any symbol in the library contains the specified address */
-        Elf32_Sym *sym = soinfo_find_symbol(si, addr);
-
-        if(sym != NULL) {
-            info->dli_sname = si->strtab + sym->st_name;
-            info->dli_saddr = (void*)(si->load_bias + sym->st_value);
-        }
-
-        ret = 1;
-    }
-
-    pthread_mutex_unlock(&dl_lock);
-
-    return ret;
-}
-
-int dlclose(void* handle) {
-    pthread_mutex_lock(&dl_lock);
-    int result = soinfo_unload((soinfo*)handle);
-    pthread_mutex_unlock(&dl_lock);
-    return result;
-}
-
-#if defined(ANDROID_ARM_LINKER)
-//                     0000000 00011111 111112 22222222 2333333 333344444444445555555
-//                     0123456 78901234 567890 12345678 9012345 678901234567890123456
-#define ANDROID_LIBDL_STRTAB \
-                      "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_unwind_find_exidx\0"
-
-#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
-//                     0000000 00011111 111112 22222222 2333333 3333444444444455
-//                     0123456 78901234 567890 12345678 9012345 6789012345678901
-#define ANDROID_LIBDL_STRTAB \
-                      "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_iterate_phdr\0"
-#else
-#error Unsupported architecture. Only ARM, MIPS, and x86 are presently supported.
-#endif
-
-
-static Elf32_Sym libdl_symtab[] = {
-      // total length of libdl_info.strtab, including trailing 0
-      // This is actually the the STH_UNDEF entry. Technically, it's
-      // supposed to have st_name == 0, but instead, it points to an index
-      // in the strtab with a \0 to make iterating through the symtab easier.
-    { st_name: sizeof(ANDROID_LIBDL_STRTAB) - 1,
-    },
-    { st_name: 0,   // starting index of the name in libdl_info.strtab
-      st_value: (Elf32_Addr) &dlopen,
-      st_info: STB_GLOBAL << 4,
-      st_shndx: 1,
-    },
-    { st_name: 7,
-      st_value: (Elf32_Addr) &dlclose,
-      st_info: STB_GLOBAL << 4,
-      st_shndx: 1,
-    },
-    { st_name: 15,
-      st_value: (Elf32_Addr) &dlsym,
-      st_info: STB_GLOBAL << 4,
-      st_shndx: 1,
-    },
-    { st_name: 21,
-      st_value: (Elf32_Addr) &dlerror,
-      st_info: STB_GLOBAL << 4,
-      st_shndx: 1,
-    },
-    { st_name: 29,
-      st_value: (Elf32_Addr) &dladdr,
-      st_info: STB_GLOBAL << 4,
-      st_shndx: 1,
-    },
-#ifdef ANDROID_ARM_LINKER
-    { st_name: 36,
-      st_value: (Elf32_Addr) &dl_unwind_find_exidx,
-      st_info: STB_GLOBAL << 4,
-      st_shndx: 1,
-    },
-#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
-    { st_name: 36,
-      st_value: (Elf32_Addr) &dl_iterate_phdr,
-      st_info: STB_GLOBAL << 4,
-      st_shndx: 1,
-    },
-#endif
-};
-
-/* Fake out a hash table with a single bucket.
- * A search of the hash table will look through
- * libdl_symtab starting with index [1], then
- * use libdl_chains to find the next index to
- * look at.  libdl_chains should be set up to
- * walk through every element in libdl_symtab,
- * and then end with 0 (sentinel value).
- *
- * I.e., libdl_chains should look like
- * { 0, 2, 3, ... N, 0 } where N is the number
- * of actual symbols, or nelems(libdl_symtab)-1
- * (since the first element of libdl_symtab is not
- * a real symbol).
- *
- * (see _elf_lookup())
- *
- * Note that adding any new symbols here requires
- * stubbing them out in libdl.
- */
-static unsigned libdl_buckets[1] = { 1 };
-static unsigned libdl_chains[7] = { 0, 2, 3, 4, 5, 6, 0 };
-
-soinfo libdl_info = {
-    name: "libdl.so",
-    flags: FLAG_LINKED,
-
-    strtab: ANDROID_LIBDL_STRTAB,
-    symtab: libdl_symtab,
-
-    nbucket: 1,
-    nchain: 7,
-    bucket: libdl_buckets,
-    chain: libdl_chains,
-};
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
new file mode 100644
index 0000000..8486bfa
--- /dev/null
+++ b/linker/dlfcn.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2007 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>
+#include <pthread.h>
+#include <stdio.h>
+
+#include <private/ScopedPthreadMutexLocker.h>
+
+#include "linker.h"
+#include "linker_format.h"
+
+/* This file hijacks the symbols stubbed out in libdl.so. */
+
+static char dl_err_buf[1024];
+static const char* dl_err_str;
+
+#define likely(expr)   __builtin_expect (expr, 1)
+#define unlikely(expr) __builtin_expect (expr, 0)
+
+pthread_mutex_t dl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+
+static void set_dlerror(const char* msg, const char* detail) {
+  if (detail != NULL) {
+    format_buffer(dl_err_buf, sizeof(dl_err_buf), "%s: %s", msg, detail);
+  } else {
+    format_buffer(dl_err_buf, sizeof(dl_err_buf), "%s", msg);
+  }
+  dl_err_str = (const char*) &dl_err_buf[0];
+}
+
+void *dlopen(const char* filename, int flag) {
+  ScopedPthreadMutexLocker locker(&dl_lock);
+  soinfo* result = find_library(filename);
+  if (result == NULL) {
+    set_dlerror("dlopen failed", linker_get_error());
+    return NULL;
+  }
+  soinfo_call_constructors(result);
+  result->refcount++;
+  return result;
+}
+
+const char* dlerror() {
+  const char* old_value = dl_err_str;
+  dl_err_str = NULL;
+  return (const char*) old_value;
+}
+
+void* dlsym(void* handle, const char* symbol) {
+  ScopedPthreadMutexLocker locker(&dl_lock);
+
+  if (unlikely(handle == 0)) {
+    set_dlerror("dlsym library handle is null", NULL);
+    return NULL;
+  }
+  if (unlikely(symbol == 0)) {
+    set_dlerror("dlsym symbol name is null", NULL);
+    return NULL;
+  }
+
+  soinfo* found = NULL;
+  Elf32_Sym* sym = NULL;
+  if (handle == RTLD_DEFAULT) {
+    sym = lookup(symbol, &found, NULL);
+  } else if (handle == RTLD_NEXT) {
+    void* ret_addr = __builtin_return_address(0);
+    soinfo* si = find_containing_library(ret_addr);
+
+    sym = NULL;
+    if (si && si->next) {
+      sym = lookup(symbol, &found, si->next);
+    }
+  } else {
+    found = (soinfo*) handle;
+    sym = soinfo_lookup(found, symbol);
+  }
+
+  if (likely(sym != 0)) {
+    unsigned bind = ELF32_ST_BIND(sym->st_info);
+
+    if (likely((bind == STB_GLOBAL) && (sym->st_shndx != 0))) {
+      unsigned ret = sym->st_value + found->load_bias;
+      return (void*) ret;
+    }
+
+    set_dlerror("symbol found but not global", symbol);
+    return NULL;
+  } else {
+    set_dlerror("undefined symbol", symbol);
+    return NULL;
+  }
+}
+
+int dladdr(const void* addr, Dl_info* info) {
+  ScopedPthreadMutexLocker locker(&dl_lock);
+
+  // Determine if this address can be found in any library currently mapped.
+  soinfo* si = find_containing_library(addr);
+  if (si == NULL) {
+    return 0;
+  }
+
+  memset(info, 0, sizeof(Dl_info));
+
+  info->dli_fname = si->name;
+  // Address at which the shared object is loaded.
+  info->dli_fbase = (void*) si->base;
+
+  // Determine if any symbol in the library contains the specified address.
+  Elf32_Sym *sym = soinfo_find_symbol(si, addr);
+  if (sym != NULL) {
+    info->dli_sname = si->strtab + sym->st_name;
+    info->dli_saddr = (void*)(si->load_bias + sym->st_value);
+  }
+
+  return 1;
+}
+
+int dlclose(void* handle) {
+  ScopedPthreadMutexLocker locker(&dl_lock);
+  return soinfo_unload((soinfo*) handle);
+}
+
+#if defined(ANDROID_ARM_LINKER)
+//                     0000000 00011111 111112 22222222 2333333 333344444444445555555
+//                     0123456 78901234 567890 12345678 9012345 678901234567890123456
+#define ANDROID_LIBDL_STRTAB \
+                      "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_unwind_find_exidx\0"
+
+#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
+//                     0000000 00011111 111112 22222222 2333333 3333444444444455
+//                     0123456 78901234 567890 12345678 9012345 6789012345678901
+#define ANDROID_LIBDL_STRTAB \
+                      "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_iterate_phdr\0"
+#else
+#error Unsupported architecture. Only ARM, MIPS, and x86 are presently supported.
+#endif
+
+// name_offset: starting index of the name in libdl_info.strtab
+#define ELF32_SYM_INITIALIZER(name_offset, value, shndx) \
+    { name_offset, \
+      reinterpret_cast<Elf32_Addr>(reinterpret_cast<void*>(value)), \
+      /* st_size */ 0, \
+      (shndx == 0) ? 0 : (STB_GLOBAL << 4), \
+      /* st_other */ 0, \
+      shndx }
+
+static Elf32_Sym libdl_symtab[] = {
+  // Total length of libdl_info.strtab, including trailing 0.
+  // This is actually the STH_UNDEF entry. Technically, it's
+  // supposed to have st_name == 0, but instead, it points to an index
+  // in the strtab with a \0 to make iterating through the symtab easier.
+  ELF32_SYM_INITIALIZER(sizeof(ANDROID_LIBDL_STRTAB) - 1, NULL, 0),
+  ELF32_SYM_INITIALIZER( 0, &dlopen, 1),
+  ELF32_SYM_INITIALIZER( 7, &dlclose, 1),
+  ELF32_SYM_INITIALIZER(15, &dlsym, 1),
+  ELF32_SYM_INITIALIZER(21, &dlerror, 1),
+  ELF32_SYM_INITIALIZER(29, &dladdr, 1),
+#if defined(ANDROID_ARM_LINKER)
+  ELF32_SYM_INITIALIZER(36, &dl_unwind_find_exidx, 1),
+#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
+  ELF32_SYM_INITIALIZER(36, &dl_iterate_phdr, 1),
+#endif
+};
+
+/* Fake out a hash table with a single bucket.
+ * A search of the hash table will look through
+ * libdl_symtab starting with index [1], then
+ * use libdl_chains to find the next index to
+ * look at.  libdl_chains should be set up to
+ * walk through every element in libdl_symtab,
+ * and then end with 0 (sentinel value).
+ *
+ * I.e., libdl_chains should look like
+ * { 0, 2, 3, ... N, 0 } where N is the number
+ * of actual symbols, or nelems(libdl_symtab)-1
+ * (since the first element of libdl_symtab is not
+ * a real symbol).
+ *
+ * (see _elf_lookup())
+ *
+ * Note that adding any new symbols here requires
+ * stubbing them out in libdl.
+ */
+static unsigned libdl_buckets[1] = { 1 };
+static unsigned libdl_chains[7] = { 0, 2, 3, 4, 5, 6, 0 };
+
+soinfo libdl_info = {
+    name: "libdl.so",
+
+    phdr: 0, phnum: 0,
+    entry: 0, base: 0, size: 0,
+    unused: 0, dynamic: 0, unused2: 0, unused3: 0,
+    next: 0,
+
+    flags: FLAG_LINKED,
+
+    strtab: ANDROID_LIBDL_STRTAB,
+    symtab: libdl_symtab,
+
+    nbucket: 1,
+    nchain: 7,
+    bucket: libdl_buckets,
+    chain: libdl_chains,
+
+    plt_got: 0, plt_rel: 0, plt_rel_count: 0, rel: 0, rel_count: 0,
+    preinit_array: 0, preinit_array_count: 0, init_array: 0, init_array_count: 0,
+    fini_array: 0, fini_array_count: 0, init_func: 0, fini_func: 0,
+
+#if defined(ANDROID_ARM_LINKER)
+    ARM_exidx: 0, ARM_exidx_count: 0,
+#elif defined(ANDROID_MIPS_LINKER)
+    mips_symtabno: 0, mips_local_gotno: 0, mips_gotsym: 0,
+#endif
+
+    refcount: 0,
+    { l_addr: 0, l_name: 0, l_ld: 0, l_next: 0, l_prev: 0, },
+    constructors_called: 0, load_bias: 0, has_text_relocations: 0,
+};
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 998e608..6e1c604 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -43,6 +43,7 @@
 // Private C library headers.
 #include <private/bionic_tls.h>
 #include <private/logd.h>
+#include <private/ScopedPthreadMutexLocker.h>
 
 #include "linker.h"
 #include "linker_debug.h"
@@ -161,12 +162,9 @@
 
 static char tmp_err_buf[768];
 static char __linker_dl_err_buf[768];
-#define BASENAME(s) (strrchr(s, '/') != NULL ? strrchr(s, '/') + 1 : s)
 #define DL_ERR(fmt, x...) \
     do { \
-        format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf), \
-                      "%s(%s:%d): " fmt, \
-                      __FUNCTION__, BASENAME(__FILE__), __LINE__, ##x); \
+        format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf), fmt, ##x); \
         ERROR(fmt "\n", ##x); \
     } while(0)
 
@@ -185,7 +183,7 @@
                                   RT_CONSISTENT, 0};
 static link_map* r_debug_tail = 0;
 
-static pthread_mutex_t _r_debug_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t gDebugMutex = PTHREAD_MUTEX_INITIALIZER;
 
 static void insert_soinfo_into_debug_map(soinfo * info) {
     // Copy the necessary fields into the debug structure.
@@ -232,7 +230,7 @@
         return;
     }
 
-    pthread_mutex_lock(&_r_debug_lock);
+    ScopedPthreadMutexLocker locker(&gDebugMutex);
 
     _r_debug.r_state = RT_ADD;
     rtld_db_dlactivity();
@@ -241,8 +239,6 @@
 
     _r_debug.r_state = RT_CONSISTENT;
     rtld_db_dlactivity();
-
-    pthread_mutex_unlock(&_r_debug_lock);
 }
 
 static void notify_gdb_of_unload(soinfo* info) {
@@ -251,7 +247,7 @@
         return;
     }
 
-    pthread_mutex_lock(&_r_debug_lock);
+    ScopedPthreadMutexLocker locker(&gDebugMutex);
 
     _r_debug.r_state = RT_DELETE;
     rtld_db_dlactivity();
@@ -260,8 +256,6 @@
 
     _r_debug.r_state = RT_CONSISTENT;
     rtld_db_dlactivity();
-
-    pthread_mutex_unlock(&_r_debug_lock);
 }
 
 extern "C" void notify_gdb_of_libraries()
@@ -345,7 +339,7 @@
  *
  * Intended to be called by libc's __gnu_Unwind_Find_exidx().
  *
- * This function is exposed via dlfcn.c and libdl.so.
+ * This function is exposed via dlfcn.cpp and libdl.so.
  */
 _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount)
 {
diff --git a/tests/dlopen_test.cpp b/tests/dlopen_test.cpp
index 2fcfa0c..5ef32ad 100644
--- a/tests/dlopen_test.cpp
+++ b/tests/dlopen_test.cpp
@@ -24,14 +24,19 @@
 
 #include <string>
 
+#define ASSERT_SUBSTR(needle, haystack) \
+    ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack)
+
 static bool gCalled = false;
 extern "C" void DlSymTestFunction() {
   gCalled = true;
 }
 
 TEST(dlopen, dlsym_in_self) {
+  dlerror(); // Clear any pending errors.
   void* self = dlopen(NULL, RTLD_NOW);
   ASSERT_TRUE(self != NULL);
+  ASSERT_TRUE(dlerror() == NULL);
 
   void* sym = dlsym(self, "DlSymTestFunction");
   ASSERT_TRUE(sym != NULL);
@@ -43,9 +48,52 @@
   ASSERT_TRUE(gCalled);
 }
 
-TEST(dlopen, dladdr) {
+TEST(dlopen, dlopen_failure) {
+  void* self = dlopen("/does/not/exist", RTLD_NOW);
+  ASSERT_TRUE(self == NULL);
+#if __BIONIC__
+  ASSERT_STREQ("dlopen failed: library \"/does/not/exist\" not found", dlerror());
+#else
+  ASSERT_STREQ("/does/not/exist: cannot open shared object file: No such file or directory", dlerror());
+#endif
+}
+
+TEST(dlopen, dlsym_failures) {
+  dlerror(); // Clear any pending errors.
   void* self = dlopen(NULL, RTLD_NOW);
   ASSERT_TRUE(self != NULL);
+  ASSERT_TRUE(dlerror() == NULL);
+
+  void* sym;
+
+  // NULL handle.
+  sym = dlsym(NULL, "test");
+  ASSERT_TRUE(sym == NULL);
+#if __BIONIC__
+  ASSERT_SUBSTR("dlsym library handle is null", dlerror());
+#else
+  ASSERT_SUBSTR("undefined symbol: test", dlerror()); // glibc isn't specific about the failure.
+#endif
+
+  // NULL symbol name.
+#if __BIONIC__
+  // glibc marks this parameter non-null and SEGVs if you cheat.
+  sym = dlsym(self, NULL);
+  ASSERT_TRUE(sym == NULL);
+  ASSERT_SUBSTR("", dlerror());
+#endif
+
+  // Symbol that doesn't exist.
+  sym = dlsym(self, "ThisSymbolDoesNotExist");
+  ASSERT_TRUE(sym == NULL);
+  ASSERT_SUBSTR("undefined symbol: ThisSymbolDoesNotExist", dlerror());
+}
+
+TEST(dlopen, dladdr) {
+  dlerror(); // Clear any pending errors.
+  void* self = dlopen(NULL, RTLD_NOW);
+  ASSERT_TRUE(self != NULL);
+  ASSERT_TRUE(dlerror() == NULL);
 
   void* sym = dlsym(self, "DlSymTestFunction");
   ASSERT_TRUE(sym != NULL);
@@ -102,9 +150,13 @@
 TEST(dlopen, dladdr_invalid) {
   Dl_info info;
 
+  dlerror(); // Clear any pending errors.
+
   // No symbol corresponding to NULL.
   ASSERT_EQ(dladdr(NULL, &info), 0); // Zero on error, non-zero on success.
+  ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
 
   // No symbol corresponding to a stack address.
   ASSERT_EQ(dladdr(&info, &info), 0); // Zero on error, non-zero on success.
+  ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
 }