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/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 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)
{
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).
}