OSDN Git Service

Add RTLD_LOCAL support for dlopened libs. Reported by
[uclinux-h8/uClibc.git] / ldso / ldso / dl-hash.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) 2004 by Joakim Tjernlund <joakim.tjernlund@lumentis.se>
7  * Copyright (C) 2000-2004 by Erik Andersen <andersen@codpoet.org>
8  * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
9  *                              David Engel, Hongjiu Lu and Mitch D'Souza
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. The name of the above contributors may not be
17  *    used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33
34 /* Various symbol table handling functions, including symbol lookup */
35
36 /*
37  * This is the start of the linked list that describes all of the files present
38  * in the system with pointers to all of the symbol, string, and hash tables,
39  * as well as all of the other good stuff in the binary.
40  */
41 struct elf_resolve *_dl_loaded_modules = NULL;
42
43 /*
44  * This is the list of modules that are loaded when the image is first
45  * started.  As we add more via dlopen, they get added into other
46  * chains.
47  */
48 struct dyn_elf *_dl_symbol_tables = NULL;
49
50 /*
51  * This is the list of modules that are loaded via dlopen.  We may need
52  * to search these for RTLD_GLOBAL files.
53  */
54 struct dyn_elf *_dl_handles = NULL;
55
56
57 /* This is the hash function that is used by the ELF linker to generate the
58  * hash table that each executable and library is required to have.  We need
59  * it to decode the hash table.  */
60 unsigned long _dl_elf_hash(const unsigned char *name)
61 {
62         unsigned long hash = 0;
63         unsigned long tmp;
64
65         while (*name) {
66                 hash = (hash << 4) + *name++;
67                 if ((tmp = hash & 0xf0000000))
68                         hash ^= tmp >> 24;
69                 hash &= ~tmp;
70         }
71         return hash;
72 }
73
74 /* Check to see if a library has already been added to the hash chain.  */
75 struct elf_resolve *_dl_check_hashed_files(const char *libname)
76 {
77         struct elf_resolve *tpnt;
78         int len = _dl_strlen(libname);
79
80         for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) {
81                 if (_dl_strncmp(tpnt->libname, libname, len) == 0 &&
82                                 (tpnt->libname[len] == '\0' || tpnt->libname[len] == '.'))
83                         return tpnt;
84         }
85
86         return NULL;
87 }
88
89 /*
90  * We call this function when we have just read an ELF library or executable.
91  * We add the relevant info to the symbol chain, so that we can resolve all
92  * externals properly.
93  */
94 struct elf_resolve *_dl_add_elf_hash_table(const char *libname,
95         char *loadaddr, unsigned long *dynamic_info, unsigned long dynamic_addr,
96         unsigned long dynamic_size)
97 {
98         unsigned long *hash_addr;
99         struct elf_resolve *tpnt;
100         int i;
101
102         if (!_dl_loaded_modules) {
103                 tpnt = _dl_loaded_modules = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
104                 _dl_memset(tpnt, 0, sizeof(struct elf_resolve));
105         } else {
106                 tpnt = _dl_loaded_modules;
107                 while (tpnt->next)
108                         tpnt = tpnt->next;
109                 tpnt->next = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
110                 _dl_memset(tpnt->next, 0, sizeof(struct elf_resolve));
111                 tpnt->next->prev = tpnt;
112                 tpnt = tpnt->next;
113         };
114
115         tpnt->next = NULL;
116         tpnt->init_flag = 0;
117         tpnt->libname = _dl_strdup(libname);
118         tpnt->dynamic_addr = (ElfW(Dyn) *)dynamic_addr;
119         tpnt->libtype = loaded_file;
120
121         if (dynamic_info[DT_HASH] != 0) {
122                 hash_addr = (unsigned long *) (intptr_t)(dynamic_info[DT_HASH] + loadaddr);
123                 tpnt->nbucket = *hash_addr++;
124                 tpnt->nchain = *hash_addr++;
125                 tpnt->elf_buckets = hash_addr;
126                 hash_addr += tpnt->nbucket;
127                 tpnt->chains = hash_addr;
128         }
129         tpnt->loadaddr = (ElfW(Addr))loadaddr;
130         for (i = 0; i < DYNAMIC_SIZE; i++)
131                 tpnt->dynamic_info[i] = dynamic_info[i];
132         return tpnt;
133 }
134
135
136 /*
137  * This function resolves externals, and this is either called when we process
138  * relocations or when we call an entry in the PLT table for the first time.
139  */
140 char *_dl_find_hash(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, int type_class)
141 {
142         struct elf_resolve *tpnt;
143         int si;
144         char *strtab;
145         Elf32_Sym *symtab;
146         unsigned long elf_hash_number, hn;
147         const ElfW(Sym) *sym;
148         char *weak_result = NULL;
149
150         elf_hash_number = _dl_elf_hash(name);
151            
152         for (; rpnt; rpnt = rpnt->next) {
153                 tpnt = rpnt->dyn;
154
155                 if (!(tpnt->rtld_flags & RTLD_GLOBAL) && mytpnt) {
156                         if (mytpnt == tpnt)
157                                 ;
158                         else {
159                                 struct init_fini_list *tmp;
160
161                                 for (tmp = mytpnt->rtld_local; tmp; tmp = tmp->next) {
162                                         if (tmp->tpnt == tpnt)
163                                                 break;
164                                 }
165                                 if (!tmp)
166                                         continue;
167                         }
168                 }
169                 /* Don't search the executable when resolving a copy reloc. */
170                 if ((type_class &  ELF_RTYPE_CLASS_COPY) && tpnt->libtype == elf_executable)
171                         continue;
172
173                 /* Avoid calling .urem here. */
174                 do_rem(hn, elf_hash_number, tpnt->nbucket);
175                 symtab = (Elf32_Sym *) (intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
176                 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
177
178                 for (si = tpnt->elf_buckets[hn]; si != STN_UNDEF; si = tpnt->chains[si]) {
179                         sym = &symtab[si];
180
181                         if (type_class & (sym->st_shndx == SHN_UNDEF))
182                                 continue;
183                         if (_dl_strcmp(strtab + sym->st_name, name) != 0)
184                                 continue;
185                         if (sym->st_value == 0)
186                                 continue;
187                         if (ELF32_ST_TYPE(sym->st_info) > STT_FUNC)
188                                 continue;
189
190                         switch (ELF32_ST_BIND(sym->st_info)) {
191                         case STB_WEAK:
192 #if 0
193 /* Perhaps we should support old style weak symbol handling
194  * per what glibc does when you export LD_DYNAMIC_WEAK */
195                                 if (!weak_result)
196                                         weak_result = (char *)tpnt->loadaddr + sym->st_value;
197                                 break;
198 #endif
199                         case STB_GLOBAL:
200                                 return (char*)tpnt->loadaddr + sym->st_value;
201                         default:        /* Local symbols not handled here */
202                                 break;
203                         }
204                 }
205         }
206         return weak_result;
207 }