summaryrefslogtreecommitdiffstats
path: root/linker/dlfcn.c
diff options
context:
space:
mode:
Diffstat (limited to 'linker/dlfcn.c')
-rw-r--r--linker/dlfcn.c212
1 files changed, 212 insertions, 0 deletions
diff --git a/linker/dlfcn.c b/linker/dlfcn.c
new file mode 100644
index 0000000..cd73d11
--- /dev/null
+++ b/linker/dlfcn.c
@@ -0,0 +1,212 @@
+/*
+ * 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 "linker.h"
+
+/* This file hijacks the symbols stubbed out in libdl.so. */
+
+#define DL_SUCCESS 0
+#define DL_ERR_CANNOT_FIND_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 const char *dl_errors[] = {
+ [DL_SUCCESS] = NULL,
+ [DL_ERR_CANNOT_FIND_LIBRARY] = "Cannot find 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",
+};
+
+static int dl_last_err = DL_SUCCESS;
+
+#define likely(expr) __builtin_expect (expr, 1)
+#define unlikely(expr) __builtin_expect (expr, 0)
+
+static pthread_mutex_t dl_lock = PTHREAD_MUTEX_INITIALIZER;
+
+void *dlopen(const char *filename, int flag)
+{
+ soinfo *ret;
+
+ pthread_mutex_lock(&dl_lock);
+ ret = find_library(filename);
+ if (unlikely(ret == NULL)) {
+ dl_last_err = DL_ERR_CANNOT_FIND_LIBRARY;
+ } else {
+ ret->refcount++;
+ }
+ pthread_mutex_unlock(&dl_lock);
+ return ret;
+}
+
+const char *dlerror(void)
+{
+ const char *err = dl_errors[dl_last_err];
+ dl_last_err = DL_SUCCESS;
+ return err;
+}
+
+void *dlsym(void *handle, const char *symbol)
+{
+ unsigned base;
+ Elf32_Sym *sym;
+ unsigned bind;
+
+ pthread_mutex_lock(&dl_lock);
+
+ if(unlikely(handle == 0)) {
+ dl_last_err = DL_ERR_INVALID_LIBRARY_HANDLE;
+ goto err;
+ }
+ if(unlikely(symbol == 0)) {
+ dl_last_err = DL_ERR_BAD_SYMBOL_NAME;
+ goto err;
+ }
+
+ if(handle == RTLD_DEFAULT) {
+ sym = lookup(symbol, &base);
+ } else if(handle == RTLD_NEXT) {
+ sym = lookup(symbol, &base);
+ } else {
+ sym = lookup_in_library((soinfo*) handle, symbol);
+ base = ((soinfo*) handle)->base;
+ }
+
+ 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 + base;
+ pthread_mutex_unlock(&dl_lock);
+ return (void*)ret;
+ }
+
+ dl_last_err = DL_ERR_SYMBOL_NOT_GLOBAL;
+ }
+ else dl_last_err = DL_ERR_SYMBOL_NOT_FOUND;
+
+err:
+ pthread_mutex_unlock(&dl_lock);
+ return 0;
+}
+
+int dlclose(void *handle)
+{
+ pthread_mutex_lock(&dl_lock);
+ (void)unload_library((soinfo*)handle);
+ pthread_mutex_unlock(&dl_lock);
+ return 0;
+}
+
+#if defined(ANDROID_ARM_LINKER)
+// 0000000 00011111 111112 22222222 233333333334444444444
+// 0123456 78901234 567890 12345678 901234567890123456789
+#define ANDROID_LIBDL_STRTAB \
+ "dlopen\0dlclose\0dlsym\0dlerror\0dl_unwind_find_exidx\0"
+
+#elif defined(ANDROID_X86_LINKER)
+// 0000000 00011111 111112 22222222 2333333333344444
+// 0123456 78901234 567890 12345678 9012345678901234
+#define ANDROID_LIBDL_STRTAB \
+ "dlopen\0dlclose\0dlsym\0dlerror\0dl_iterate_phdr\0"
+
+#else /* !defined(ANDROID_ARM_LINKER) && !defined(ANDROID_X86_LINKER) */
+#error Unsupported architecture. Only ARM 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,
+ },
+#ifdef ANDROID_ARM_LINKER
+ { st_name: 29,
+ st_value: (Elf32_Addr) &dl_unwind_find_exidx,
+ st_info: STB_GLOBAL << 4,
+ st_shndx: 1,
+ },
+#elif defined(ANDROID_X86_LINKER)
+ { st_name: 29,
+ 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[6] = { 0, 2, 3, 4, 5, 0 };
+
+soinfo libdl_info = {
+ name: "libdl.so",
+ flags: FLAG_LINKED,
+
+ strtab: ANDROID_LIBDL_STRTAB,
+ symtab: libdl_symtab,
+
+ nbucket: 1,
+ nchain: 6,
+ bucket: libdl_buckets,
+ chain: libdl_chains,
+};
+