OSDN Git Service

Avoid warnings about shifting more bits than we have
[uclinux-h8/uClibc.git] / ldso / libdl / libdl.c
index 505f095..8646a74 100644 (file)
@@ -32,6 +32,8 @@
 
 #include <ldso.h>
 #include <stdio.h>
+#include <string.h> /* Needed for 'strstr' prototype' */
+#include <stdbool.h>
 
 
 #ifdef SHARED
@@ -51,6 +53,7 @@ extern struct elf_resolve *_dl_loaded_modules;
 extern struct r_debug *_dl_debug_addr;
 extern unsigned long _dl_error_number;
 extern void *(*_dl_malloc_function)(size_t);
+extern void (*_dl_free_function) (void *p);
 extern void _dl_run_init_array(struct elf_resolve *);
 extern void _dl_run_fini_array(struct elf_resolve *);
 #ifdef __LDSO_CACHE_SUPPORT__
@@ -64,23 +67,35 @@ extern void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt, in
 extern char *_dl_debug;
 #endif
 
+#else /* !SHARED */
 
-#else /* SHARED */
+#define _dl_malloc malloc
+#define _dl_free free
 
 /* When libdl is linked as a static library, we need to replace all
  * the symbols that otherwise would have been loaded in from ldso... */
 
 #ifdef __SUPPORT_LD_DEBUG__
-char *_dl_debug  = 0;
+char *_dl_debug  = NULL;
+char *_dl_debug_symbols   = NULL;
+char *_dl_debug_move      = NULL;
+char *_dl_debug_reloc     = NULL;
+char *_dl_debug_detail    = NULL;
+char *_dl_debug_nofixups  = NULL;
+char *_dl_debug_bindings  = NULL;
+int   _dl_debug_file      = NULL;
 #endif
 const char *_dl_progname       = "";        /* Program name */
-char *_dl_library_path         = 0;         /* Where we look for libraries */
-char *_dl_ldsopath             = 0;         /* Location of the shared lib loader */
+void *(*_dl_malloc_function)(size_t);
+void (*_dl_free_function) (void *p);
+char *_dl_library_path         = NULL;         /* Where we look for libraries */
+char *_dl_ldsopath             = NULL;         /* Location of the shared lib loader */
 int _dl_errno                  = 0;         /* We can't use the real errno in ldso */
 size_t _dl_pagesize            = PAGE_SIZE; /* Store the page size for use later */
 /* This global variable is also to communicate with debuggers such as gdb. */
 struct r_debug *_dl_debug_addr = NULL;
-#define _dl_malloc malloc
+
+#include "../ldso/dl-array.c"
 #include "../ldso/dl-debug.c"
 #include LDSO_ELFINTERP
 #include "../ldso/dl-hash.c"
@@ -101,7 +116,7 @@ struct r_debug *_dl_debug_addr = NULL;
 static int do_dlclose(void *, int need_fini);
 
 
