OSDN Git Service

Fix libdl bug reported by Cedric Hombourger in
[uclinux-h8/uClibc.git] / ldso / libdl / libdl.c
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
36
37 #ifdef SHARED
38
39 /* When libdl is loaded as a shared library, we need to load in
40  * and use a pile of symbols from ldso... */
41
42 extern char *_dl_find_hash(const char *, struct dyn_elf *, struct elf_resolve *, int);
43 extern struct elf_resolve * _dl_load_shared_library(int, struct dyn_elf **,
44         struct elf_resolve *, char *, int);
45 extern int _dl_fixup(struct dyn_elf *rpnt, int lazy);
46 extern void _dl_protect_relro(struct elf_resolve * tpnt);
47 extern int _dl_errno;
48 extern struct dyn_elf *_dl_symbol_tables;
49 extern struct dyn_elf *_dl_handles;
50 extern struct elf_resolve *_dl_loaded_modules;
51 extern struct r_debug *_dl_debug_addr;
52 extern unsigned long _dl_error_number;
53 extern void *(*_dl_malloc_function)(size_t);
54 extern void _dl_run_init_array(struct elf_resolve *);
55 extern void _dl_run_fini_array(struct elf_resolve *);
56 #ifdef __LDSO_CACHE_SUPPORT__
57 int _dl_map_cache(void);
58 int _dl_unmap_cache(void);
59 #endif
60 #ifdef __mips__
61 extern void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt, int lazy);
62 #endif
63 #ifdef __SUPPORT_LD_DEBUG__
64 extern char *_dl_debug;
65 #endif
66
67
68 #else /* SHARED */
69
70 /* When libdl is linked as a static library, we need to replace all
71  * the symbols that otherwise would have been loaded in from ldso... */
72
73 #ifdef __SUPPORT_LD_DEBUG__
74 char *_dl_debug  = 0;
75 #endif
76 const char *_dl_progname       = "";        /* Program name */
77 char *_dl_library_path         = 0;         /* Where we look for libraries */
78 char *_dl_ldsopath             = 0;         /* Location of the shared lib loader */
79 int _dl_errno                  = 0;         /* We can't use the real errno in ldso */
80 size_t _dl_pagesize            = PAGE_SIZE; /* Store the page size for use later */
81 /* This global variable is also to communicate with debuggers such as gdb. */
82 struct r_debug *_dl_debug_addr = NULL;
83 #define _dl_malloc malloc
84 #include "../ldso/dl-debug.c"
85 #include LDSO_ELFINTERP
86 #include "../ldso/dl-hash.c"
87 #define _dl_trace_loaded_objects    0
88 #include "../ldso/dl-elf.c"
89 #endif /* SHARED */
90
91 #ifdef __SUPPORT_LD_DEBUG__
92 # define _dl_if_debug_print(fmt, args...) \
93         do { \
94         if (_dl_debug) \
95                 fprintf(stderr, "%s():%i: " fmt, __FUNCTION__, __LINE__, ## args); \
96         } while (0)
97 #else
98 # define _dl_if_debug_print(fmt, args...)
99 #endif
100
101 static int do_dlclose(void *, int need_fini);
102
103
104 static const char *dl_error_names[] = {
105         "",
106         "File not found",
107         "Unable to open /dev/zero",
108         "Not an ELF file",
109 #if defined (__i386__)
110         "Not i386 binary",
111 #elif defined (__sparc__)
112         "Not sparc binary",
113 #elif defined (__mc68000__)
114         "Not m68k binary",
115 #else
116         "Unrecognized binary type",
117 #endif
118         "Not an ELF shared library",
119         "Unable to mmap file",
120         "No dynamic section",
121 #ifdef ELF_USES_RELOCA
122         "Unable to process REL relocs",
123 #else
124         "Unable to process RELA relocs",
125 #endif
126         "Bad handle",
127         "Unable to resolve symbol"
128 };
129
130 void dl_cleanup(void) __attribute__ ((destructor));
131 void dl_cleanup(void)
132 {
133         struct dyn_elf *d;
134         for (d = _dl_handles; d; d = d->next_handle) {
135                 do_dlclose(d, 1);
136         }
137 }
138
139 void *dlopen(const char *libname, int flag)
140 {
141         struct elf_resolve *tpnt, *tfrom;
142         struct dyn_elf *dyn_chain, *rpnt = NULL, *dyn_ptr, *relro_ptr, *handle;
143         ElfW(Addr) from;
144         struct elf_resolve *tpnt1;
145         void (*dl_brk) (void);
146         int now_flag;
147         struct init_fini_list *tmp, *runp, *runp2, *dep_list;
148         unsigned int nlist, i;
149         struct elf_resolve **init_fini_list;
150
151         /* A bit of sanity checking... */
152         if (!(flag & (RTLD_LAZY|RTLD_NOW))) {
153                 _dl_error_number = LD_BAD_HANDLE;
154                 return NULL;
155         }
156
157         from = (ElfW(Addr)) __builtin_return_address(0);
158
159         /* Cover the trivial case first */
160         if (!libname)
161                 return _dl_symbol_tables;
162
163         _dl_map_cache();
164
165         /*
166          * Try and locate the module we were called from - we
167          * need this so that we get the correct RPATH/RUNPATH.  Note that
168          * this is the current behavior under Solaris, but the
169          * ABI+ specifies that we should only use the RPATH from
170          * the application.  Thus this may go away at some time
171          * in the future.
172          */
173         {
174                 struct dyn_elf *dpnt;
175                 tfrom = NULL;
176                 for (dpnt = _dl_symbol_tables; dpnt; dpnt = dpnt->next) {
177                         tpnt = dpnt->dyn;
178                         if (DL_ADDR_IN_LOADADDR(from, tpnt, tfrom))
179                                 tfrom = tpnt;
180                 }
181         }
182         for(rpnt = _dl_symbol_tables; rpnt && rpnt->next; rpnt=rpnt->next);
183
184         relro_ptr = rpnt;
185         now_flag = (flag & RTLD_NOW) ? RTLD_NOW : 0;
186         if (getenv("LD_BIND_NOW"))
187                 now_flag = RTLD_NOW;
188
189         /* Try to load the specified library */
190         _dl_if_debug_print("Trying to dlopen '%s', RTLD_GLOBAL:%d RTLD_NOW:%d\n",
191                         (char*)libname, (flag & RTLD_GLOBAL ? 1:0), (now_flag & RTLD_NOW ? 1:0));
192         tpnt = _dl_load_shared_library(0, &rpnt, tfrom, (char*)libname, 0);
193
194         if (tpnt == NULL) {
195                 _dl_unmap_cache();
196                 return NULL;
197         }
198         dyn_chain = (struct dyn_elf *) malloc(sizeof(struct dyn_elf));
199         _dl_memset(dyn_chain, 0, sizeof(struct dyn_elf));
200         dyn_chain->dyn = tpnt;
201         tpnt->rtld_flags |= (flag & RTLD_GLOBAL);
202
203         dyn_chain->next_handle = _dl_handles;
204         _dl_handles = dyn_ptr = dyn_chain;
205
206         if (tpnt->usage_count > 1) {
207                 _dl_if_debug_print("Lib: %s already opened\n", libname);
208                 /* see if there is a handle from a earlier dlopen */
209                 for (handle = _dl_handles->next_handle; handle; handle = handle->next_handle) {
210                         if (handle->dyn == tpnt) {
211                                 dyn_chain->init_fini.init_fini = handle->init_fini.init_fini;
212                                 dyn_chain->init_fini.nlist = handle->init_fini.nlist;
213                                 for(i=0; i < dyn_chain->init_fini.nlist; i++)
214                                         dyn_chain->init_fini.init_fini[i]->rtld_flags |= (flag & RTLD_GLOBAL);
215                                 dyn_chain->next = handle->next;
216                                 break;
217                         }
218                 }
219                 return dyn_chain;
220         } else {
221                 tpnt->init_flag |= DL_OPENED;
222         }
223
224         _dl_if_debug_print("Looking for needed libraries\n");
225         nlist = 0;
226         runp = alloca(sizeof(*runp));
227         runp->tpnt = tpnt;
228         runp->next = NULL;
229         dep_list = runp2 = runp;
230         for (; runp; runp = runp->next)
231         {
232                 ElfW(Dyn) *dpnt;
233                 char *lpntstr;
234
235                 nlist++;
236                 runp->tpnt->init_fini = NULL; /* clear any previous dependcies */
237                 for (dpnt = (ElfW(Dyn) *) runp->tpnt->dynamic_addr; dpnt->d_tag; dpnt++) {
238                         if (dpnt->d_tag == DT_NEEDED) {
239                                 lpntstr = (char*) (runp->tpnt->dynamic_info[DT_STRTAB] +
240                                                 dpnt->d_un.d_val);
241                                 _dl_if_debug_print("Trying to load '%s', needed by '%s'\n",
242                                                 lpntstr, runp->tpnt->libname);
243                                 tpnt1 = _dl_load_shared_library(0, &rpnt, runp->tpnt, lpntstr, 0);
244                                 if (!tpnt1)
245                                         goto oops;
246
247                                 tpnt1->rtld_flags |= (flag & RTLD_GLOBAL);
248
249                                 /* This list is for dlsym() and relocation */
250                                 dyn_ptr->next = (struct dyn_elf *) malloc(sizeof(struct dyn_elf));
251                                 _dl_memset (dyn_ptr->next, 0, sizeof (struct dyn_elf));
252                                 dyn_ptr = dyn_ptr->next;
253                                 dyn_ptr->dyn = tpnt1;
254                                 /* Used to record RTLD_LOCAL scope */
255                                 tmp = alloca(sizeof(struct init_fini_list));
256                                 tmp->tpnt = tpnt1;
257                                 tmp->next = runp->tpnt->init_fini;
258                                 runp->tpnt->init_fini = tmp;
259
260                                 for (tmp=dep_list; tmp; tmp = tmp->next) {
261                                         if (tpnt1 == tmp->tpnt) { /* if match => cirular dependency, drop it */
262                                                 _dl_if_debug_print("Circular dependency, skipping '%s',\n",
263                                                                    tmp->tpnt->libname);
264                                                 tpnt1->usage_count--;
265                                                 break;
266                                         }
267                                 }
268                                 if (!tmp) { /* Don't add if circular dependency detected */
269                                         runp2->next = alloca(sizeof(*runp));
270                                         runp2 = runp2->next;
271                                         runp2->tpnt = tpnt1;
272                                         runp2->next = NULL;
273                                 }
274                         }
275                 }
276         }
277         init_fini_list = malloc(nlist * sizeof(struct elf_resolve *));
278         dyn_chain->init_fini.init_fini = init_fini_list;
279         dyn_chain->init_fini.nlist = nlist;
280         i = 0;
281         for (runp2 = dep_list; runp2; runp2 = runp2->next) {
282                 init_fini_list[i++] = runp2->tpnt;
283                 for(runp = runp2->tpnt->init_fini; runp; runp = runp->next){
284                         if (!(runp->tpnt->rtld_flags & RTLD_GLOBAL)) {
285                                 tmp = malloc(sizeof(struct init_fini_list));
286                                 tmp->tpnt = runp->tpnt;
287                                 tmp->next = runp2->tpnt->rtld_local;
288                                 runp2->tpnt->rtld_local = tmp;
289                         }
290                 }
291
292         }
293         /* Sort the INIT/FINI list in dependency order. */
294         for (runp2 = dep_list; runp2; runp2 = runp2->next) {
295                 unsigned int j, k;
296                 for (j = 0; init_fini_list[j] != runp2->tpnt; ++j)
297                         /* Empty */;
298                 for (k = j + 1; k < nlist; ++k) {
299                         struct init_fini_list *ele = init_fini_list[k]->init_fini;
300
301                         for (; ele; ele = ele->next) {
302                                 if (ele->tpnt == runp2->tpnt) {
303                                         struct elf_resolve *here = init_fini_list[k];
304                                         _dl_if_debug_print("Move %s from pos %d to %d in INIT/FINI list.\n", here->libname, k, j);
305                                         for (i = (k - j); i; --i)
306                                                 init_fini_list[i+j] = init_fini_list[i+j-1];
307                                         init_fini_list[j] = here;
308                                         ++j;
309                                         break;
310                                 }
311                         }
312                 }
313         }
314 #ifdef __SUPPORT_LD_DEBUG__
315         if(_dl_debug) {
316                 fprintf(stderr, "\nINIT/FINI order and dependencies:\n");
317                 for (i=0;i < nlist;i++) {
318                         fprintf(stderr, "lib: %s has deps:\n", init_fini_list[i]->libname);
319                         runp = init_fini_list[i]->init_fini;
320                         for (; runp; runp = runp->next)
321                                 printf(" %s ", runp->tpnt->libname);
322                         printf("\n");
323                 }
324         }
325 #endif
326
327         _dl_if_debug_print("Beginning dlopen relocation fixups\n");
328         /*
329          * OK, now all of the kids are tucked into bed in their proper addresses.
330          * Now we go through and look for REL and RELA records that indicate fixups
331          * to the GOT tables.  We need to do this in reverse order so that COPY
332          * directives work correctly */
333 #ifdef __mips__
334         /*
335          * Relocation of the GOT entries for MIPS have to be done
336          * after all the libraries have been loaded.
337          */
338         _dl_perform_mips_global_got_relocations(tpnt, !now_flag);
339 #endif
340
341         if (_dl_fixup(dyn_chain, now_flag))
342                 goto oops;
343
344         if (relro_ptr) {
345                 for (rpnt = relro_ptr->next; rpnt; rpnt = rpnt->next) {
346                         if (rpnt->dyn->relro_size)
347                                 _dl_protect_relro(rpnt->dyn);
348                 }
349         }
350         /* TODO:  Should we set the protections of all pages back to R/O now ? */
351
352
353         /* Notify the debugger we have added some objects. */
354         if (_dl_debug_addr) {
355                 dl_brk = (void (*)(void)) _dl_debug_addr->r_brk;
356                 if (dl_brk != NULL) {
357                         _dl_debug_addr->r_state = RT_ADD;
358                         (*dl_brk) ();
359
360                         _dl_debug_addr->r_state = RT_CONSISTENT;
361                         (*dl_brk) ();
362                 }
363         }
364
365 #ifdef SHARED
366         /* Run the ctors and setup the dtors */
367         for (i = nlist; i; --i) {
368                 tpnt = init_fini_list[i-1];
369                 if (tpnt->init_flag & INIT_FUNCS_CALLED)
370                         continue;
371                 tpnt->init_flag |= INIT_FUNCS_CALLED;
372
373                 if (tpnt->dynamic_info[DT_INIT]) {
374                         void (*dl_elf_func) (void);
375                         dl_elf_func = (void (*)(void)) DL_RELOC_ADDR(tpnt->loadaddr, tpnt->dynamic_info[DT_INIT]);
376                         if (dl_elf_func) {
377                                 _dl_if_debug_print("running ctors for library %s at '%p'\n",
378                                                 tpnt->libname, dl_elf_func);
379                                 DL_CALL_FUNC_AT_ADDR (dl_elf_func, tpnt->loadaddr, (void(*)(void)));
380                         }
381                 }
382
383                 _dl_run_init_array(tpnt);
384         }
385 #endif /* SHARED */
386
387         _dl_unmap_cache();
388         return (void *) dyn_chain;
389
390 oops:
391         /* Something went wrong.  Clean up and return NULL. */
392         _dl_unmap_cache();
393         do_dlclose(dyn_chain, 0);
394         return NULL;
395 }
396
397 void *dlsym(void *vhandle, const char *name)
398 {
399         struct elf_resolve *tpnt, *tfrom;
400         struct dyn_elf *handle;
401         ElfW(Addr) from;
402         struct dyn_elf *rpnt;
403         void *ret;
404
405         handle = (struct dyn_elf *) vhandle;
406
407         /* First of all verify that we have a real handle
408            of some kind.  Return NULL if not a valid handle. */
409
410         if (handle == NULL)
411                 handle = _dl_symbol_tables;
412         else if (handle != RTLD_NEXT && handle != _dl_symbol_tables) {
413                 for (rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle)
414                         if (rpnt == handle)
415                                 break;
416                 if (!rpnt) {
417                         _dl_error_number = LD_BAD_HANDLE;
418                         return NULL;
419                 }
420         } else if (handle == RTLD_NEXT) {
421                 /*
422                  * Try and locate the module we were called from - we
423                  * need this so that we know where to start searching
424                  * from.  We never pass RTLD_NEXT down into the actual
425                  * dynamic loader itself, as it doesn't know
426                  * how to properly treat it.
427                  */
428                 from = (ElfW(Addr)) __builtin_return_address(0);
429
430                 tfrom = NULL;
431                 for (rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next) {
432                         tpnt = rpnt->dyn;
433                         if (DL_ADDR_IN_LOADADDR(from, tpnt, tfrom)) {
434                                 tfrom = tpnt;
435                                 handle = rpnt->next;
436                         }
437                 }
438         }
439         tpnt = NULL;
440         if (handle == _dl_symbol_tables)
441            tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */
442         ret = _dl_find_hash((char*)name, handle, tpnt, 0);
443
444         /*
445          * Nothing found.
446          */
447         if (!ret)
448                 _dl_error_number = LD_NO_SYMBOL;
449         return ret;
450 }
451
452 #if 0
453 void *dlvsym(void *vhandle, const char *name, const char *version)
454 {
455         return dlsym(vhandle, name);
456 }
457 #endif
458
459 static int do_dlclose(void *vhandle, int need_fini)
460 {
461         struct dyn_elf *rpnt, *rpnt1, *rpnt1_tmp;
462         struct init_fini_list *runp, *tmp;
463         ElfW(Phdr) *ppnt;
464         struct elf_resolve *tpnt, *run_tpnt;
465         int (*dl_elf_fini) (void);
466         void (*dl_brk) (void);
467         struct dyn_elf *handle;
468         unsigned int end;
469         unsigned int i, j;
470
471         handle = (struct dyn_elf *) vhandle;
472         if (handle == _dl_symbol_tables)
473                 return 0;
474         rpnt1 = NULL;
475         for (rpnt = _dl_handles; rpnt; rpnt = rpnt->next_handle) {
476                 if (rpnt == handle)
477                         break;
478                 rpnt1 = rpnt;
479         }
480
481         if (!rpnt) {
482                 _dl_error_number = LD_BAD_HANDLE;
483                 return 1;
484         }
485         if (rpnt1)
486                 rpnt1->next_handle = rpnt->next_handle;
487         else
488                 _dl_handles = rpnt->next_handle;
489         _dl_if_debug_print("%s: usage count: %d\n",
490                         handle->dyn->libname, handle->dyn->usage_count);
491         if (handle->dyn->usage_count != 1) {
492                 handle->dyn->usage_count--;
493                 free(handle);
494                 return 0;
495         }
496         /* OK, this is a valid handle - now close out the file */
497         for (j = 0; j < handle->init_fini.nlist; ++j) {
498                 tpnt = handle->init_fini.init_fini[j];
499                 if (--tpnt->usage_count == 0) {
500                         if ((tpnt->dynamic_info[DT_FINI]
501                              || tpnt->dynamic_info[DT_FINI_ARRAY])
502                             && need_fini &&
503                             !(tpnt->init_flag & FINI_FUNCS_CALLED)) {
504                                 tpnt->init_flag |= FINI_FUNCS_CALLED;
505 #ifdef SHARED
506                                 _dl_run_fini_array(tpnt);
507 #endif
508
509                                 if (tpnt->dynamic_info[DT_FINI]) {
510                                         dl_elf_fini = (int (*)(void)) DL_RELOC_ADDR(tpnt->loadaddr, tpnt->dynamic_info[DT_FINI]);
511                                         _dl_if_debug_print("running dtors for library %s at '%p'\n",
512                                                         tpnt->libname, dl_elf_fini);
513                                         DL_CALL_FUNC_AT_ADDR (dl_elf_fini, tpnt->loadaddr, (int (*)(void)));
514                                 }
515                         }
516
517                         _dl_if_debug_print("unmapping: %s\n", tpnt->libname);
518                         end = 0;
519                         for (i = 0, ppnt = tpnt->ppnt;
520                                         i < tpnt->n_phent; ppnt++, i++) {
521                                 if (ppnt->p_type != PT_LOAD)
522                                         continue;
523                                 if (end < ppnt->p_vaddr + ppnt->p_memsz)
524                                         end = ppnt->p_vaddr + ppnt->p_memsz;
525                         }
526                         _dl_munmap((void*)tpnt->loadaddr, end);
527                         /* Free elements in RTLD_LOCAL scope list */ 
528                         for (runp = tpnt->rtld_local; runp; runp = tmp) {
529                                 tmp = runp->next;
530                                 free(runp);
531                         }
532
533                         /* Next, remove tpnt from the loaded_module list */
534                         if (_dl_loaded_modules == tpnt) {
535                                 _dl_loaded_modules = tpnt->next;
536                                 if (_dl_loaded_modules)
537                                         _dl_loaded_modules->prev = 0;
538                         } else
539                                 for (run_tpnt = _dl_loaded_modules; run_tpnt; run_tpnt = run_tpnt->next)
540                                         if (run_tpnt->next == tpnt) {
541                                                 _dl_if_debug_print("removing loaded_modules: %s\n", tpnt->libname);
542                                                 run_tpnt->next = run_tpnt->next->next;
543                                                 if (run_tpnt->next)
544                                                         run_tpnt->next->prev = run_tpnt;
545                                                 break;
546                                         }
547
548                         /* Next, remove tpnt from the global symbol table list */
549                         if (_dl_symbol_tables) {
550                                 if (_dl_symbol_tables->dyn == tpnt) {
551                                         _dl_symbol_tables = _dl_symbol_tables->next;
552                                         if (_dl_symbol_tables)
553                                                 _dl_symbol_tables->prev = 0;
554                                 } else
555                                         for (rpnt1 = _dl_symbol_tables; rpnt1->next; rpnt1 = rpnt1->next) {
556                                                 if (rpnt1->next->dyn == tpnt) {
557                                                         _dl_if_debug_print("removing symbol_tables: %s\n", tpnt->libname);
558                                                         rpnt1_tmp = rpnt1->next->next;
559                                                         free(rpnt1->next);
560                                                         rpnt1->next = rpnt1_tmp;
561                                                         if (rpnt1->next)
562                                                                 rpnt1->next->prev = rpnt1;
563                                                         break;
564                                                 }
565                                         }
566                         }
567                         free(tpnt->libname);
568                         free(tpnt);
569                 }
570         }
571         free(handle->init_fini.init_fini);
572         free(handle);
573
574
575         if (_dl_debug_addr) {
576                 dl_brk = (void (*)(void)) _dl_debug_addr->r_brk;
577                 if (dl_brk != NULL) {
578                         _dl_debug_addr->r_state = RT_DELETE;
579                         (*dl_brk) ();
580
581                         _dl_debug_addr->r_state = RT_CONSISTENT;
582                         (*dl_brk) ();
583                 }
584         }
585
586         return 0;
587 }
588
589 int dlclose(void *vhandle)
590 {
591         return do_dlclose(vhandle, 1);
592 }
593
594 char *dlerror(void)
595 {
596         const char *retval;
597
598         if (!_dl_error_number)
599                 return NULL;
600         retval = dl_error_names[_dl_error_number];
601         _dl_error_number = 0;
602         return (char *)retval;
603 }
604
605 /*
606  * Dump information to stderr about the current loaded modules
607  */
608 #ifdef __USE_GNU
609 static char *type[] = { "Lib", "Exe", "Int", "Mod" };
610
611 int dlinfo(void)
612 {
613         struct elf_resolve *tpnt;
614         struct dyn_elf *rpnt, *hpnt;
615
616         fprintf(stderr, "List of loaded modules\n");
617         /* First start with a complete list of all of the loaded files. */
618         for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) {
619                 fprintf(stderr, "\t%p %p %p %s %d %s\n",
620                         DL_LOADADDR_BASE(tpnt->loadaddr), tpnt, tpnt->symbol_scope,
621                         type[tpnt->libtype],
622                         tpnt->usage_count, tpnt->libname);
623         }
624
625         /* Next dump the module list for the application itself */
626         fprintf(stderr, "\nModules for application (%p):\n", _dl_symbol_tables);
627         for (rpnt = _dl_symbol_tables; rpnt; rpnt = rpnt->next)
628                 fprintf(stderr, "\t%p %s\n", rpnt->dyn, rpnt->dyn->libname);
629
630         for (hpnt = _dl_handles; hpnt; hpnt = hpnt->next_handle) {
631                 fprintf(stderr, "Modules for handle %p\n", hpnt);
632                 for (rpnt = hpnt; rpnt; rpnt = rpnt->next)
633                         fprintf(stderr, "\t%p %s\n", rpnt->dyn, rpnt->dyn->libname);
634         }
635         return 0;
636 }
637
638 int dladdr(const void *__address, Dl_info * __info)
639 {
640         struct elf_resolve *pelf;
641         struct elf_resolve *rpnt;
642
643         _dl_map_cache();
644
645         /*
646          * Try and locate the module address is in
647          */
648         pelf = NULL;
649
650         _dl_if_debug_print("__address: %p  __info: %p\n", __address, __info);
651
652         for (rpnt = _dl_loaded_modules; rpnt; rpnt = rpnt->next) {
653                 struct elf_resolve *tpnt;
654
655                 tpnt = rpnt;
656
657                 _dl_if_debug_print("Module \"%s\" at %p\n",
658                                    tpnt->libname, DL_LOADADDR_BASE(tpnt->loadaddr));
659
660                 if (DL_ADDR_IN_LOADADDR((ElfW(Addr)) __address, tpnt, pelf))
661                         pelf = tpnt;
662         }
663
664         if (!pelf) {
665                 return 0;
666         }
667
668         /*
669          * Try and locate the symbol of address
670          */
671
672         {
673                 char *strtab;
674                 ElfW(Sym) *symtab;
675                 unsigned int hn, si, sn, sf;
676                 ElfW(Addr) sa;
677
678                 sa = 0;
679                 symtab = (ElfW(Sym) *) (pelf->dynamic_info[DT_SYMTAB]);
680                 strtab = (char *) (pelf->dynamic_info[DT_STRTAB]);
681
682                 sf = sn = 0;
683                 for (hn = 0; hn < pelf->nbucket; hn++) {
684                         for (si = pelf->elf_buckets[hn]; si; si = pelf->chains[si]) {
685                                 ElfW(Addr) symbol_addr;
686
687                                 symbol_addr = (ElfW(Addr)) DL_RELOC_ADDR(pelf->loadaddr, symtab[si].st_value);
688                                 if (symbol_addr <= (ElfW(Addr))__address && (!sf || sa < symbol_addr)) {
689                                         sa = symbol_addr;
690                                         sn = si;
691                                         sf = 1;
692                                 }
693
694                                 _dl_if_debug_print("Symbol \"%s\" at %p\n",
695                                                    strtab + symtab[si].st_name, symbol_addr);
696                         }
697                 }
698
699                 if (sf) {
700                         __info->dli_fname = pelf->libname;
701                         __info->dli_fbase = (void *) DL_LOADADDR_BASE(pelf->loadaddr);
702                         __info->dli_sname = strtab + symtab[sn].st_name;
703                         __info->dli_saddr = (void *)sa;
704                 }
705                 return 1;
706         }
707 }
708 #endif