]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/l4re-core/uclibc/lib/contrib/uclibc/ldso/ldso/i386/elfinterp.c
Update
[l4.git] / l4 / pkg / l4re-core / uclibc / lib / contrib / uclibc / ldso / ldso / i386 / elfinterp.c
1 /* vi: set sw=4 ts=4: */
2 /* i386 ELF shared library loader suppport
3  *
4  * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
5  *                              David Engel, Hongjiu Lu and Mitch D'Souza
6  * Copyright (C) 2001-2004 Erik Andersen
7  *
8  * All rights reserved.
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 #include "ldso.h"
33
34 /* Program to load an ELF binary on a linux system, and run it.
35    References to symbols in sharable libraries can be resolved by either
36    an ELF sharable library or a linux style of shared library. */
37
38 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
39    I ever taken any courses on internals.  This program was developed using
40    information available through the book "UNIX SYSTEM V RELEASE 4,
41    Programmers guide: Ansi C and Programming Support Tools", which did
42    a more than adequate job of explaining everything required to get this
43    working. */
44
45 extern int _dl_linux_resolve(void);
46
47 unsigned long
48 _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
49 {
50         ELF_RELOC *this_reloc;
51         char *strtab;
52         ElfW(Sym) *symtab;
53         int symtab_index;
54         char *rel_addr;
55         char *new_addr;
56         char **got_addr;
57         unsigned long instr_addr;
58         char *symname;
59
60         rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
61         this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
62         symtab_index = ELF_R_SYM(this_reloc->r_info);
63
64         symtab = (ElfW(Sym) *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
65         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
66         symname = strtab + symtab[symtab_index].st_name;
67
68         /* Address of the jump instruction to fix up. */
69         instr_addr = ((unsigned long)this_reloc->r_offset +
70                       (unsigned long)tpnt->loadaddr);
71         got_addr = (char **)instr_addr;
72
73         /* Get the address of the GOT entry. */
74         new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
75         if (unlikely(!new_addr)) {
76                 _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname);
77                 _dl_exit(1);
78         }
79
80 #if defined (__SUPPORT_LD_DEBUG__)
81         if ((unsigned long)got_addr < 0x40000000) {
82                 if (_dl_debug_bindings) {
83                         _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
84                         if (_dl_debug_detail)
85                                 _dl_dprintf(_dl_debug_file,
86                                             "\n\tpatched: %x ==> %x @ %x",
87                                             *got_addr, new_addr, got_addr);
88                 }
89         }
90         if (!_dl_debug_nofixups) {
91                 *got_addr = new_addr;
92         }
93 #else
94         *got_addr = new_addr;
95 #endif
96
97         return (unsigned long)new_addr;
98 }
99
100 static int
101 _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
102           unsigned long rel_addr, unsigned long rel_size,
103           int (*reloc_fnc)(struct elf_resolve *tpnt, struct r_scope_elem *scope,
104                            ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab))
105 {
106         unsigned int i;
107         char *strtab;
108         ElfW(Sym) *symtab;
109         ELF_RELOC *rpnt;
110         int symtab_index;
111
112         /* Parse the relocation information. */
113         rpnt = (ELF_RELOC *)(intptr_t)rel_addr;
114         rel_size /= sizeof(ELF_RELOC);
115
116         symtab = (ElfW(Sym) *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
117         strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
118
119         for (i = 0; i < rel_size; i++, rpnt++) {
120                 int res;
121
122                 symtab_index = ELF_R_SYM(rpnt->r_info);
123
124                 debug_sym(symtab, strtab, symtab_index);
125                 debug_reloc(symtab, strtab, rpnt);
126
127                 res = reloc_fnc(tpnt, scope, rpnt, symtab, strtab);
128
129                 if (res == 0)
130                         continue;
131
132                 _dl_dprintf(2, "\n%s: ", _dl_progname);
133
134                 if (symtab_index)
135                         _dl_dprintf(2, "symbol '%s': ",
136                                     strtab + symtab[symtab_index].st_name);
137
138                 if (unlikely(res < 0)) {
139                         int reloc_type = ELF_R_TYPE(rpnt->r_info);
140
141 #if defined (__SUPPORT_LD_DEBUG__)
142                         _dl_dprintf(2, "can't handle reloc type '%s' in lib '%s'\n",
143                                     _dl_reltypes(reloc_type), tpnt->libname);
144 #else
145                         _dl_dprintf(2, "can't handle reloc type %x in lib '%s'\n",
146                                     reloc_type, tpnt->libname);
147 #endif
148                         return res;
149                 } else if (unlikely(res > 0)) {
150                         _dl_dprintf(2, "can't resolve symbol in lib '%s'.\n", tpnt->libname);
151                         return res;
152                 }
153         }
154
155         return 0;
156 }
157
158 static int
159 _dl_do_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope,
160              ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
161 {
162         int reloc_type;
163         int symtab_index;
164         char *symname;
165         struct elf_resolve *tls_tpnt = NULL;
166         unsigned long *reloc_addr;
167         unsigned long symbol_addr;
168 #if defined (__SUPPORT_LD_DEBUG__)
169         unsigned long old_val;
170 #endif
171         struct symbol_ref sym_ref;
172
173         reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
174         reloc_type = ELF_R_TYPE(rpnt->r_info);
175         symtab_index = ELF_R_SYM(rpnt->r_info);
176         symbol_addr = 0;
177         sym_ref.sym = &symtab[symtab_index];
178         sym_ref.tpnt = NULL;
179         symname = strtab + symtab[symtab_index].st_name;
180
181         if (symtab_index) {
182                 symbol_addr = (unsigned long)_dl_find_hash(symname, scope, tpnt,
183                                                            elf_machine_type_class(reloc_type), &sym_ref);
184
185                 /*
186                  * We want to allow undefined references to weak symbols - this
187                  * might have been intentional.  We should not be linking local
188                  * symbols here, so all bases should be covered.
189                  */
190                 if (unlikely(!symbol_addr && (ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS)
191                                         && ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK))
192                         return 1;
193                 if (_dl_trace_prelink) {
194                         _dl_debug_lookup (symname, tpnt, &symtab[symtab_index],
195                                         &sym_ref, elf_machine_type_class(reloc_type));
196                 }
197                 tls_tpnt = sym_ref.tpnt;
198         } else {
199                 symbol_addr = symtab[symtab_index].st_value;
200                 tls_tpnt = tpnt;
201         }
202         
203 #if defined (__SUPPORT_LD_DEBUG__)
204         /* Start: aw11: fixed pagefault with empty relocations */
205         if (reloc_type != R_386_NONE) old_val = *reloc_addr; else old_val = 0;
206         /* End: aw11 */
207 #endif
208
209         switch (reloc_type) {
210                 case R_386_NONE:
211                         break;
212                 case R_386_32:
213                         *reloc_addr += symbol_addr;
214                         break;
215                 case R_386_PC32:
216                         *reloc_addr += symbol_addr - (unsigned long)reloc_addr;
217                         break;
218                 case R_386_GLOB_DAT:
219                 case R_386_JMP_SLOT:
220                         *reloc_addr = symbol_addr;
221                         break;
222                 case R_386_RELATIVE:
223                         *reloc_addr += (unsigned long)tpnt->loadaddr;
224                         break;
225                 case R_386_COPY:
226                         if (symbol_addr) {
227 #if defined (__SUPPORT_LD_DEBUG__)
228                                 if (_dl_debug_move)
229                                         _dl_dprintf(_dl_debug_file,
230                                                     "\n%s move %d bytes from %x to %x",
231                                                     symname, symtab[symtab_index].st_size,
232                                                     symbol_addr, reloc_addr);
233 #endif
234
235                                 _dl_memcpy((char *)reloc_addr,
236                                            (char *)symbol_addr,
237                                            symtab[symtab_index].st_size);
238                         }
239                         break;
240 #if defined USE_TLS && USE_TLS
241                 case R_386_TLS_DTPMOD32:
242                         *reloc_addr = tls_tpnt->l_tls_modid;
243                         break;
244                 case R_386_TLS_DTPOFF32:
245                         /* During relocation all TLS symbols are defined and used.
246                          * Therefore the offset is already correct. */
247                         *reloc_addr = symbol_addr;
248                         break;
249                 case R_386_TLS_TPOFF32:
250                         /* The offset is positive, backward from the thread pointer. */
251                         CHECK_STATIC_TLS((struct link_map*) tls_tpnt);
252                         *reloc_addr += tls_tpnt->l_tls_offset - symbol_addr;
253                         break;
254                 case R_386_TLS_TPOFF:
255                         /* The offset is negative, forward from the thread pointer. */
256                         CHECK_STATIC_TLS((struct link_map*) tls_tpnt);
257                         *reloc_addr += symbol_addr - tls_tpnt->l_tls_offset;
258                         break;
259 #endif
260                 default:
261                         return -1;
262         }
263
264 #if defined (__SUPPORT_LD_DEBUG__)
265         if (_dl_debug_reloc && _dl_debug_detail)
266                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n",
267                             old_val, *reloc_addr, reloc_addr);
268 #endif
269
270         return 0;
271 }
272
273 static int
274 _dl_do_lazy_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope,
275                   ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
276 {
277         int reloc_type;
278         unsigned long *reloc_addr;
279 #if defined (__SUPPORT_LD_DEBUG__)
280         unsigned long old_val;
281 #endif
282
283         (void)scope;
284         (void)symtab;
285         (void)strtab;
286
287         reloc_addr = (unsigned long *)(intptr_t)(tpnt->loadaddr + (unsigned long)rpnt->r_offset);
288         reloc_type = ELF_R_TYPE(rpnt->r_info);
289
290 #if defined (__SUPPORT_LD_DEBUG__)
291         old_val = *reloc_addr;
292 #endif
293
294         switch (reloc_type) {
295                 case R_386_NONE:
296                         break;
297                 case R_386_JMP_SLOT:
298                         *reloc_addr += (unsigned long)tpnt->loadaddr;
299                         break;
300                 default:
301                         return -1;
302         }
303
304 #if defined (__SUPPORT_LD_DEBUG__)
305         if (_dl_debug_reloc && _dl_debug_detail)
306                 _dl_dprintf(_dl_debug_file, "\n\tpatched: %x ==> %x @ %x\n",
307                             old_val, *reloc_addr, reloc_addr);
308 #endif
309
310         return 0;
311 }
312
313 void
314 _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
315                                       unsigned long rel_addr,
316                                       unsigned long rel_size)
317 {
318         (void)_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
319 }
320
321 int
322 _dl_parse_relocation_information(struct dyn_elf *rpnt,
323                                  struct r_scope_elem *scope,
324                                  unsigned long rel_addr,
325                                  unsigned long rel_size)
326 {
327         return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, _dl_do_reloc);
328 }