-static const char *dl_error_names[] = {
+static const char *const dl_error_names[] = {
        "",
        "File not found",
        "Unable to open /dev/zero",
@@ -130,9 +145,11 @@ static const char *dl_error_names[] = {
 void dl_cleanup(void) __attribute__ ((destructor));
 void dl_cleanup(void)
 {
-       struct dyn_elf *d;
-       for (d = _dl_handles; d; d = d->next_handle) {
-               do_dlclose(d, 1);
+       struct dyn_elf *h, *n;
+
+       for (h = _dl_handles; h; h = n) {
+               n = h->next_handle;
+               do_dlclose(h, 1);
        }
 }
 
@@ -147,6 +164,7 @@ void *dlopen(const char *libname, int flag)
        struct init_fini_list *tmp, *runp, *runp2, *dep_list;
        unsigned int nlist, i;
        struct elf_resolve **init_fini_list;
+       static bool _dl_init;
 
        /* A bit of sanity checking... */
        if (!(flag & (RTLD_LAZY|RTLD_NOW))) {
@@ -156,10 +174,34 @@ void *dlopen(const char *libname, int flag)
 
        from = (ElfW(Addr)) __builtin_return_address(0);
 
+       if (!_dl_init) {
+               _dl_init = true;
+               _dl_malloc_function = malloc;
+               _dl_free_function = free;
+       }
        /* Cover the trivial case first */
        if (!libname)
                return _dl_symbol_tables;
 
+#ifndef SHARED
+# ifdef __SUPPORT_LD_DEBUG__
+       _dl_debug = getenv("LD_DEBUG");
+       if (_dl_debug) {
+               if (_dl_strstr(_dl_debug, "all")) {
+                       _dl_debug_detail = _dl_debug_move = _dl_debug_symbols
+                               = _dl_debug_reloc = _dl_debug_bindings = _dl_debug_nofixups = (void*)1;
+               } else {
+                       _dl_debug_detail   = strstr(_dl_debug, "detail");
+                       _dl_debug_move     = strstr(_dl_debug, "move");
+                       _dl_debug_symbols  = strstr(_dl_debug, "sym");
+                       _dl_debug_reloc    = strstr(_dl_debug, "reloc");
+                       _dl_debug_nofixups = strstr(_dl_debug, "nofix");
+                       _dl_debug_bindings = strstr(_dl_debug, "bind");
+               }
+       }
+# endif
+#endif
+
        _dl_map_cache();
 
        /*
@@ -179,13 +221,19 @@ void *dlopen(const char *libname, int flag)
                                tfrom = tpnt;
                }
        }
-       for(rpnt = _dl_symbol_tables; rpnt && rpnt->next; rpnt=rpnt->next);
+       for (rpnt = _dl_symbol_tables; rpnt && rpnt->next; rpnt = rpnt->next)
+               continue;
 
        relro_ptr = rpnt;
        now_flag = (flag & RTLD_NOW) ? RTLD_NOW : 0;
        if (getenv("LD_BIND_NOW"))
                now_flag = RTLD_NOW;
 
+#ifndef SHARED
+       /* When statically linked, the _dl_library_path is not yet initialized */
+       _dl_library_path = getenv("LD_LIBRARY_PATH");
+#endif
+
        /* Try to load the specified library */
        _dl_if_debug_print("Trying to dlopen '%s', RTLD_GLOBAL:%d RTLD_NOW:%d\n",
                        (char*)libname, (flag & RTLD_GLOBAL ? 1:0), (now_flag & RTLD_NOW ? 1:0));
@@ -210,25 +258,24 @@ void *dlopen(const char *libname, int flag)
                        if (handle->dyn == tpnt) {
                                dyn_chain->init_fini.init_fini = handle->init_fini.init_fini;
                                dyn_chain->init_fini.nlist = handle->init_fini.nlist;
-                               for(i=0; i < dyn_chain->init_fini.nlist; i++)
+                               for (i = 0; i < dyn_chain->init_fini.nlist; i++)
                                        dyn_chain->init_fini.init_fini[i]->rtld_flags |= (flag & RTLD_GLOBAL);
                                dyn_chain->next = handle->next;
                                break;
                        }
                }
                return dyn_chain;
-       } else {
-               tpnt->init_flag |= DL_OPENED;
        }
 
+       tpnt->init_flag |= DL_OPENED;
+
        _dl_if_debug_print("Looking for needed libraries\n");
        nlist = 0;
        runp = alloca(sizeof(*runp));
        runp->tpnt = tpnt;
        runp->next = NULL;
        dep_list = runp2 = runp;
-       for (; runp; runp = runp->next)
-       {
+       for (; runp; runp = runp->next) {
                ElfW(Dyn) *dpnt;
                char *lpntstr;
 
@@ -280,7 +327,7 @@ void *dlopen(const char *libname, int flag)
        i = 0;
        for (runp2 = dep_list; runp2; runp2 = runp2->next) {
                init_fini_list[i++] = runp2->tpnt;
-               for(runp = runp2->tpnt->init_fini; runp; runp = runp->next){
+               for (runp = runp2->tpnt->init_fini; runp; runp = runp->next) {
                        if (!(runp->tpnt->rtld_flags & RTLD_GLOBAL)) {
                                tmp = malloc(sizeof(struct init_fini_list));
                                tmp->tpnt = runp->tpnt;
@@ -312,14 +359,14 @@ void *dlopen(const char *libname, int flag)
                }
        }
 #ifdef __SUPPORT_LD_DEBUG__
-       if(_dl_debug) {
+       if (_dl_debug) {
                fprintf(stderr, "\nINIT/FINI order and dependencies:\n");
-               for (i=0;i < nlist;i++) {
+               for (i = 0; i < nlist; i++) {
                        fprintf(stderr, "lib: %s has deps:\n", init_fini_list[i]->libname);
                        runp = init_fini_list[i]->init_fini;
                        for (; runp; runp = runp->next)
-                               printf(" %s ", runp->tpnt->libname);
-                       printf("\n");
+                               fprintf(stderr, " %s ", runp->tpnt->libname);
+                       fprintf(stderr, "\n");
                }
        }
 #endif
@@ -349,7 +396,6 @@ void *dlopen(const char *libname, int flag)
        }
        /* TODO:  Should we set the protections of all pages back to R/O now ? */
 
-
        /* Notify the debugger we have added some objects. */
        if (_dl_debug_addr) {
                dl_brk = (void (*)(void)) _dl_debug_addr->r_brk;
@@ -362,7 +408,6 @@ void *dlopen(const char *libname, int flag)
                }
        }
 
-#ifdef SHARED
        /* Run the ctors and setup the dtors */
        for (i = nlist; i; --i) {
                tpnt = init_fini_list[i-1];
@@ -382,7 +427,6 @@ void *dlopen(const char *libname, int flag)
 
                _dl_run_init_array(tpnt);
        }
-#endif /* SHARED */
 
        _dl_unmap_cache();
        return (void *) dyn_chain;
@@ -401,7 +445,22 @@ void *dlsym(void *vhandle, const char *name)
        ElfW(Addr) from;
        struct dyn_elf *rpnt;
        void *ret;
-
+       /* Nastiness to support underscore prefixes.  */
+#ifdef __UCLIBC_UNDERSCORES__
+       char tmp_buf[80];
+       char *name2 = tmp_buf;
+       size_t nlen = strlen (name) + 1;
+       if (nlen + 1 > sizeof (tmp_buf))
+               name2 = malloc (nlen + 1);
+       if (name2 == 0) {
+               _dl_error_number = LD_ERROR_MMAP_FAILED;
+               return 0;
+       }
+       name2[0] = '_';
+       memcpy (name2 + 1, name, nlen);
+#else
+       const char *name2 = name;
+#endif
        handle = (struct dyn_elf *) vhandle;
 
        /* First of all verify that we have a real handle
@@ -415,7 +474,8 @@ void *dlsym(void *vhandle, const char *name)
                                break;
                if (!rpnt) {
                        _dl_error_number = LD_BAD_HANDLE;
-                       return NULL;
+                       ret = NULL;
+                       goto out;
                }
        } else if (handle == RTLD_NEXT) {
                /*
@@ -438,14 +498,19 @@ void *dlsym(void *vhandle, const char *name)
        }
        tpnt = NULL;
        if (handle == _dl_symbol_tables)
-          tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */
-       ret = _dl_find_hash((char*)name, handle, tpnt, 0);
+               tpnt = handle->dyn; /* Only search RTLD_GLOBAL objs if global object */
+       ret = _dl_find_hash(name2, handle, tpnt, ELF_RTYPE_CLASS_DLSYM);
 
        /*
         * Nothing found.
         */
        if (!ret)
                _dl_error_number = LD_NO_SYMBOL;
+out:
+#ifdef __UCLIBC_UNDERSCORES__
+       if (name2 != tmp_buf)
+               free (name2);
+#endif
        return ret;
 }
 
@@ -499,12 +564,11 @@ static int do_dlclose(void *vhandle, int need_fini)
                if (--tpnt->usage_count == 0) {
                        if ((tpnt->dynamic_info[DT_FINI]
                             || tpnt->dynamic_info[DT_FINI_ARRAY])
-                           && need_fini &&
-                           !(tpnt->init_flag & FINI_FUNCS_CALLED)) {
+                        && need_fini
+                        && !(tpnt->init_flag & FINI_FUNCS_CALLED)
+                       ) {
                                tpnt->init_flag |= FINI_FUNCS_CALLED;
-#ifdef SHARED
                                _dl_run_fini_array(tpnt);
-#endif
 
                                if (tpnt->dynamic_info[DT_FINI]) {
                                        dl_elf_fini = (int (*)(void)) DL_RELOC_ADDR(tpnt->loadaddr, tpnt->dynamic_info[DT_FINI]);
@@ -523,8 +587,8 @@ static int do_dlclose(void *vhandle, int need_fini)
                                if (end < ppnt->p_vaddr + ppnt->p_memsz)
                                        end = ppnt->p_vaddr + ppnt->p_memsz;
                        }
-                       _dl_munmap((void*)tpnt->loadaddr, end);
-                       /* Free elements in RTLD_LOCAL scope list */ 
+                       DL_LIB_UNMAP (tpnt, end);
+                       /* Free elements in RTLD_LOCAL scope list */
                        for (runp = tpnt->rtld_local; runp; runp = tmp) {
                                tmp = runp->next;
                                free(runp);
@@ -535,8 +599,8 @@ static int do_dlclose(void *vhandle, int need_fini)
                                _dl_loaded_modules = tpnt->next;
                                if (_dl_loaded_modules)
                                        _dl_loaded_modules->prev = 0;
-                       } else
-                               for (run_tpnt = _dl_loaded_modules; run_tpnt; run_tpnt = run_tpnt->next)
+                       } else {
+                               for (run_tpnt = _dl_loaded_modules; run_tpnt; run_tpnt = run_tpnt->next) {
                                        if (run_tpnt->next == tpnt) {
                                                _dl_if_debug_print("removing loaded_modules: %s\n", tpnt->libname);
                                                run_tpnt->next = run_tpnt->next->next;
@@ -544,6 +608,8 @@ static int do_dlclose(void *vhandle, int need_fini)
                                                        run_tpnt->next->prev = run_tpnt;
                                                break;
                                        }
+                               }
+                       }
 
                        /* Next, remove tpnt from the global symbol table list */
                        if (_dl_symbol_tables) {
@@ -551,7 +617,7 @@ static int do_dlclose(void *vhandle, int need_fini)
                                        _dl_symbol_tables = _dl_symbol_tables->next;
                                        if (_dl_symbol_tables)
                                                _dl_symbol_tables->prev = 0;
-                               } else
+                               } else {
                                        for (rpnt1 = _dl_symbol_tables; rpnt1->next; rpnt1 = rpnt1->next) {
                                                if (rpnt1->next->dyn == tpnt) {
                                                        _dl_if_debug_print("removing symbol_tables: %s\n", tpnt->libname);
@@ -563,6 +629,7 @@ static int do_dlclose(void *vhandle, int need_fini)
                                                        break;
                                                }
                                        }
+                               }
                        }
                        free(tpnt->libname);
                        free(tpnt);
@@ -571,7 +638,6 @@ static int do_dlclose(void *vhandle, int need_fini)
        free(handle->init_fini.init_fini);
        free(handle);
 
-
        if (_dl_debug_addr) {
                dl_brk = (void (*)(void)) _dl_debug_addr->r_brk;
                if (dl_brk != NULL) {
@@ -606,7 +672,7 @@ char *dlerror(void)
  * Dump information to stderr about the current loaded modules
  */
 #ifdef __USE_GNU
-static char *type[] = { "Lib", "Exe", "Int", "Mod" };
+static const char type[][4] = { "Lib", "Exe", "Int", "Mod" };
 
 int dlinfo(void)
 {
@@ -649,6 +715,8 @@ int dladdr(const void *__address, Dl_info * __info)
 
        _dl_if_debug_print("__address: %p  __info: %p\n", __address, __info);
 
+       __address = DL_LOOKUP_ADDRESS (__address);
+
        for (rpnt = _dl_loaded_modules; rpnt; rpnt = rpnt->next) {
                struct elf_resolve *tpnt;
 
@@ -673,13 +741,40 @@ int dladdr(const void *__address, Dl_info * __info)
                char *strtab;
                ElfW(Sym) *symtab;
                unsigned int hn, si, sn, sf;
-               ElfW(Addr) sa;
+               ElfW(Addr) sa = 0;
+
+               /* Set the info for the object the address lies in */
+               __info->dli_fname = pelf->libname;
+               __info->dli_fbase = (void *)pelf->mapaddr;
 
-               sa = 0;
                symtab = (ElfW(Sym) *) (pelf->dynamic_info[DT_SYMTAB]);
                strtab = (char *) (pelf->dynamic_info[DT_STRTAB]);
 
                sf = sn = 0;
+
+#ifdef __LDSO_GNU_HASH_SUPPORT__
+               if (pelf->l_gnu_bitmask) {
+                       for (hn = 0; hn < pelf->nbucket; hn++) {
+                               si = pelf->l_gnu_buckets[hn];
+                               if (!si)
+                                       continue;
+
+                               const Elf32_Word *hasharr = &pelf->l_gnu_chain_zero[si];
+                               do {
+                                       ElfW(Addr) symbol_addr;
+
+                                       symbol_addr = (ElfW(Addr)) DL_RELOC_ADDR(pelf->loadaddr, symtab[si].st_value);
+                                       if (symbol_addr <= (ElfW(Addr))__address && (!sf || sa < symbol_addr)) {
+                                               sa = symbol_addr;
+                                               sn = si;
+                                               sf = 1;
+                                       }
+                                       _dl_if_debug_print("Symbol \"%s\" at %p\n", strtab + symtab[si].st_name, symbol_addr);
+                                       ++si;
+                               } while ((*hasharr++ & 1u) == 0);
+                       }
+               } else
+#endif
                for (hn = 0; hn < pelf->nbucket; hn++) {
                        for (si = pelf->elf_buckets[hn]; si; si = pelf->chains[si]) {
                                ElfW(Addr) symbol_addr;
@@ -697,10 +792,14 @@ int dladdr(const void *__address, Dl_info * __info)
                }
 
                if (sf) {
-                       __info->dli_fname = pelf->libname;
-                       __info->dli_fbase = (void *) DL_LOADADDR_BASE(pelf->loadaddr);
+                       /* A nearest symbol has been found; fill the entries */
                        __info->dli_sname = strtab + symtab[sn].st_name;
                        __info->dli_saddr = (void *)sa;
+               } else {
+                       /* No symbol found, fill entries with NULL value,
+                       only the containing object will be returned. */
+                       __info->dli_sname = NULL;
+                       __info->dli_saddr = NULL;
                }
                return 1;
        }