Fix dlerror(3).

Add unit tests for dlerror(3) in various situations. I think We're at least
as good as glibc now.

Also factor out the ScopedPthreadMutexLock and use it here too.

Bug: http://code.google.com/p/android/issues/detail?id=38398
Change-Id: I040938b4366ab836e3df46d1d8055b92f4ea6ed8
diff --git a/linker/Android.mk b/linker/Android.mk
index 99618f5..503da86 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)
 {