OSDN Git Service

patch from Bernd Schmidt to abstract away initializing of relocation addresses
[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-2006 by Erik Andersen <andersen@codepoet.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 static inline Elf_Symndx _dl_elf_hash(const char *name)
61 {
62         unsigned long hash=0;
63         unsigned long tmp;
64
65         while (*name) {
66                 hash = (hash << 4) + *name++;
67                 tmp = hash & 0xf0000000;
68                 /* The algorithm specified in the ELF ABI is as follows:
69                    if (tmp != 0)
70                        hash ^= tmp >> 24;
71                    hash &= ~tmp;
72                    But the following is equivalent and a lot
73                    faster, especially on modern processors. */
74                 hash ^= tmp;
75                 hash ^= tmp >> 24;
76         }
77         return hash;
78 }
79
80 /*
81  * We call this function when we have just read an ELF library or executable.
82  * We add the relevant info to the symbol chain, so that we can resolve all
83  * externals properly.
84  */
85 struct elf_resolve *_dl_add_elf_hash_table(const char *libname,
86         DL_LOADADDR_TYPE loadaddr, unsigned long *dynamic_info, unsigned long dynamic_addr,
87         attribute_unused unsigned long dynamic_size)
88 {
89         Elf_Symndx *hash_addr;
90         struct elf_resolve *tpnt;
91         int i;
92
93         if (!_dl_loaded_modules) {
94                 tpnt = _dl_loaded_modules = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
95                 _dl_memset(tpnt, 0, sizeof(struct elf_resolve));
96         } else {
97                 tpnt = _dl_loaded_modules;
98                 while (tpnt->next)
99                         tpnt = tpnt->next;
100                 tpnt->next = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
101                 _dl_memset(tpnt->next, 0, sizeof(struct elf_resolve));
102                 tpnt->next->prev = tpnt;
103                 tpnt = tpnt->next;
104         };
105
106         tpnt->next = NULL;
107         tpnt->init_flag = 0;
108         tpnt->libname = _dl_strdup(libname);
109         tpnt->dynamic_addr = (ElfW(Dyn) *)dynamic_addr;
110         tpnt->libtype = loaded_file;
111
112         if (dynamic_info[DT_HASH] != 0) {
113                 hash_addr = (Elf_Symndx*)dynamic_info[DT_HASH];
114                 tpnt->nbucket = *hash_addr++;
115                 tpnt->nchain = *hash_addr++;
116                 tpnt->elf_buckets = hash_addr;
117                 hash_addr += tpnt->nbucket;
118                 tpnt->chains = hash_addr;
119         }
120         tpnt->loadaddr = loadaddr;
121         for (i = 0; i < DYNAMIC_SIZE; i++)
122                 tpnt->dynamic_info[i] = dynamic_info[i];
123         return tpnt;
124 }
125
126
127 /*
128  * This function resolves externals, and this is either called when we process
129  * relocations or when we call an entry in the PLT table for the first time.
130  */
131 char *_dl_find_hash(const char *name, struct dyn_elf *rpnt, struct elf_resolve *mytpnt, int type_class)
132 {
133         struct elf_resolve *tpnt;
134         int si;
135         char *strtab;
136         ElfW(Sym) *symtab;
137         unsigned long elf_hash_number, hn;
138         const ElfW(Sym) *sym;
139         char *weak_result = NULL;
140
141         elf_hash_number = _dl_elf_hash(name);
142
143         for (; rpnt; rpnt = rpnt->next) {
144                 tpnt = rpnt->dyn;
145
146                 if (!(tpnt->rtld_flags & RTLD_GLOBAL) && mytpnt) {
147                         if (mytpnt == tpnt)
148                                 ;
149                         else {
150                                 struct init_fini_list *tmp;
151
152                                 for (tmp = mytpnt->rtld_local; tmp; tmp = tmp->next) {
153                                         if (tmp->tpnt == tpnt)
154                                                 break;
155                                 }
156                                 if (!tmp)
157                                         continue;
158                         }
159                 }
160                 /* Don't search the executable when resolving a copy reloc. */
161                 if ((type_class &  ELF_RTYPE_CLASS_COPY) && tpnt->libtype == elf_executable)
162                         continue;
163
164                 /* Avoid calling .urem here. */
165                 do_rem(hn, elf_hash_number, tpnt->nbucket);
166                 symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
167                 strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]);
168
169                 for (si = tpnt->elf_buckets[hn]; si != STN_UNDEF; si = tpnt->chains[si]) {
170                         sym = &symtab[si];
171
172                         if (type_class & (sym->st_shndx == SHN_UNDEF))
173                                 continue;
174                         if (_dl_strcmp(strtab + sym->st_name, name) != 0)
175                                 continue;
176                         if (sym->st_value == 0)
177                                 continue;
178                         if (ELF_ST_TYPE(sym->st_info) > STT_FUNC)
179                                 continue;
180
181                         switch (ELF_ST_BIND(sym->st_info)) {
182                         case STB_WEAK:
183 #if 0
184 /* Perhaps we should support old style weak symbol handling
185  * per what glibc does when you export LD_DYNAMIC_WEAK */
186                                 if (!weak_result)
187                                         weak_result = (char *) DL_RELOC_ADDR(tpnt->loadaddr, sym->st_value);
188                                 break;
189 #endif
190                         case STB_GLOBAL:
191                                 return (char*) DL_RELOC_ADDR(tpnt->loadaddr, sym->st_value);
192                         default:        /* Local symbols not handled here */
193                                 break;
194                         }
195                 }
196         }
197         return weak_result;
198 }