lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* vi: set sw=4 ts=4: */ |
| 2 | /* |
| 3 | * Program to load an ELF binary on a linux system, and run it |
| 4 | * after resolving ELF shared library symbols |
| 5 | * |
| 6 | * Copyright (C) 2000-2006 by Erik Andersen <andersen@uclibc.org> |
| 7 | * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald, |
| 8 | * David Engel, Hongjiu Lu and Mitch D'Souza |
| 9 | * |
| 10 | * Redistribution and use in source and binary forms, with or without |
| 11 | * modification, are permitted provided that the following conditions |
| 12 | * are met: |
| 13 | * 1. Redistributions of source code must retain the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer. |
| 15 | * 2. The name of the above contributors may not be |
| 16 | * used to endorse or promote products derived from this software |
| 17 | * without specific prior written permission. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND |
| 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE |
| 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 29 | * SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | |
| 33 | #include <ldso.h> |
| 34 | #include <stdio.h> |
| 35 | #include <string.h> /* Needed for 'strstr' prototype' */ |
| 36 | #include <stdbool.h> |
| 37 | #include <bits/uClibc_mutex.h> |
| 38 | |
| 39 | #ifdef __UCLIBC_HAS_TLS__ |
| 40 | #include <tls.h> |
| 41 | #endif |
| 42 | |
| 43 | #if defined(USE_TLS) && USE_TLS |
| 44 | #include <ldsodefs.h> |
| 45 | extern void _dl_add_to_slotinfo(struct link_map *l); |
| 46 | #endif |
| 47 | |
| 48 | /* TODO: get rid of global lock and use more finegrained locking, or |
| 49 | * perhaps RCU for the global structures */ |
| 50 | __UCLIBC_MUTEX_STATIC(_dl_mutex, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP); |
| 51 | |
| 52 | #ifdef SHARED |
| 53 | # if defined(USE_TLS) && USE_TLS |
| 54 | # include <dl-tls.h> |
| 55 | extern struct link_map *_dl_update_slotinfo(unsigned long int req_modid); |
| 56 | # endif |
| 57 | |
| 58 | /* When libdl is loaded as a shared library, we need to load in |
| 59 | * and use a pile of symbols from ldso... */ |
| 60 | #include <dl-elf.h> |
| 61 | #if 0 |
| 62 | extern struct elf_resolve * _dl_load_shared_library(unsigned, struct dyn_elf **, |
| 63 | struct elf_resolve *, char *, int); |
| 64 | extern int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int lazy); |
| 65 | extern void _dl_protect_relro(struct elf_resolve * tpnt); |
| 66 | #endif |
| 67 | extern int _dl_errno; |
| 68 | extern struct dyn_elf *_dl_symbol_tables; |
| 69 | extern struct dyn_elf *_dl_handles; |
| 70 | extern struct elf_resolve *_dl_loaded_modules; |
| 71 | extern void _dl_free (void *__ptr); |
| 72 | extern struct r_debug *_dl_debug_addr; |
| 73 | extern unsigned long _dl_error_number; |
| 74 | extern void *(*_dl_malloc_function)(size_t); |
| 75 | extern void (*_dl_free_function) (void *p); |
| 76 | extern void _dl_run_init_array(struct elf_resolve *); |
| 77 | extern void _dl_run_fini_array(struct elf_resolve *); |
| 78 | #ifdef __LDSO_CACHE_SUPPORT__ |
| 79 | int _dl_map_cache(void); |
| 80 | int _dl_unmap_cache(void); |
| 81 | #endif |
| 82 | #ifdef __mips__ |
| 83 | extern void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt, int lazy); |
| 84 | #endif |
| 85 | #ifdef __SUPPORT_LD_DEBUG__ |
| 86 | extern char *_dl_debug; |
| 87 | #endif |
| 88 | |
| 89 | #else /* !SHARED */ |
| 90 | |
| 91 | #define _dl_malloc malloc |
| 92 | #define _dl_free free |
| 93 | |
| 94 | /* When libdl is linked as a static library, we need to replace all |
| 95 | * the symbols that otherwise would have been loaded in from ldso... */ |
| 96 | |
| 97 | #ifdef __SUPPORT_LD_DEBUG__ |
| 98 | char *_dl_debug = NULL; |
| 99 | char *_dl_debug_symbols = NULL; |
| 100 | char *_dl_debug_move = NULL; |
| 101 | char *_dl_debug_reloc = NULL; |
| 102 | char *_dl_debug_detail = NULL; |
| 103 | char *_dl_debug_nofixups = NULL; |
| 104 | char *_dl_debug_bindings = NULL; |
| 105 | int _dl_debug_file = 2; |
| 106 | #endif |
| 107 | const char *_dl_progname = ""; /* Program name */ |
| 108 | void *(*_dl_malloc_function)(size_t); |
| 109 | void (*_dl_free_function) (void *p); |
| 110 | #ifdef __LDSO_LD_LIBRARY_PATH__ |
| 111 | char *_dl_library_path = NULL; /* Where we look for libraries */ |
| 112 | #endif |
| 113 | int _dl_errno = 0; /* We can't use the real errno in ldso */ |
| 114 | size_t _dl_pagesize = PAGE_SIZE; /* Store the page size for use later */ |
| 115 | /* This global variable is also to communicate with debuggers such as gdb. */ |
| 116 | struct r_debug *_dl_debug_addr = NULL; |
| 117 | |
| 118 | #include "../ldso/dl-array.c" |
| 119 | #include "../ldso/dl-debug.c" |
| 120 | |
| 121 | |
| 122 | # if defined(USE_TLS) && USE_TLS |
| 123 | /* |
| 124 | * Giving this initialized value preallocates some surplus bytes in the |
| 125 | * static TLS area, see __libc_setup_tls (libc-tls.c). |
| 126 | */ |
| 127 | size_t _dl_tls_static_size = 2048; |
| 128 | # endif |
| 129 | #include LDSO_ELFINTERP |
| 130 | #include "../ldso/dl-hash.c" |
| 131 | #define _dl_trace_loaded_objects 0 |
| 132 | #include "../ldso/dl-elf.c" |
| 133 | #endif /* SHARED */ |
| 134 | |
| 135 | #ifdef __SUPPORT_LD_DEBUG__ |
| 136 | # define _dl_if_debug_print(fmt, args...) \ |
| 137 | do { \ |
| 138 | if (_dl_debug) \ |
| 139 | fprintf(stderr, "%s():%i: " fmt, __func__, __LINE__, ## args); \ |
| 140 | } while (0) |
| 141 | #else |
| 142 | # define _dl_if_debug_print(fmt, args...) |
| 143 | #endif |
| 144 | |
| 145 | static int do_dlclose(void *, int need_fini); |
| 146 | |
| 147 | |
| 148 | static const char *const dl_error_names[] = { |
| 149 | "", |
| 150 | "File not found", |
| 151 | "Unable to open /dev/zero", |
| 152 | "Not an ELF file", |
| 153 | #if defined (__i386__) |
| 154 | "Not i386 binary", |
| 155 | #elif defined (__sparc__) |
| 156 | "Not sparc binary", |
| 157 | #elif defined (__mc68000__) |
| 158 | "Not m68k binary", |
| 159 | #else |
| 160 | "Unrecognized binary type", |
| 161 | #endif |
| 162 | "Not an ELF shared library", |
| 163 | "Unable to mmap file", |
| 164 | "No dynamic section", |
| 165 | "Library contains unsupported TLS", |
| 166 | #ifdef ELF_USES_RELOCA |
| 167 | "Unable to process REL relocs", |
| 168 | #else |
| 169 | "Unable to process RELA relocs", |
| 170 | #endif |
| 171 | "Bad handle", |
| 172 | "Unable to resolve symbol" |
| 173 | }; |
| 174 | |
| 175 | |
| 176 | #if defined(USE_TLS) && USE_TLS |
| 177 | #ifdef SHARED |
| 178 | /* |
| 179 | * Systems which do not have tls_index also probably have to define |
| 180 | * DONT_USE_TLS_INDEX. |
| 181 | */ |
| 182 | |
| 183 | # ifndef __TLS_GET_ADDR |
| 184 | # define __TLS_GET_ADDR __tls_get_addr |
| 185 | # endif |
| 186 | |
| 187 | /* |
| 188 | * Return the symbol address given the map of the module it is in and |
| 189 | * the symbol record. This is used in dl-sym.c. |
| 190 | */ |
| 191 | static void * |
| 192 | internal_function |
| 193 | _dl_tls_symaddr(struct link_map *map, const Elf32_Addr st_value) |
| 194 | { |
| 195 | # ifndef DONT_USE_TLS_INDEX |
| 196 | tls_index tmp = |
| 197 | { |
| 198 | .ti_module = map->l_tls_modid, |
| 199 | .ti_offset = st_value |
| 200 | }; |
| 201 | |
| 202 | return __TLS_GET_ADDR (&tmp); |
| 203 | # else |
| 204 | return __TLS_GET_ADDR (map->l_tls_modid, st_value); |
| 205 | # endif |
| 206 | } |
| 207 | #endif |
| 208 | |
| 209 | /* Returns true when a non-empty entry was found. */ |
| 210 | static bool |
| 211 | remove_slotinfo(size_t idx, struct dtv_slotinfo_list *listp, size_t disp, |
| 212 | bool should_be_there) |
| 213 | { |
| 214 | if (idx - disp >= listp->len) { |
| 215 | if (listp->next == NULL) { |
| 216 | /* |
| 217 | * The index is not actually valid in the slotinfo list, |
| 218 | * because this object was closed before it was fully set |
| 219 | * up due to some error. |
| 220 | */ |
| 221 | _dl_assert(!should_be_there); |
| 222 | } else { |
| 223 | if (remove_slotinfo(idx, listp->next, disp + listp->len, |
| 224 | should_be_there)) |
| 225 | return true; |
| 226 | |
| 227 | /* |
| 228 | * No non-empty entry. Search from the end of this element's |
| 229 | * slotinfo array. |
| 230 | */ |
| 231 | idx = disp + listp->len; |
| 232 | } |
| 233 | } else { |
| 234 | struct link_map *old_map = listp->slotinfo[idx - disp].map; |
| 235 | |
| 236 | /* |
| 237 | * The entry might still be in its unused state if we are |
| 238 | * closing an object that wasn't fully set up. |
| 239 | */ |
| 240 | if (__builtin_expect(old_map != NULL, 1)) { |
| 241 | _dl_assert(old_map->l_tls_modid == idx); |
| 242 | |
| 243 | /* Mark the entry as unused. */ |
| 244 | listp->slotinfo[idx - disp].gen = _dl_tls_generation + 1; |
| 245 | listp->slotinfo[idx - disp].map = NULL; |
| 246 | } |
| 247 | |
| 248 | /* |
| 249 | * If this is not the last currently used entry no need to |
| 250 | * look further. |
| 251 | */ |
| 252 | if (idx != _dl_tls_max_dtv_idx) |
| 253 | return true; |
| 254 | } |
| 255 | |
| 256 | while (idx - disp > (disp == 0 ? 1 + _dl_tls_static_nelem : 0)) { |
| 257 | --idx; |
| 258 | |
| 259 | if (listp->slotinfo[idx - disp].map != NULL) { |
| 260 | /* Found a new last used index. */ |
| 261 | _dl_tls_max_dtv_idx = idx; |
| 262 | return true; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | /* No non-entry in this list element. */ |
| 267 | return false; |
| 268 | } |
| 269 | #endif |
| 270 | |
| 271 | #ifndef __LDSO_NO_CLEANUP__ |
| 272 | void dl_cleanup(void) __attribute__ ((destructor)); |
| 273 | void dl_cleanup(void) |
| 274 | { |
| 275 | struct dyn_elf *h, *n; |
| 276 | |
| 277 | for (h = _dl_handles; h; h = n) { |
| 278 | n = h->next_handle; |
| 279 | do_dlclose(h, 1); |
| 280 | } |
| 281 | } |
| 282 | #endif |
| 283 | |
| 284 | static ptrdiff_t _dl_build_local_scope (struct elf_resolve **list, |
| 285 | struct elf_resolve *map) |
| 286 | { |
| 287 | struct elf_resolve **p = list; |
| 288 | struct init_fini_list *q; |
| 289 | |
| 290 | *p++ = map; |
| 291 | map->init_flag |= DL_RESERVED; |
| 292 | if (map->init_fini) |
| 293 | for (q = map->init_fini; q; q = q->next) |
| 294 | if (! (q->tpnt->init_flag & DL_RESERVED)) |
| 295 | p += _dl_build_local_scope (p, q->tpnt); |
| 296 | return p - list; |
| 297 | } |
| 298 | |
| 299 | static void *do_dlopen(const char *libname, int flag) |
| 300 | { |
| 301 | struct elf_resolve *tpnt, *tfrom; |
| 302 | struct dyn_elf *dyn_chain, *rpnt = NULL, *dyn_ptr, *relro_ptr, *handle; |
| 303 | ElfW(Addr) from; |
| 304 | struct elf_resolve *tpnt1; |
| 305 | void (*dl_brk) (void); |
| 306 | int now_flag; |
| 307 | struct init_fini_list *tmp, *runp, *runp2, *dep_list; |
| 308 | unsigned int nlist, i; |
| 309 | struct elf_resolve **init_fini_list; |
| 310 | static bool _dl_init; |
| 311 | struct elf_resolve **local_scope; |
| 312 | struct r_scope_elem *ls; |
| 313 | #if defined(USE_TLS) && USE_TLS |
| 314 | bool any_tls = false; |
| 315 | #endif |
| 316 | |
| 317 | /* A bit of sanity checking... */ |
| 318 | if (!(flag & (RTLD_LAZY|RTLD_NOW|RTLD_NOLOAD))) { |
| 319 | _dl_error_number = LD_BAD_HANDLE; |
| 320 | return NULL; |
| 321 | } |
| 322 | |
| 323 | from = (ElfW(Addr)) __builtin_return_address(0); |
| 324 | |
| 325 | if (!_dl_init) { |
| 326 | _dl_init = true; |
| 327 | _dl_malloc_function = malloc; |
| 328 | _dl_free_function = free; |
| 329 | } |
| 330 | /* Cover the trivial case first */ |
| 331 | if (!libname) |
| 332 | return _dl_symbol_tables; |
| 333 | |
| 334 | #ifndef SHARED |
| 335 | # ifdef __SUPPORT_LD_DEBUG__ |
| 336 | _dl_debug = getenv("LD_DEBUG"); |
| 337 | if (_dl_debug) { |
| 338 | if (_dl_strstr(_dl_debug, "all")) { |
| 339 | _dl_debug_detail = _dl_debug_move = _dl_debug_symbols |
| 340 | = _dl_debug_reloc = _dl_debug_bindings = _dl_debug_nofixups = (void*)1; |
| 341 | } else { |
| 342 | _dl_debug_detail = strstr(_dl_debug, "detail"); |
| 343 | _dl_debug_move = strstr(_dl_debug, "move"); |
| 344 | _dl_debug_symbols = strstr(_dl_debug, "sym"); |
| 345 | _dl_debug_reloc = strstr(_dl_debug, "reloc"); |
| 346 | _dl_debug_nofixups = strstr(_dl_debug, "nofix"); |
| 347 | _dl_debug_bindings = strstr(_dl_debug, "bind"); |
| 348 | } |
| 349 | } |
| 350 | # endif |
| 351 | #endif |
| 352 | |
| 353 | _dl_map_cache(); |
| 354 | |
| 355 | /* |
| 356 | * Try and locate the module we were called from - we |
| 357 | * need this so that we get the correct RPATH/RUNPATH. Note that |
| 358 | * this is the current behavior under Solaris, but the |
| 359 | * ABI+ specifies that we should only use the RPATH from |
| 360 | * the application. Thus this may go away at some time |
| 361 | * in the future. |
| 362 | */ |
| 363 | { |
| 364 | struct dyn_elf *dpnt; |
| 365 | tfrom = NULL; |
| 366 | for (dpnt = _dl_symbol_tables; dpnt; dpnt = dpnt->next) { |
| 367 | tpnt = dpnt->dyn; |
| 368 | if (DL_ADDR_IN_LOADADDR(from, tpnt, tfrom)) |
| 369 | tfrom = tpnt; |
| 370 | } |
| 371 | } |
| 372 | for (rpnt = _dl_symbol_tables; rpnt && rpnt->next; rpnt = rpnt->next) |
| 373 | continue; |
| 374 | |
| 375 | relro_ptr = rpnt; |
| 376 | now_flag = (flag & RTLD_NOW) ? RTLD_NOW : 0; |
| 377 | if (getenv("LD_BIND_NOW")) |
| 378 | now_flag = RTLD_NOW; |
| 379 | |
| 380 | #if !defined SHARED && defined __LDSO_LIBRARY_PATH__ |
| 381 | /* When statically linked, the _dl_library_path is not yet initialized */ |
| 382 | _dl_library_path = getenv("LD_LIBRARY_PATH"); |
| 383 | #endif |
| 384 | |
| 385 | /* Try to load the specified library */ |
| 386 | _dl_if_debug_print("Trying to dlopen '%s', RTLD_GLOBAL:%d RTLD_NOW:%d\n", |
| 387 | (char*)libname, (flag & RTLD_GLOBAL ? 1:0), (now_flag & RTLD_NOW ? 1:0)); |
| 388 | |
| 389 | tpnt = _dl_load_shared_library((flag & RTLD_NOLOAD) ? DL_RESOLVE_NOLOAD : 0, |
| 390 | &rpnt, tfrom, (char*)libname, 0); |
| 391 | if (tpnt == NULL) { |
| 392 | _dl_unmap_cache(); |
| 393 | return NULL; |
| 394 | } |
| 395 | dyn_chain = (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); |
| 396 | _dl_memset(dyn_chain, 0, sizeof(struct dyn_elf)); |
| 397 | dyn_chain->dyn = tpnt; |
| 398 | tpnt->rtld_flags |= (flag & RTLD_GLOBAL); |
| 399 | |
| 400 | dyn_chain->next_handle = _dl_handles; |
| 401 | _dl_handles = dyn_ptr = dyn_chain; |
| 402 | |
| 403 | if (tpnt->usage_count > 1) { |
| 404 | _dl_if_debug_print("Lib: %s already opened\n", libname); |
| 405 | /* see if there is a handle from a earlier dlopen */ |
| 406 | for (handle = _dl_handles->next_handle; handle; handle = handle->next_handle) { |
| 407 | if (handle->dyn == tpnt) { |
| 408 | dyn_chain->init_fini.init_fini = handle->init_fini.init_fini; |
| 409 | dyn_chain->init_fini.nlist = handle->init_fini.nlist; |
| 410 | for (i = 0; i < dyn_chain->init_fini.nlist; i++) |
| 411 | dyn_chain->init_fini.init_fini[i]->rtld_flags |= (flag & RTLD_GLOBAL); |
| 412 | dyn_chain->next = handle->next; |
| 413 | break; |
| 414 | } |
| 415 | } |
| 416 | return dyn_chain; |
| 417 | } |
| 418 | |
| 419 | tpnt->init_flag |= DL_OPENED; |
| 420 | |
| 421 | _dl_if_debug_print("Looking for needed libraries\n"); |
| 422 | nlist = 0; |
| 423 | runp = alloca(sizeof(*runp)); |
| 424 | runp->tpnt = tpnt; |
| 425 | runp->next = NULL; |
| 426 | dep_list = runp2 = runp; |
| 427 | for (; runp; runp = runp->next) { |
| 428 | ElfW(Dyn) *dpnt; |
| 429 | char *lpntstr; |
| 430 | |
| 431 | nlist++; |
| 432 | runp->tpnt->init_fini = NULL; /* clear any previous dependcies */ |
| 433 | for (dpnt = (ElfW(Dyn) *) runp->tpnt->dynamic_addr; dpnt->d_tag; dpnt++) { |
| 434 | if (dpnt->d_tag == DT_NEEDED) { |
| 435 | lpntstr = (char*) (runp->tpnt->dynamic_info[DT_STRTAB] + |
| 436 | dpnt->d_un.d_val); |
| 437 | _dl_if_debug_print("Trying to load '%s', needed by '%s'\n", |
| 438 | lpntstr, runp->tpnt->libname); |
| 439 | tpnt1 = _dl_load_shared_library(0, &rpnt, runp->tpnt, lpntstr, 0); |
| 440 | if (!tpnt1) |
| 441 | goto oops; |
| 442 | |
| 443 | tpnt1->rtld_flags |= (flag & RTLD_GLOBAL); |
| 444 | |
| 445 | /* This list is for dlsym() and relocation */ |
| 446 | dyn_ptr->next = (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); |
| 447 | _dl_memset (dyn_ptr->next, 0, sizeof (struct dyn_elf)); |
| 448 | dyn_ptr = dyn_ptr->next; |
| 449 | dyn_ptr->dyn = tpnt1; |
| 450 | /* Used to record RTLD_LOCAL scope */ |
| 451 | tmp = alloca(sizeof(struct init_fini_list)); |
| 452 | tmp->tpnt = tpnt1; |
| 453 | tmp->next = runp->tpnt->init_fini; |
| 454 | runp->tpnt->init_fini = tmp; |
| 455 | |
| 456 | for (tmp=dep_list; tmp; tmp = tmp->next) { |
| 457 | if (tpnt1 == tmp->tpnt) { /* if match => cirular dependency, drop it */ |
| 458 | _dl_if_debug_print("Circular dependency, skipping '%s',\n", |
| 459 | tmp->tpnt->libname); |
| 460 | tpnt1->usage_count--; |
| 461 | break; |
| 462 | } |
| 463 | } |
| 464 | if (!tmp) { /* Don't add if circular dependency detected */ |
| 465 | runp2->next = alloca(sizeof(*runp)); |
| 466 | runp2 = runp2->next; |
| 467 | runp2->tpnt = tpnt1; |
| 468 | runp2->next = NULL; |
| 469 | } |
| 470 | } |
| 471 | } |
| 472 | } |
| 473 | init_fini_list = malloc(nlist * sizeof(struct elf_resolve *)); |
| 474 | dyn_chain->init_fini.init_fini = init_fini_list; |
| 475 | dyn_chain->init_fini.nlist = nlist; |
| 476 | i = 0; |
| 477 | for (runp2 = dep_list; runp2; runp2 = runp2->next) { |
| 478 | init_fini_list[i++] = runp2->tpnt; |
| 479 | for (runp = runp2->tpnt->init_fini; runp; runp = runp->next) { |
| 480 | if (!(runp->tpnt->rtld_flags & RTLD_GLOBAL)) { |
| 481 | tmp = malloc(sizeof(struct init_fini_list)); |
| 482 | tmp->tpnt = runp->tpnt; |
| 483 | tmp->next = runp2->tpnt->rtld_local; |
| 484 | runp2->tpnt->rtld_local = tmp; |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | } |
| 489 | /* Build the local scope for the dynamically loaded modules. */ |
| 490 | local_scope = _dl_malloc(nlist * sizeof(struct elf_resolve *)); /* Could it allocated on stack? */ |
| 491 | for (i = 0; i < nlist; i++) |
| 492 | if (init_fini_list[i]->symbol_scope.r_nlist == 0) { |
| 493 | int k, cnt; |
| 494 | cnt = _dl_build_local_scope(local_scope, init_fini_list[i]); |
| 495 | init_fini_list[i]->symbol_scope.r_list = _dl_malloc(cnt * sizeof(struct elf_resolve *)); |
| 496 | init_fini_list[i]->symbol_scope.r_nlist = cnt; |
| 497 | _dl_memcpy (init_fini_list[i]->symbol_scope.r_list, local_scope, |
| 498 | cnt * sizeof (struct elf_resolve *)); |
| 499 | /* Restoring the init_flag.*/ |
| 500 | for (k = 0; k < nlist; k++) |
| 501 | init_fini_list[k]->init_flag &= ~DL_RESERVED; |
| 502 | } |
| 503 | |
| 504 | _dl_free(local_scope); |
| 505 | |
| 506 | /* Sort the INIT/FINI list in dependency order. */ |
| 507 | for (runp2 = dep_list; runp2; runp2 = runp2->next) { |
| 508 | unsigned int j, k; |
| 509 | for (j = 0; init_fini_list[j] != runp2->tpnt; ++j) |
| 510 | /* Empty */; |
| 511 | for (k = j + 1; k < nlist; ++k) { |
| 512 | struct init_fini_list *ele = init_fini_list[k]->init_fini; |
| 513 | |
| 514 | for (; ele; ele = ele->next) { |
| 515 | if (ele->tpnt == runp2->tpnt) { |
| 516 | struct elf_resolve *here = init_fini_list[k]; |
| 517 | _dl_if_debug_print("Move %s from pos %d to %d in INIT/FINI list.\n", here->libname, k, j); |
| 518 | for (i = (k - j); i; --i) |
| 519 | init_fini_list[i+j] = init_fini_list[i+j-1]; |
| 520 | init_fini_list[j] = here; |
| 521 | ++j; |
| 522 | break; |
| 523 | } |
| 524 | } |
| 525 | } |
| 526 | } |
| 527 | #ifdef __SUPPORT_LD_DEBUG__ |
| 528 | if (_dl_debug) { |
| 529 | fprintf(stderr, "\nINIT/FINI order and dependencies:\n"); |
| 530 | for (i = 0; i < nlist; i++) { |
| 531 | fprintf(stderr, "lib: %s has deps:\n", init_fini_list[i]->libname); |
| 532 | runp = init_fini_list[i]->init_fini; |
| 533 | for (; runp; runp = runp->next) |
| 534 | fprintf(stderr, " %s ", runp->tpnt->libname); |
| 535 | fprintf(stderr, "\n"); |
| 536 | } |
| 537 | } |
| 538 | #endif |
| 539 | |
| 540 | _dl_if_debug_print("Beginning dlopen relocation fixups\n"); |
| 541 | /* |
| 542 | * OK, now all of the kids are tucked into bed in their proper addresses. |
| 543 | * Now we go through and look for REL and RELA records that indicate fixups |
| 544 | * to the GOT tables. We need to do this in reverse order so that COPY |
| 545 | * directives work correctly */ |
| 546 | |
| 547 | /* Get the tail of the list */ |
| 548 | for (ls = &_dl_loaded_modules->symbol_scope; ls && ls->next; ls = ls->next); |
| 549 | |
| 550 | /* Extend the global scope by adding the local scope of the dlopened DSO. */ |
| 551 | ls->next = &dyn_chain->dyn->symbol_scope; |
| 552 | #ifdef __mips__ |
| 553 | /* |
| 554 | * Relocation of the GOT entries for MIPS have to be done |
| 555 | * after all the libraries have been loaded. |
| 556 | */ |
| 557 | _dl_perform_mips_global_got_relocations(tpnt, !now_flag); |
| 558 | #endif |
| 559 | |
| 560 | if (_dl_fixup(dyn_chain, &_dl_loaded_modules->symbol_scope, now_flag)) |
| 561 | goto oops; |
| 562 | |
| 563 | if (relro_ptr) { |
| 564 | for (rpnt = relro_ptr->next; rpnt; rpnt = rpnt->next) { |
| 565 | if (rpnt->dyn->relro_size) |
| 566 | _dl_protect_relro(rpnt->dyn); |
| 567 | } |
| 568 | } |
| 569 | /* TODO: Should we set the protections of all pages back to R/O now ? */ |
| 570 | |
| 571 | |
| 572 | #if defined(USE_TLS) && USE_TLS |
| 573 | |
| 574 | for (i=0; i < nlist; i++) { |
| 575 | struct elf_resolve *tmp_tpnt = init_fini_list[i]; |
| 576 | /* Only add TLS memory if this object is loaded now and |
| 577 | therefore is not yet initialized. */ |
| 578 | |
| 579 | if (!(tmp_tpnt->init_flag & INIT_FUNCS_CALLED) |
| 580 | /* Only if the module defines thread local data. */ |
| 581 | && __builtin_expect (tmp_tpnt->l_tls_blocksize > 0, 0)) { |
| 582 | |
| 583 | /* Now that we know the object is loaded successfully add |
| 584 | modules containing TLS data to the slot info table. We |
| 585 | might have to increase its size. */ |
| 586 | _dl_add_to_slotinfo ((struct link_map*)tmp_tpnt); |
| 587 | |
| 588 | /* It is the case in which we couldn't perform TLS static |
| 589 | initialization at relocation time, and we delayed it until |
| 590 | the relocation has been completed. */ |
| 591 | |
| 592 | if (tmp_tpnt->l_need_tls_init) { |
| 593 | tmp_tpnt->l_need_tls_init = 0; |
| 594 | # ifdef SHARED |
| 595 | /* Update the slot information data for at least the |
| 596 | generation of the DSO we are allocating data for. */ |
| 597 | _dl_update_slotinfo (tmp_tpnt->l_tls_modid); |
| 598 | # endif |
| 599 | |
| 600 | _dl_init_static_tls((struct link_map*)tmp_tpnt); |
| 601 | _dl_assert (tmp_tpnt->l_need_tls_init == 0); |
| 602 | } |
| 603 | |
| 604 | /* We have to bump the generation counter. */ |
| 605 | any_tls = true; |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | /* Bump the generation number if necessary. */ |
| 610 | if (any_tls && __builtin_expect (++_dl_tls_generation == 0, 0)) { |
| 611 | _dl_debug_early("TLS generation counter wrapped! Please report this."); |
| 612 | _dl_exit(30); |
| 613 | } |
| 614 | |
| 615 | #endif |
| 616 | |
| 617 | /* Notify the debugger we have added some objects. */ |
| 618 | if (_dl_debug_addr) { |
| 619 | dl_brk = (void (*)(void)) _dl_debug_addr->r_brk; |
| 620 | if (dl_brk != NULL) { |
| 621 | _dl_debug_addr->r_state = RT_ADD; |
| 622 | (*dl_brk) (); |
| 623 | |
| 624 | _dl_debug_addr->r_state = RT_CONSISTENT; |
| 625 | (*dl_brk) (); |
| 626 | } |
| 627 | } |
| 628 | |
| 629 | /* Run the ctors and setup the dtors */ |
| 630 | for (i = nlist; i; --i) { |
| 631 | tpnt = init_fini_list[i-1]; |
| 632 | if (tpnt->init_flag & INIT_FUNCS_CALLED) |
| 633 | continue; |
| 634 | tpnt->init_flag |= INIT_FUNCS_CALLED; |
| 635 | |
| 636 | if (tpnt->dynamic_info[DT_INIT]) { |
| 637 | void (*dl_elf_func) (void); |
| 638 | dl_elf_func = (void (*)(void)) DL_RELOC_ADDR(tpnt->loadaddr, tpnt->dynamic_info[DT_INIT]); |
| 639 | if (dl_elf_func) { |
| 640 | _dl_if_debug_print("running ctors for library %s at '%p'\n", |
| 641 | tpnt->libname, dl_elf_func); |
| 642 | DL_CALL_FUNC_AT_ADDR (dl_elf_func, tpnt->loadaddr, (void(*)(void))); |
| 643 | } |
| 644 | } |
| 645 | |
| 646 | _dl_run_init_array(tpnt); |
| 647 | } |
| 648 | |
| 649 | _dl_unmap_cache(); |
| 650 | return (void *) dyn_chain; |
| 651 | |
| 652 | oops: |
| 653 | /* Something went wrong. Clean up and return NULL. */ |
| 654 | _dl_unmap_cache(); |
| 655 | do_dlclose(dyn_chain, 0); |
| 656 | return NULL; |
| 657 | } |
| 658 | |
| 659 | void *dlopen(const char *libname, int flag) |
| 660 | { |
| 661 | void *ret; |
| 662 | |
| 663 | __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1); |
| 664 | ret = do_dlopen(libname, flag); |
| 665 | __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1); |
| 666 | |
| 667 | return ret; |
| 668 | } |
| 669 | |
| 670 | static void *do_dlsym(void *vhandle, const char *name, void *caller_address) |
| 671 | { |
| 672 | struct elf_resolve *tpnt, *tfrom; |
| 673 | struct dyn_elf *handle; |
| 674 | ElfW(Addr) from; |
| 675 | struct dyn_elf *rpnt; |
| 676 | void *ret; |
| 677 | struct symbol_ref sym_ref = { NULL, NULL }; |
| 678 | /* Nastiness to support underscore prefixes. */ |
| 679 | #ifdef __UCLIBC_UNDERSCORES__ |
| 680 | char tmp_buf[80]; |
| 681 | char *name2 = tmp_buf; |
| 682 | size_t nlen = strlen (name) + 1; |
| 683 | if (nlen + 1 > sizeof (tmp_buf)) |
| 684 | name2 = malloc (nlen + 1); |
| 685 | if (name2 == 0) { |
| 686 | _dl_error_number = LD_ERROR_MMAP_FAILED; |
| 687 | return 0; |
| 688 | } |
| 689 | name2[0] = '_'; |
| 690 | memcpy (name2 + 1, name, nlen); |
| 691 | #else |
| 692 | const char *name2 = name; |
| 693 | #endif |
| 694 | handle = (struct dyn_elf *) vhandle; |
| 695 | |
| 696 | /* First of all verify that we have a real handle |
| 697 | of some kind. Return NULL if not a valid handle. */ |
| 698 | |
| 699 | if (handle == NULL) |
| 700 | handle = _dl_symbol_tables; |
| 701 | else if (handle != RTLD_NEXT && handle != _dl_symbol_tables) { |
| 702 | for (rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle) |
| 703 | if (rpnt == handle) |
| 704 | break; |
| 705 | if (!rpnt) { |
| 706 | _dl_error_number = LD_BAD_HANDLE; |
| 707 | ret = NULL; |
| 708 | goto out; |
| 709 | } |
| 710 | } else if (handle == RTLD_NEXT) { |
| 711 | /* |
| 712 | * Try and locate the module we were called from - we |
| 713 | * need this so that we know where to start searching |
| 714 | * from. We never pass RTLD_NEXT down into the actual |
| 715 | * dynamic loader itself, as it doesn't know |
| 716 | * how to properly treat it. |
| 717 | */ |
| 718 | from = (ElfW(Addr)) caller_address; |
| 719 | |
| 720 | tfrom = NULL; |
| 721 | for (rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) { |
| 722 | tpnt = rpnt->dyn; |
| 723 | if (DL_ADDR_IN_LOADADDR(from, tpnt, tfrom)) { |
| 724 | tfrom = tpnt; |
| 725 | handle = rpnt->next; |
| 726 | } |
| 727 | } |
| 728 | } |
| 729 | tpnt = NULL; |
| 730 | if (handle == _dl_symbol_tables) |
| 731 | tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */ |
| 732 | ret = _dl_find_hash(name2, &handle->dyn->symbol_scope, tpnt, ELF_RTYPE_CLASS_DLSYM, &sym_ref); |
| 733 | |
| 734 | #if defined(USE_TLS) && USE_TLS && defined SHARED |
| 735 | if (sym_ref.sym && (ELF_ST_TYPE(sym_ref.sym->st_info) == STT_TLS) && (sym_ref.tpnt)) { |
| 736 | /* The found symbol is a thread-local storage variable. |
| 737 | Return its address for the current thread. */ |
| 738 | ret = _dl_tls_symaddr ((struct link_map *)sym_ref.tpnt, (Elf32_Addr)ret); |
| 739 | } |
| 740 | #endif |
| 741 | |
| 742 | /* |
| 743 | * Nothing found. |
| 744 | */ |
| 745 | if (!ret) |
| 746 | _dl_error_number = LD_NO_SYMBOL; |
| 747 | out: |
| 748 | #ifdef __UCLIBC_UNDERSCORES__ |
| 749 | if (name2 != tmp_buf) |
| 750 | free (name2); |
| 751 | #endif |
| 752 | return ret; |
| 753 | } |
| 754 | |
| 755 | void *dlsym(void *vhandle, const char *name) |
| 756 | { |
| 757 | void *ret; |
| 758 | |
| 759 | __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1); |
| 760 | ret = do_dlsym(vhandle, name, __builtin_return_address(0)); |
| 761 | __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1); |
| 762 | |
| 763 | return ret; |
| 764 | } |
| 765 | |
| 766 | #if 0 |
| 767 | void *dlvsym(void *vhandle, const char *name, const char *version) |
| 768 | { |
| 769 | return dlsym(vhandle, name); |
| 770 | } |
| 771 | #endif |
| 772 | |
| 773 | static int do_dlclose(void *vhandle, int need_fini) |
| 774 | { |
| 775 | struct dyn_elf *rpnt, *rpnt1, *rpnt1_tmp; |
| 776 | struct init_fini_list *runp, *tmp; |
| 777 | ElfW(Phdr) *ppnt; |
| 778 | struct elf_resolve *tpnt, *run_tpnt; |
| 779 | int (*dl_elf_fini) (void); |
| 780 | void (*dl_brk) (void); |
| 781 | struct dyn_elf *handle; |
| 782 | unsigned int end = 0, start = 0xffffffff; |
| 783 | unsigned int i, j; |
| 784 | struct r_scope_elem *ls, *ls_next = NULL; |
| 785 | struct elf_resolve **handle_rlist; |
| 786 | |
| 787 | #if defined(USE_TLS) && USE_TLS |
| 788 | bool any_tls = false; |
| 789 | size_t tls_free_start = NO_TLS_OFFSET; |
| 790 | size_t tls_free_end = NO_TLS_OFFSET; |
| 791 | struct link_map *tls_lmap; |
| 792 | #endif |
| 793 | |
| 794 | handle = (struct dyn_elf *) vhandle; |
| 795 | if (handle == _dl_symbol_tables) |
| 796 | return 0; |
| 797 | rpnt1 = NULL; |
| 798 | for (rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle) { |
| 799 | if (rpnt == handle) |
| 800 | break; |
| 801 | rpnt1 = rpnt; |
| 802 | } |
| 803 | |
| 804 | if (!rpnt) { |
| 805 | _dl_error_number = LD_BAD_HANDLE; |
| 806 | return 1; |
| 807 | } |
| 808 | if (rpnt1) |
| 809 | rpnt1->next_handle = rpnt->next_handle; |
| 810 | else |
| 811 | _dl_handles = rpnt->next_handle; |
| 812 | _dl_if_debug_print("%s: usage count: %d\n", |
| 813 | handle->dyn->libname, handle->dyn->usage_count); |
| 814 | if (handle->dyn->usage_count != 1 || (handle->dyn->rtld_flags & RTLD_NODELETE)) { |
| 815 | handle->dyn->usage_count--; |
| 816 | free(handle); |
| 817 | return 0; |
| 818 | } |
| 819 | |
| 820 | /* Store the handle's local scope array for later removal */ |
| 821 | handle_rlist = handle->dyn->symbol_scope.r_list; |
| 822 | |
| 823 | /* Store references to the local scope entries for later removal */ |
| 824 | for (ls = &_dl_loaded_modules->symbol_scope; ls && ls->next; ls = ls->next) |
| 825 | if (ls->next->r_list[0] == handle->dyn) { |
| 826 | break; |
| 827 | } |
| 828 | /* ls points to the previous local symbol scope */ |
| 829 | if(ls && ls->next) |
| 830 | ls_next = ls->next->next; |
| 831 | |
| 832 | /* OK, this is a valid handle - now close out the file */ |
| 833 | for (j = 0; j < handle->init_fini.nlist; ++j) { |
| 834 | tpnt = handle->init_fini.init_fini[j]; |
| 835 | tpnt->usage_count--; |
| 836 | if (tpnt->usage_count == 0 && !(tpnt->rtld_flags & RTLD_NODELETE)) { |
| 837 | if ((tpnt->dynamic_info[DT_FINI] |
| 838 | || tpnt->dynamic_info[DT_FINI_ARRAY]) |
| 839 | && need_fini |
| 840 | && !(tpnt->init_flag & FINI_FUNCS_CALLED) |
| 841 | ) { |
| 842 | tpnt->init_flag |= FINI_FUNCS_CALLED; |
| 843 | _dl_run_fini_array(tpnt); |
| 844 | |
| 845 | if (tpnt->dynamic_info[DT_FINI]) { |
| 846 | dl_elf_fini = (int (*)(void)) DL_RELOC_ADDR(tpnt->loadaddr, tpnt->dynamic_info[DT_FINI]); |
| 847 | _dl_if_debug_print("running dtors for library %s at '%p'\n", |
| 848 | tpnt->libname, dl_elf_fini); |
| 849 | DL_CALL_FUNC_AT_ADDR (dl_elf_fini, tpnt->loadaddr, (int (*)(void))); |
| 850 | } |
| 851 | } |
| 852 | |
| 853 | _dl_if_debug_print("unmapping: %s\n", tpnt->libname); |
| 854 | end = 0; |
| 855 | for (i = 0, ppnt = tpnt->ppnt; |
| 856 | i < tpnt->n_phent; ppnt++, i++) { |
| 857 | if (ppnt->p_type != PT_LOAD) |
| 858 | continue; |
| 859 | if (ppnt->p_vaddr < start) |
| 860 | start = ppnt->p_vaddr; |
| 861 | if (end < ppnt->p_vaddr + ppnt->p_memsz) |
| 862 | end = ppnt->p_vaddr + ppnt->p_memsz; |
| 863 | } |
| 864 | |
| 865 | #if defined(USE_TLS) && USE_TLS |
| 866 | /* Do the cast to make things easy. */ |
| 867 | tls_lmap = (struct link_map *) tpnt; |
| 868 | |
| 869 | /* Remove the object from the dtv slotinfo array if it uses TLS. */ |
| 870 | if (__builtin_expect (tls_lmap->l_tls_blocksize > 0, 0)) { |
| 871 | any_tls = true; |
| 872 | |
| 873 | if (_dl_tls_dtv_slotinfo_list != NULL |
| 874 | && ! remove_slotinfo (tls_lmap->l_tls_modid, |
| 875 | _dl_tls_dtv_slotinfo_list, 0, |
| 876 | (tpnt->init_flag & INIT_FUNCS_CALLED))) |
| 877 | /* All dynamically loaded modules with TLS are unloaded. */ |
| 878 | _dl_tls_max_dtv_idx = _dl_tls_static_nelem; |
| 879 | |
| 880 | if (tls_lmap->l_tls_offset != NO_TLS_OFFSET) { |
| 881 | /* |
| 882 | * Collect a contiguous chunk built from the objects in |
| 883 | * this search list, going in either direction. When the |
| 884 | * whole chunk is at the end of the used area then we can |
| 885 | * reclaim it. |
| 886 | */ |
| 887 | # if defined(TLS_TCB_AT_TP) |
| 888 | if (tls_free_start == NO_TLS_OFFSET |
| 889 | || (size_t) tls_lmap->l_tls_offset == tls_free_start) { |
| 890 | /* Extend the contiguous chunk being reclaimed. */ |
| 891 | tls_free_start |
| 892 | = tls_lmap->l_tls_offset - |
| 893 | tls_lmap->l_tls_blocksize; |
| 894 | |
| 895 | if (tls_free_end == NO_TLS_OFFSET) |
| 896 | tls_free_end = tls_lmap->l_tls_offset; |
| 897 | } else if (tls_lmap->l_tls_offset - tls_lmap->l_tls_blocksize |
| 898 | == tls_free_end) |
| 899 | /* Extend the chunk backwards. */ |
| 900 | tls_free_end = tls_lmap->l_tls_offset; |
| 901 | else { |
| 902 | /* |
| 903 | * This isn't contiguous with the last chunk freed. |
| 904 | * One of them will be leaked unless we can free |
| 905 | * one block right away. |
| 906 | */ |
| 907 | if (tls_free_end == _dl_tls_static_used) { |
| 908 | _dl_tls_static_used = tls_free_start; |
| 909 | tls_free_end = tls_lmap->l_tls_offset; |
| 910 | tls_free_start |
| 911 | = tls_free_end - tls_lmap->l_tls_blocksize; |
| 912 | } else if ((size_t) tls_lmap->l_tls_offset |
| 913 | == _dl_tls_static_used) |
| 914 | _dl_tls_static_used = tls_lmap->l_tls_offset - |
| 915 | tls_lmap->l_tls_blocksize; |
| 916 | else if (tls_free_end < (size_t) tls_lmap->l_tls_offset) { |
| 917 | /* |
| 918 | * We pick the later block. It has a chance |
| 919 | * to be freed. |
| 920 | */ |
| 921 | tls_free_end = tls_lmap->l_tls_offset; |
| 922 | tls_free_start = tls_free_end - |
| 923 | tls_lmap->l_tls_blocksize; |
| 924 | } |
| 925 | } |
| 926 | # elif defined(TLS_DTV_AT_TP) |
| 927 | if ((size_t) tls_lmap->l_tls_offset == tls_free_end) |
| 928 | /* Extend the contiguous chunk being reclaimed. */ |
| 929 | tls_free_end -= tls_lmap->l_tls_blocksize; |
| 930 | else if (tls_lmap->l_tls_offset + tls_lmap->l_tls_blocksize |
| 931 | == tls_free_start) |
| 932 | /* Extend the chunk backwards. */ |
| 933 | tls_free_start = tls_lmap->l_tls_offset; |
| 934 | else { |
| 935 | /* |
| 936 | * This isn't contiguous with the last chunk |
| 937 | * freed. One of them will be leaked. |
| 938 | */ |
| 939 | if (tls_free_end == _dl_tls_static_used) |
| 940 | _dl_tls_static_used = tls_free_start; |
| 941 | tls_free_start = tls_lmap->l_tls_offset; |
| 942 | tls_free_end = tls_free_start + |
| 943 | tls_lmap->l_tls_blocksize; |
| 944 | } |
| 945 | # else |
| 946 | # error Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined |
| 947 | # endif |
| 948 | } else { |
| 949 | |
| 950 | #define TLS_DTV_UNALLOCATED ((void *) -1l) |
| 951 | |
| 952 | dtv_t *dtv = THREAD_DTV (); |
| 953 | |
| 954 | _dl_assert(!(dtv[tls_lmap->l_tls_modid].pointer.is_static)); |
| 955 | if (dtv[tls_lmap->l_tls_modid].pointer.val != TLS_DTV_UNALLOCATED) { |
| 956 | /* Note that free is called for NULL is well. We |
| 957 | deallocate even if it is this dtv entry we are |
| 958 | supposed to load. The reason is that we call |
| 959 | memalign and not malloc. */ |
| 960 | _dl_free (dtv[tls_lmap->l_tls_modid].pointer.val); |
| 961 | dtv[tls_lmap->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED; |
| 962 | } |
| 963 | } |
| 964 | } |
| 965 | #endif |
| 966 | |
| 967 | end = (end + ADDR_ALIGN) & PAGE_ALIGN; |
| 968 | start = start & ~ADDR_ALIGN; |
| 969 | DL_LIB_UNMAP (tpnt, end - start); |
| 970 | /* Free elements in RTLD_LOCAL scope list */ |
| 971 | for (runp = tpnt->rtld_local; runp; runp = tmp) { |
| 972 | tmp = runp->next; |
| 973 | free(runp); |
| 974 | } |
| 975 | |
| 976 | /* Next, remove tpnt from the loaded_module list */ |
| 977 | if (_dl_loaded_modules == tpnt) { |
| 978 | _dl_loaded_modules = tpnt->next; |
| 979 | if (_dl_loaded_modules) |
| 980 | _dl_loaded_modules->prev = 0; |
| 981 | } else { |
| 982 | for (run_tpnt = _dl_loaded_modules; run_tpnt; run_tpnt = run_tpnt->next) { |
| 983 | if (run_tpnt->next == tpnt) { |
| 984 | _dl_if_debug_print("removing loaded_modules: %s\n", tpnt->libname); |
| 985 | run_tpnt->next = run_tpnt->next->next; |
| 986 | if (run_tpnt->next) |
| 987 | run_tpnt->next->prev = run_tpnt; |
| 988 | break; |
| 989 | } |
| 990 | } |
| 991 | } |
| 992 | |
| 993 | /* Next, remove tpnt from the global symbol table list */ |
| 994 | if (_dl_symbol_tables) { |
| 995 | if (_dl_symbol_tables->dyn == tpnt) { |
| 996 | _dl_symbol_tables = _dl_symbol_tables->next; |
| 997 | if (_dl_symbol_tables) |
| 998 | _dl_symbol_tables->prev = 0; |
| 999 | } else { |
| 1000 | for (rpnt1 = _dl_symbol_tables; rpnt1->next; rpnt1 = rpnt1->next) { |
| 1001 | if (rpnt1->next->dyn == tpnt) { |
| 1002 | _dl_if_debug_print("removing symbol_tables: %s\n", tpnt->libname); |
| 1003 | rpnt1_tmp = rpnt1->next->next; |
| 1004 | free(rpnt1->next); |
| 1005 | rpnt1->next = rpnt1_tmp; |
| 1006 | if (rpnt1->next) |
| 1007 | rpnt1->next->prev = rpnt1; |
| 1008 | break; |
| 1009 | } |
| 1010 | } |
| 1011 | } |
| 1012 | } |
| 1013 | free(tpnt->libname); |
| 1014 | if (handle->dyn != tpnt) |
| 1015 | free(tpnt->symbol_scope.r_list); |
| 1016 | free(tpnt); |
| 1017 | } |
| 1018 | } |
| 1019 | /* Unlink and release the handle's local scope from global one */ |
| 1020 | if(ls) |
| 1021 | ls->next = ls_next; |
| 1022 | free(handle_rlist); |
| 1023 | |
| 1024 | for (rpnt1 = handle->next; rpnt1; rpnt1 = rpnt1_tmp) { |
| 1025 | rpnt1_tmp = rpnt1->next; |
| 1026 | free(rpnt1); |
| 1027 | } |
| 1028 | free(handle->init_fini.init_fini); |
| 1029 | free(handle); |
| 1030 | |
| 1031 | #if defined(USE_TLS) && USE_TLS |
| 1032 | /* If we removed any object which uses TLS bump the generation counter. */ |
| 1033 | if (any_tls) { |
| 1034 | if (__builtin_expect(++_dl_tls_generation == 0, 0)) { |
| 1035 | _dl_debug_early("TLS generation counter wrapped! Please report to the uClibc mailing list.\n"); |
| 1036 | _dl_exit(30); |
| 1037 | } |
| 1038 | |
| 1039 | if (tls_free_end == _dl_tls_static_used) |
| 1040 | _dl_tls_static_used = tls_free_start; |
| 1041 | } |
| 1042 | #endif |
| 1043 | |
| 1044 | if (_dl_debug_addr) { |
| 1045 | dl_brk = (void (*)(void)) _dl_debug_addr->r_brk; |
| 1046 | if (dl_brk != NULL) { |
| 1047 | _dl_debug_addr->r_state = RT_DELETE; |
| 1048 | (*dl_brk) (); |
| 1049 | |
| 1050 | _dl_debug_addr->r_state = RT_CONSISTENT; |
| 1051 | (*dl_brk) (); |
| 1052 | } |
| 1053 | } |
| 1054 | |
| 1055 | return 0; |
| 1056 | } |
| 1057 | |
| 1058 | int dlclose(void *vhandle) |
| 1059 | { |
| 1060 | int ret; |
| 1061 | |
| 1062 | __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1); |
| 1063 | ret = do_dlclose(vhandle, 1); |
| 1064 | __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1); |
| 1065 | |
| 1066 | return ret; |
| 1067 | } |
| 1068 | |
| 1069 | char *dlerror(void) |
| 1070 | { |
| 1071 | const char *retval; |
| 1072 | |
| 1073 | if (!_dl_error_number) |
| 1074 | return NULL; |
| 1075 | retval = dl_error_names[_dl_error_number]; |
| 1076 | _dl_error_number = 0; |
| 1077 | return (char *)retval; |
| 1078 | } |
| 1079 | |
| 1080 | /* |
| 1081 | * Dump information to stderr about the current loaded modules |
| 1082 | */ |
| 1083 | #ifdef __USE_GNU |
| 1084 | static const char type[][4] = { "Lib", "Exe", "Int", "Mod" }; |
| 1085 | |
| 1086 | int dlinfo(void) |
| 1087 | { |
| 1088 | struct elf_resolve *tpnt; |
| 1089 | struct dyn_elf *rpnt, *hpnt; |
| 1090 | |
| 1091 | fprintf(stderr, "List of loaded modules\n"); |
| 1092 | /* First start with a complete list of all of the loaded files. */ |
| 1093 | for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) { |
| 1094 | fprintf(stderr, "\t%p %p %p %s %d %s\n", |
| 1095 | DL_LOADADDR_BASE(tpnt->loadaddr), tpnt, tpnt->symbol_scope, |
| 1096 | type[tpnt->libtype], |
| 1097 | tpnt->usage_count, tpnt->libname); |
| 1098 | } |
| 1099 | |
| 1100 | /* Next dump the module list for the application itself */ |
| 1101 | fprintf(stderr, "\nModules for application (%p):\n", _dl_symbol_tables); |
| 1102 | for (rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) |
| 1103 | fprintf(stderr, "\t%p %s\n", rpnt->dyn, rpnt->dyn->libname); |
| 1104 | |
| 1105 | for (hpnt = _dl_handles; hpnt; hpnt = hpnt->next_handle) { |
| 1106 | fprintf(stderr, "Modules for handle %p\n", hpnt); |
| 1107 | for (rpnt = hpnt; rpnt; rpnt = rpnt->next) |
| 1108 | fprintf(stderr, "\t%p %s\n", rpnt->dyn, rpnt->dyn->libname); |
| 1109 | } |
| 1110 | return 0; |
| 1111 | } |
| 1112 | |
| 1113 | static int do_dladdr(const void *__address, Dl_info * __info) |
| 1114 | { |
| 1115 | struct elf_resolve *pelf; |
| 1116 | struct elf_resolve *rpnt; |
| 1117 | |
| 1118 | _dl_map_cache(); |
| 1119 | |
| 1120 | /* |
| 1121 | * Try and locate the module address is in |
| 1122 | */ |
| 1123 | pelf = NULL; |
| 1124 | |
| 1125 | _dl_if_debug_print("__address: %p __info: %p\n", __address, __info); |
| 1126 | |
| 1127 | __address = DL_LOOKUP_ADDRESS (__address); |
| 1128 | |
| 1129 | for (rpnt = _dl_loaded_modules; rpnt; rpnt = rpnt->next) { |
| 1130 | struct elf_resolve *tpnt; |
| 1131 | |
| 1132 | tpnt = rpnt; |
| 1133 | |
| 1134 | _dl_if_debug_print("Module \"%s\" at %p\n", |
| 1135 | tpnt->libname, DL_LOADADDR_BASE(tpnt->loadaddr)); |
| 1136 | |
| 1137 | if (DL_ADDR_IN_LOADADDR((ElfW(Addr)) __address, tpnt, pelf)) |
| 1138 | pelf = tpnt; |
| 1139 | } |
| 1140 | |
| 1141 | if (!pelf) { |
| 1142 | return 0; |
| 1143 | } |
| 1144 | |
| 1145 | /* |
| 1146 | * Try and locate the symbol of address |
| 1147 | */ |
| 1148 | |
| 1149 | { |
| 1150 | char *strtab; |
| 1151 | ElfW(Sym) *symtab; |
| 1152 | unsigned int hn, si, sn, sf; |
| 1153 | ElfW(Addr) sa = 0; |
| 1154 | |
| 1155 | /* Set the info for the object the address lies in */ |
| 1156 | __info->dli_fname = pelf->libname; |
| 1157 | __info->dli_fbase = (void *)pelf->mapaddr; |
| 1158 | |
| 1159 | symtab = (ElfW(Sym) *) (pelf->dynamic_info[DT_SYMTAB]); |
| 1160 | strtab = (char *) (pelf->dynamic_info[DT_STRTAB]); |
| 1161 | |
| 1162 | sf = sn = 0; |
| 1163 | |
| 1164 | #ifdef __LDSO_GNU_HASH_SUPPORT__ |
| 1165 | if (pelf->l_gnu_bitmask) { |
| 1166 | for (hn = 0; hn < pelf->nbucket; hn++) { |
| 1167 | si = pelf->l_gnu_buckets[hn]; |
| 1168 | if (!si) |
| 1169 | continue; |
| 1170 | |
| 1171 | const Elf32_Word *hasharr = &pelf->l_gnu_chain_zero[si]; |
| 1172 | do { |
| 1173 | ElfW(Addr) symbol_addr; |
| 1174 | |
| 1175 | symbol_addr = (ElfW(Addr)) DL_RELOC_ADDR(pelf->loadaddr, symtab[si].st_value); |
| 1176 | if ((symtab[si].st_shndx != SHN_UNDEF |
| 1177 | || symtab[si].st_value != 0) |
| 1178 | && ELF_ST_TYPE(symtab[si].st_info) != STT_TLS |
| 1179 | && DL_ADDR_SYM_MATCH(symbol_addr, &symtab[si], sa, |
| 1180 | (ElfW(Addr)) __address)) { |
| 1181 | sa = symbol_addr; |
| 1182 | sn = si; |
| 1183 | sf = 1; |
| 1184 | } |
| 1185 | _dl_if_debug_print("Symbol \"%s\" at %p\n", strtab + symtab[si].st_name, symbol_addr); |
| 1186 | ++si; |
| 1187 | } while ((*hasharr++ & 1u) == 0); |
| 1188 | } |
| 1189 | } else |
| 1190 | #endif |
| 1191 | for (hn = 0; hn < pelf->nbucket; hn++) { |
| 1192 | for (si = pelf->elf_buckets[hn]; si; si = pelf->chains[si]) { |
| 1193 | ElfW(Addr) symbol_addr; |
| 1194 | |
| 1195 | symbol_addr = (ElfW(Addr)) DL_RELOC_ADDR(pelf->loadaddr, symtab[si].st_value); |
| 1196 | if ((symtab[si].st_shndx != SHN_UNDEF |
| 1197 | || symtab[si].st_value != 0) |
| 1198 | && ELF_ST_TYPE(symtab[si].st_info) != STT_TLS |
| 1199 | && DL_ADDR_SYM_MATCH(symbol_addr, &symtab[si], sa, |
| 1200 | (ElfW(Addr)) __address)) { |
| 1201 | sa = symbol_addr; |
| 1202 | sn = si; |
| 1203 | sf = 1; |
| 1204 | } |
| 1205 | |
| 1206 | _dl_if_debug_print("Symbol \"%s\" at %p\n", |
| 1207 | strtab + symtab[si].st_name, symbol_addr); |
| 1208 | } |
| 1209 | } |
| 1210 | |
| 1211 | if (sf) { |
| 1212 | /* A nearest symbol has been found; fill the entries */ |
| 1213 | __info->dli_sname = strtab + symtab[sn].st_name; |
| 1214 | __info->dli_saddr = (void *)sa; |
| 1215 | } else { |
| 1216 | /* No symbol found, fill entries with NULL value, |
| 1217 | only the containing object will be returned. */ |
| 1218 | __info->dli_sname = NULL; |
| 1219 | __info->dli_saddr = NULL; |
| 1220 | } |
| 1221 | return 1; |
| 1222 | } |
| 1223 | } |
| 1224 | #endif |
| 1225 | |
| 1226 | int dladdr(const void *__address, Dl_info * __info) |
| 1227 | { |
| 1228 | int ret; |
| 1229 | |
| 1230 | __UCLIBC_MUTEX_CONDITIONAL_LOCK(_dl_mutex, 1); |
| 1231 | ret = do_dladdr(__address, __info); |
| 1232 | __UCLIBC_MUTEX_CONDITIONAL_UNLOCK(_dl_mutex, 1); |
| 1233 | |
| 1234 | return ret; |
| 1235 | } |