dlfcn.c revision 1dc9e472e19acfe6dc7f41e429236e7eef7ceda1
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16#include <dlfcn.h> 17#include <pthread.h> 18#include "linker.h" 19 20/* This file hijacks the symbols stubbed out in libdl.so. */ 21 22#define DL_SUCCESS 0 23#define DL_ERR_CANNOT_FIND_LIBRARY 1 24#define DL_ERR_INVALID_LIBRARY_HANDLE 2 25#define DL_ERR_BAD_SYMBOL_NAME 3 26#define DL_ERR_SYMBOL_NOT_FOUND 4 27#define DL_ERR_SYMBOL_NOT_GLOBAL 5 28 29static const char *dl_errors[] = { 30 [DL_SUCCESS] = NULL, 31 [DL_ERR_CANNOT_FIND_LIBRARY] = "Cannot find library", 32 [DL_ERR_INVALID_LIBRARY_HANDLE] = "Invalid library handle", 33 [DL_ERR_BAD_SYMBOL_NAME] = "Invalid symbol name", 34 [DL_ERR_SYMBOL_NOT_FOUND] = "Symbol not found", 35 [DL_ERR_SYMBOL_NOT_GLOBAL] = "Symbol is not global", 36}; 37 38static int dl_last_err = DL_SUCCESS; 39 40#define likely(expr) __builtin_expect (expr, 1) 41#define unlikely(expr) __builtin_expect (expr, 0) 42 43static pthread_mutex_t dl_lock = PTHREAD_MUTEX_INITIALIZER; 44 45void *dlopen(const char *filename, int flag) 46{ 47 soinfo *ret; 48 49 pthread_mutex_lock(&dl_lock); 50 ret = find_library(filename); 51 if (unlikely(ret == NULL)) { 52 dl_last_err = DL_ERR_CANNOT_FIND_LIBRARY; 53 } else { 54 ret->refcount++; 55 } 56 pthread_mutex_unlock(&dl_lock); 57 return ret; 58} 59 60const char *dlerror(void) 61{ 62 const char *err = dl_errors[dl_last_err]; 63 dl_last_err = DL_SUCCESS; 64 return err; 65} 66 67void *dlsym(void *handle, const char *symbol) 68{ 69 unsigned base; 70 Elf32_Sym *sym; 71 unsigned bind; 72 73 pthread_mutex_lock(&dl_lock); 74 75 if(unlikely(handle == 0)) { 76 dl_last_err = DL_ERR_INVALID_LIBRARY_HANDLE; 77 goto err; 78 } 79 if(unlikely(symbol == 0)) { 80 dl_last_err = DL_ERR_BAD_SYMBOL_NAME; 81 goto err; 82 } 83 84 if(handle == RTLD_DEFAULT) { 85 sym = lookup(symbol, &base); 86 } else if(handle == RTLD_NEXT) { 87 sym = lookup(symbol, &base); 88 } else { 89 sym = lookup_in_library((soinfo*) handle, symbol); 90 base = ((soinfo*) handle)->base; 91 } 92 93 if(likely(sym != 0)) { 94 bind = ELF32_ST_BIND(sym->st_info); 95 96 if(likely((bind == STB_GLOBAL) && (sym->st_shndx != 0))) { 97 unsigned ret = sym->st_value + base; 98 pthread_mutex_unlock(&dl_lock); 99 return (void*)ret; 100 } 101 102 dl_last_err = DL_ERR_SYMBOL_NOT_GLOBAL; 103 } 104 else dl_last_err = DL_ERR_SYMBOL_NOT_FOUND; 105 106err: 107 pthread_mutex_unlock(&dl_lock); 108 return 0; 109} 110 111int dlclose(void *handle) 112{ 113 pthread_mutex_lock(&dl_lock); 114 (void)unload_library((soinfo*)handle); 115 pthread_mutex_unlock(&dl_lock); 116 return 0; 117} 118 119#if defined(ANDROID_ARM_LINKER) 120// 0000000 00011111 111112 22222222 233333333334444444444 121// 0123456 78901234 567890 12345678 901234567890123456789 122#define ANDROID_LIBDL_STRTAB \ 123 "dlopen\0dlclose\0dlsym\0dlerror\0dl_unwind_find_exidx\0" 124 125#elif defined(ANDROID_X86_LINKER) 126// 0000000 00011111 111112 22222222 2333333333344444 127// 0123456 78901234 567890 12345678 9012345678901234 128#define ANDROID_LIBDL_STRTAB \ 129 "dlopen\0dlclose\0dlsym\0dlerror\0dl_iterate_phdr\0" 130 131#else /* !defined(ANDROID_ARM_LINKER) && !defined(ANDROID_X86_LINKER) */ 132#error Unsupported architecture. Only ARM and x86 are presently supported. 133#endif 134 135 136static Elf32_Sym libdl_symtab[] = { 137 // total length of libdl_info.strtab, including trailing 0 138 // This is actually the the STH_UNDEF entry. Technically, it's 139 // supposed to have st_name == 0, but instead, it points to an index 140 // in the strtab with a \0 to make iterating through the symtab easier. 141 { st_name: sizeof(ANDROID_LIBDL_STRTAB) - 1, 142 }, 143 { st_name: 0, // starting index of the name in libdl_info.strtab 144 st_value: (Elf32_Addr) &dlopen, 145 st_info: STB_GLOBAL << 4, 146 st_shndx: 1, 147 }, 148 { st_name: 7, 149 st_value: (Elf32_Addr) &dlclose, 150 st_info: STB_GLOBAL << 4, 151 st_shndx: 1, 152 }, 153 { st_name: 15, 154 st_value: (Elf32_Addr) &dlsym, 155 st_info: STB_GLOBAL << 4, 156 st_shndx: 1, 157 }, 158 { st_name: 21, 159 st_value: (Elf32_Addr) &dlerror, 160 st_info: STB_GLOBAL << 4, 161 st_shndx: 1, 162 }, 163#ifdef ANDROID_ARM_LINKER 164 { st_name: 29, 165 st_value: (Elf32_Addr) &dl_unwind_find_exidx, 166 st_info: STB_GLOBAL << 4, 167 st_shndx: 1, 168 }, 169#elif defined(ANDROID_X86_LINKER) 170 { st_name: 29, 171 st_value: (Elf32_Addr) &dl_iterate_phdr, 172 st_info: STB_GLOBAL << 4, 173 st_shndx: 1, 174 }, 175#endif 176}; 177 178/* Fake out a hash table with a single bucket. 179 * A search of the hash table will look through 180 * libdl_symtab starting with index [1], then 181 * use libdl_chains to find the next index to 182 * look at. libdl_chains should be set up to 183 * walk through every element in libdl_symtab, 184 * and then end with 0 (sentinel value). 185 * 186 * I.e., libdl_chains should look like 187 * { 0, 2, 3, ... N, 0 } where N is the number 188 * of actual symbols, or nelems(libdl_symtab)-1 189 * (since the first element of libdl_symtab is not 190 * a real symbol). 191 * 192 * (see _elf_lookup()) 193 * 194 * Note that adding any new symbols here requires 195 * stubbing them out in libdl. 196 */ 197static unsigned libdl_buckets[1] = { 1 }; 198static unsigned libdl_chains[6] = { 0, 2, 3, 4, 5, 0 }; 199 200soinfo libdl_info = { 201 name: "libdl.so", 202 flags: FLAG_LINKED, 203 204 strtab: ANDROID_LIBDL_STRTAB, 205 symtab: libdl_symtab, 206 207 nbucket: 1, 208 nchain: 6, 209 bucket: libdl_buckets, 210 chain: libdl_chains, 211}; 212 213